Luis Angel Cofiño

Trucos Linux

Copias de seguridad desatendidas





Secciones

Índices

Una de las principales preocupaciones de la gente es cómo hacer copias de seguridad de una forma sencilla y sin errores.

:-)

Vale, no conozco a NADIE que le importen un pimiento las copias de seguridad. Pero yo soy así de rarito, y desde siempre he tenido una especial preocupación por encontrar una forma de que el ordenador haga él solito todo lo necesario para mantener a salvo la información. Al fin y al cabo, para eso es un ordenador,¿no?: para manipular y conservar la información al mismo tiempo que para automatizar las tareas rutinarias.

Con Windows no pude hacerlo. Con OS/2 hice algún intento (Dios,que bueno era el OS/2, Santa IBM lo tenga en su seno). Pero fue con Linux con lo que la cosa cuajó de verdad y a mi gusto.

  1. Copias de seguridad personalizadas en CD/DVD
  2. Mi antiguo script
  3. El sistema definitivo (Disco duro externo)
  4. El sistema más definitivo (script perl y discos duros externos)

  *

Copias de seguridad personalizadas

Tras una limpieza del disco duro, me he encontrado con un montón de megas libres que pretendo organizar para dejar una partición libre, especialmente volcando a CDs todo lo que no uso nunca (sobre todo kernels antiguos, documentos sobre cosas raras bajados de Internet, etc)

El caso es que voy a crear un nuevo script (en realidad, varios scripts que trabajen juntos) para hacer copias de seguridad automatizadas. Será un sistema de backup personalizado diseñado para que funcione a mi gusto, y lo voy a hacer como suelo hacer las cosas: progresivamente, empezando por lo sencillo y complicandolo cada vez más. Y qué menos que contaroslo a vosotros en directo, ¿no?. ;-)

11/09/2004

Ahora tengo una especie de partición "home" en la que se encuentran casi todos los datos a salvar. Eso incluye los discos virtuales de dosemu (donde trabajo a veces) y algunas cosas más como música mp3 etc. Obviamente, no todo se va a salvar a la copia de seguridad, puesto que por ejemplo la música mp3 se puede volver a ripear desde el CD correspondiente. Por otro lado, hay directorios que sí deben protegerse pero no están en esa partición, como por ejemplo /etc o /root. Bien, pues vamos a hacer una cosa sencillita, de andar por casa. Para ello creamos un script llamado "seguridad" y que sea tal que así:

----------------inicio script seguridad-----------------
#!/bin/bash
# Este script sirve para hacer copias de seguridad
# usando grupos de CD-RW rotatorios
# ---------------------------------
	
DIRS1="/datos/textos \
       /datos/personal \
       /datos/profesional"
DIRS2="/datos/compartidos \
       /datos/evolution_lacofi \
       /datos/evolution_maria \
       /datos/externos"
DIRS3="/home \
       /etc \
       /root \
       /datos/dosemu \
       /datos/imagen"
TEMP="/datos/seguridad/zap"
FECHA=`date +%d%b%Y`
IMAGEN="/datos/seguridad/cd"

# Poner todo a cero
rm -Rf /datos/seguridad/zap/*.tar.bz2
rm -Rf /datos/seguridad/cd/*.iso

# Ahora creamos ficheros tar con la copia de seguridad
# y luego los pasamos a imágenes ISO, listas para grabar
for NUM in 1 2 3
do
	tar cjf $TEMP/serie$NUM-$FECHA.tar.bz2 `eval echo '$DIRS'$NUM` &> /dev/null
	mkisofs -r -o $IMAGEN/serie$NUM-$FECHA.iso \
		$TEMP &> /dev/null
	rm $TEMP/*.tar.bz2
done
---------------final script seguridad-----------------

Este script lo coloco en /datos/programas-usuario y le hago un link a un directorio en el path, concretamente /usr/local/bin, pero en realidad funcionaría igual esté donde esté.

Tan solo queda crear los directorios donde iran las copias de seguridad en la forma "tar" y en la forma "iso":

	[19:16:32/0][lacofi@claudia:~]$ su
	password:
	[19:17:02/0][root@claudia:/home/lacofi]# cd /datos
	[19:17:11/0][root@claudia:/datos]# mkdir seguridad
	[19:17:14/0][root@claudia:/datos]# cd seguridad
	[19:17:19/0][root@claudia:/datos/seguridad]# mkdir cd zap
	[19:17:23/0][root@claudia:/datos/seguridad]# exit

Y eso es todo. Con esto ya funciona minimamente. Por supuesto, primero me he asegurado de que esos directorios sin comprimir ocupan menos de 700 Mb en cada serie (DIRS1, DIRS2, DIRS3), con lo que garantizo que, comprimidos, cabrán en un CD.

Si ejecutamos este script, se harán tres imagenes ISO que luego, cuando queramos, podemos grabar en CD. No es desatendido, todavía, aunque bastaría con añadir una entrada en el cron. Tampoco hace copias incrementales, y no toma ninguna decisión. Es poco sofisticado, en resumen.

Pero, ¡eh!, dije que lo haríamos pasito a paso, ¿no?. ;-) Pues hasta otro día.

12/09/2004

Hemos creado un script muy elemental para construir imágenes ISO con nuestras copias de seguridad. Se podría meter en el cron, para que se ejecute automáticamente, pongamos, una vez cada quince días. Pero nos falta algo muy importante, claro: hay que sacar esas imágenes ISO a CD-RW.

Habitualmente, yo hago mis copias de seguridad de forma rotatoria, en dos grupos (par e impar) que se van alternando, por lo que necesitaré seis CD-RW. Tres para el grupo par, y otros tres para la semana siguiente, el grupo impar, y así sucesivamente, según este esquema:

  • Grupo 1 (serie par):
    1. CDRW 1/1
    2. CDRW 1/2
    3. CDRW 1/3
  • Grupo 2 (serie impar):
    1. CDRW 2/1
    2. CDRW 2/2
    3. CDRW 2/3

Si alguien se pregunta por qué carajos los llamo serie "par" y serie "impar", pues el nombre le viene de antiguo, cuando hacía copias de seguridad completas mensuales (e incrementales diarias). Tenía dos juegos de CD, uno para los meses pares y otro para los meses impares. ¿entendido?.

Y lógicamente, hay que automatizar el grabado. Para ello, hacemos un script que nos vaya pidiendo los CDs:

-------------- Comienza script cdsalva ---------------
#!/bin/sh

numero=1
grupo=`cat /var/log/volcado_duro.dir`
equipo="dev=1,0,0"
opciones="speed=16 driveropts=burnproof"
if [ "$grupo" = "impar" ] ; then
	grunum="2"
	echo "par" > /var/log/volcado_duro.dir
else
	grunum="1"
	echo "impar" > /var/log/volcado_duro.dir
fi
for archivo in `find /datos/seguridad/cd/* -print`
do
	echo "Grabando" > /var/log/volcado_cd.dir
	echo -e " "
	echo -e "-----------------------------"
	echo -e "Introduce el CD-RW "$numero" del grupo "$grupo "("$grunum"/"$numero")"
	echo -e "y pulsa la tecla Intro"
	read seleccion
	echo -e "Borrando el CD-RW"
	nice -20 cdrecord $equipo blank=fast > /dev/null 2>&1
	echo -e "Grabando la imagen "$archivo
	nice -20 cdrecord $opciones $carga $equipo $archivo > /dev/null 2>&1
	numero=`expr $numero + 1`
done
echo "Hecho" >> $estado
-------------- Termina script cdsalva ----------------	

Podemos meterlo en nuestro path (por ejemplo, /datos/programas-usuario y hacer un link a /usr/local/bin), o bien convertirlo en una función de bash. En cualquier caso, una vez ejecutado debería ir pidiendo los CD adecuados.

Bien, ahora observad que este script hace un pequeño registro de lo que está haciendo. Cuando se prepara para iniciar la copia, graba un fichero de log con la palabra "Grabando". Cuando la copia terminó con éxito, lo sobreescribe con la palabra "Salvado". De esta forma nos informa a nosotros y a otros scripts que hagamos en el futuro, de si la copia se ha interrumpido por algún error, o si está ya en CD, con lo que tenemos los datos verdaderamente a salvo.

Observad también que el script revisa un segundo fichero de log para comprobar si la última copia de seguridad fue de la serie par o impar, hace lo contrario, y luego cambia el fichero de log. De esta forma, el script pedirá la serie par, pero la próxima vez que se ejecute, pedirá la impar, y así sucesivamente, alternando los backups.

Pero ahora, vamos a retocar un poco el script que hicimos ayer, al que llamamos "seguridad": pondremos una cláusula para que si lo ejecutamos con el parámetro "reset" nos cree los directorios adecuados para el funcionamiento del script; también añadiremos una opción que hará que los CD sean autoarrancables; y también usaremos el fichero de log del script anterior para que lo sobreescriba con la palabra "Pendiente" cuando haya creado las imagenes ISO, pero aún se ha ejecutado el script "cdsalva". Pongo los cambios en color cian y negrita para resaltarlos de lo hecho ayer.

----------------inicio script seguridad-----------------
#!/bin/bash
# Este script sirve para hacer copias de seguridad
# usando grupos de CD-RW rotatorios
# ---------------------------------

DIRS1="/datos/textos \
       /datos/personal \
       /datos/profesional"
DIRS2="/datos/compartidos \
       /datos/evolution_lacofi \
       /datos/evolution_maria \
       /datos/externos"
DIRS3="/home \
       /etc \
       /root \
       /datos/dosemu \
       /datos/imagen"
TEMP="/datos/seguridad/zap"
FECHA=`date +%d%b%Y`
IMAGEN="/datos/seguridad/cd"
RIP="arranque/rip.img"
CAT="arranque/catalogo.cat"

if [ "$1" = "reset" ]; then
	if [ -f /var/log/volcado_duro.dir ]; then
		echo "El status es: "`cat /var/log/volcado_duro.dir`
	else
		echo "impar" > /var/log/volcado_duro.dir
	fi
	if [ -d "/datos/seguridad" ]; then
		echo "/datos/seguridad existe"
	else
		echo "Creando /datos/seguridad"
		mkdir /datos/seguridad
	fi
	if [ -d "/datos/seguridad/cd" ]; then
		echo "/datos/seguridad/cd existe"
	else
		echo "Creando /datos/seguridad/cd"
		mkdir /datos/seguridad/cd
	fi
	if [ -d "/datos/seguridad/zap" ]; then
		echo "/datos/seguridad/zap existe"
	else
		echo "Creando /datos/seguridad/zap"
		mkdir /datos/seguridad/zap
	fi
	if [ -d "/datos/seguridad/zap/arranque" ]; then
		echo "/datos/seguridad/zap/arranque existe"
	else
		echo "Creando /datos/seguridad/zap/arranque"
		mkdir /datos/seguridad/zap/arranque
		echo "Debes colocar en /datos/seguridad/zap/arranque"
		echo "una imagen de rescate, preferiblemente RIP"
	fi
	exit
fi

# Poner todo a cero
rm -Rf /datos/seguridad/zap/*.tar.bz2
rm -Rf /datos/seguridad/cd/*.iso

# Ahora creamos ficheros tar con la copia de seguridad
# y luego los pasamos a imágenes ISO, listas para grabar
for NUM in 1 2 3
do
	tar cjf $TEMP/serie$NUM-$FECHA.tar.bz2 `eval echo '$DIRS'$NUM` &> /dev/null
	mkisofs -r -o $IMAGEN/serie$NUM-$FECHA.iso \
		-b $RIP -c $CAT $TEMP &> /dev/null
	rm $TEMP/*.tar.bz2
done

if [ -f "$IMAGEN/serie1-$FECHA.iso" ] ||  
  	[ -f "$IMAGEN/serie2-$FECHA.iso" ] || 
  	[ -f "$IMAGEN/serie3-$FECHA.iso" ]; then
	echo "Pendiente" > /var/log/volcado_cd.dir
fi
----------------termina script seguridad----------------

Lo que queda por hacer es colocar la imagen de alguna minidistribución linux (ya sabes, esas que caben en un solo disquette) y meterla en el directorio /datos/seguridad/zap/arranque. Yo te recomiendo RIP. Es muy completa y tiene varias versiones (en CD, en floppy...). Naturalmente, la que nos interesa aquí es la versión floppy. Podeis bajaros el binario (por ejemplo el fichero "RIP-3.1-1680.bin") y copiarlo a /datos/seguridad/zap/arranque/rip.img

Eso es todo lo necesario para que el CD sea autoarrancable. El programa mkisofs buscará en ese subdirectorio la imagen de RIP y la usará para crear un autoarranque según el protocolo "El Torito". Chulo, ¿no?. Ahora puedes acceder a tus copias de seguridad desde cualquier máquina, tenga o no tenga linux instalado.

Esta idea, así como otras que usaré después, está sacada del artículo de Alan Keates Easy Backup and Restore - Linux. Vete echándole un vistazo, que merece la pena.

Basta por hoy.

14/09/2004

Hoy vamos a retocar los dos scripts anteriores, aunque en realidad el funcionamiento será el mismo. Los cambios están dirigidos a que el sistema pueda recoger información sobre cómo ha ido la copia de seguridad. Más adelante, otros scripts podrían usar esa información para saber qué es lo que tienen que hacer a continuación. Y también nos servirá a nosotros para saber cómo va la cosa, claro. En segundo lugar, haremos que nuestros scripts comprueban las sumas MD5 tanto del fichero ISO como de la copia en CD, después de grabarla. Si son idénticas, esto garantiza que la copia sea exacta. Es muy importante, porque la grabación del CD es un procedimiento crítico y que puede fallar: tened en cuenta que un error en un solo byte puede mandar al carajo vuestro fichero tar con todos vuestros datos dentro pero inaccesibles. Con la suma md5 nos daremos cuenta de ello inmediatamente, pues la suma se altera con un único byte equivocado. Del mismo modo, también haremos que nuestros logs recojan el código de error (o códigos de salida) de los programas tar y mkisofs (sabiendo que código cero significa: no hubo errores en la ejecución). :-)

En primer lugar, modificaremos el script "seguridad". Los cambios, en cian y negrita.

--------------Comienza script "seguridad" -------------
#!/bin/bash
# Este script sirve para hacer copias de seguridad
# usando grupos de CD-RW rotatorios
# ---------------------------------

DIRS1="/datos/textos \
       /datos/personal \
       /datos/profesional"
DIRS2="/datos/compartidos \
       /datos/evolution_lacofi \
       /datos/evolution_maria \
       /datos/externos"
DIRS3="/home \
       /etc \
       /root \
       /datos/dosemu \
       /datos/imagen"
TEMP="/datos/seguridad/zap"
FECHA=`date +%d%b%Y`
IMAGEN="/datos/seguridad/cd"
RIP="arranque/rip.img"
CAT="arranque/catalogo.cat"
ESTADO="/var/log/estado.log"

if [ "$1" = "reset" ]; then
	if [ -f /var/log/volcado_duro.dir ]; then
		echo "El status es: "`cat /var/log/volcado_duro.dir`
	else
		echo "impar" > /var/log/volcado_duro.dir
	fi
	if [ -d "/datos/seguridad" ]; then
		echo "/datos/seguridad existe"
	else
		echo "Creando /datos/seguridad"
		mkdir /datos/seguridad
	fi
	if [ -d "/datos/seguridad/cd" ]; then
		echo "/datos/seguridad/cd existe"
	else
		echo "Creando /datos/seguridad/cd"
		mkdir /datos/seguridad/cd
	fi
	if [ -d "/datos/seguridad/zap" ]; then
		echo "/datos/seguridad/zap existe"
	else
		echo "Creando /datos/seguridad/zap"
		mkdir /datos/seguridad/zap
	fi
	if [ -d "/datos/seguridad/zap/arranque" ]; then
		echo "/datos/seguridad/zap/arranque existe"
	else
		echo "Creando /datos/seguridad/zap/arranque"
		mkdir /datos/seguridad/zap/arranque
		echo "Debes colocar en /datos/seguridad/zap/arranque"
		echo "una imagen de rescate, preferiblemente RIP"
	fi
	echo "El registro ha sido reseteado" > $ESTADO
	echo "-----------------------------" >> $ESTADO
	exit
fi

# Poner todo a cero
rm -Rf /datos/seguridad/zap/*.tar.bz2
rm -Rf /datos/seguridad/cd/*.iso
rm -Rf /var/log/md5_*.log

# Ahora creamos ficheros tar con la copia de seguridad
# y luego los pasamos a imágenes ISO, listas para grabar
for NUM in 1 2 3
do
	echo "Creando serie$NUM-$FECHA.tar.bz2" >> $ESTADO
	tar cjf $TEMP/serie$NUM-$FECHA.tar.bz2 `eval echo '$DIRS'$NUM` &> /dev/null
	status=$?
	echo "La compresión se ejecutó con código $status" >> $ESTADO
	mkisofs -r -o $IMAGEN/serie$NUM-$FECHA.iso \
		-b $RIP -c $CAT $TEMP &> /dev/null
	status=$?
	echo "La imagen ISO se creó con código $status" >> $ESTADO
	sumamd5=`md5sum $IMAGEN/serie$NUM-$FECHA.iso | awk '{print $1}'`
	echo $sumamd5 >> /var/log/md5_$NUM.log
	rm $TEMP/*.tar.bz2
done

if [ -f "$IMAGEN/serie1-$FECHA.iso" ] || 
  	[ -f "$IMAGEN/serie2-$FECHA.iso" ] || 
  	[ -f "$IMAGEN/serie3-$FECHA.iso" ]; then
	echo "Pendiente" > /var/log/volcado_cd.dir
	echo "Copia de seguridad almacenada en el dico duro" >> $ESTADO
	echo "La copia de seguridad no está a salvo" >> $ESTADO
fi
--------------Final de script "seguridad" -------------

Ahora modificamos también el script "cdsalva". Atentos a los comentarios, en este script:

--------------Inicio del script "cdsalva" -------------
#!/bin/sh

numero=1
grupo=`cat /var/log/volcado_duro.dir`
equipo="dev=1,0,0"
opciones="speed=16 driveropts=burnproof"
estado="/var/log/estado.log"
carga="-eject"
echo "----------------------------" >> $estado
echo "La última serie fue $grupo" >> $estado
if [ "$grupo" = "impar" ] ; then
	grunum="2"
	echo "par" > /var/log/volcado_duro.dir
else
	grunum="1"
	echo "impar" > /var/log/volcado_duro.dir
fi
echo "La serie actual es `cat /var/log/volcado_duro.dir`" >> $estado
echo "----------------------------" >> $estado
for archivo in `find /datos/seguridad/cd/* -print`
do
	echo "Grabando" > /var/log/volcado_cd.dir
	echo "Grabando CD-RW $grunum/$numero" >> $estado
	echo -e " "
	echo -e "-----------------------------"
	echo -e "Introduce el CD-RW "$numero" del grupo "$grupo "("$grunum"/"$numero")"
	echo -e "y pulsa la tecla Intro"
	read seleccion
	echo -e "Borrando el CD-RW"
	nice -20 cdrecord $equipo blank=fast > /dev/null 2>&1
	echo -e "Grabando la imagen "$archivo
	nice -20 cdrecord $opciones $carga $equipo $archivo > /dev/null 2>&1
	status=$?
	if [ "$status" -eq 0 ]; then
		echo "Salvado" > /var/log/volcado_cd.dir
		echo "Grabado con éxito. Copia a salvo." >> $estado
	fi
	# Mi CD-RW no puede leer el CD recien grabado salvo
	# que se saque y se vuelva a cargar. Por eso, el comando cdrecord anterior lo
	# sacó con la opción -eject, y ahora hay que volver a cargarlo con -load
	# para que se pueda leer y comprobar el md5. Al ejecutar el script habrá un 
	# movimiento de recarga automática de la bandeja del CD.
	carga="-load"
	nice -20 cdrecord $carga $equipo
	# Mi sistema usa autofs, con lo que monta automáticamente todos los CD. Si
	# vosotros no usais autofs, tendreis que añadir un comando que lo haga:
	# mount /mnt/cdrw 
	echo -e $rojo"Comprobando "$nocolor"el CD-RW "$grunum/$numero
	echo "Comprobando CD-RW $grunum/$numero" >> $estado
	nbloques=`df -k /mnt/cdrw | grep cdrw | cut -c 25-30`
	md5cd=`dd if=/dev/scd0 count=$nbloques bs=1024 | md5sum | awk '{print $1}'`
	echo "El MD5 del CD-RW $grunum/$numero es $md5cd" >> $estado
	md5iso=`cat /var/log/md5_$numero.log`
	echo "El MD5 de la iso $grunum/$numero es $md5iso" >> $estado
	# y salvo que useis autofs, también un comando que desmonte
	# antes de abrir la bandeja para cambiar el CD:
	# umount /mnt/cdrw
	carga="-eject"
	nice -20 cdrecord $carga $equipo
	numero=`expr $numero + 1`
done
echo "Hecho" >> $estado
-------------- Final del script "cdsalva" -------------

Con esto, los scripts son ya completamente funcionales. No lo hacen todo, así que esto no ha terminado (por ejemplo, no hacen copias incrementales, solo completas o "full backup"). Pero lo que hacen ya es lo suficientemente sofisticado para que nos sea útil, así que lo metemos en el cron para que empiece a ser desatendido. Para ello solo hay que hacerse root y editar el fichero /etc/crontab dejandolo así (lo añadido en color cian y negrita):

	SHELL=/bin/bash
	PATH=/sbin:/bin:/usr/sbin:/usr/bin
	MAILTO=root
	HOME=/
 
	# run-parts
	01 * * * * root run-parts /etc/cron.hourly
	02 4 * * * root run-parts /etc/cron.daily
	22 4 * * 0 root run-parts /etc/cron.weekly
	42 4 1 * * root run-parts /etc/cron.monthly
	33 1 15 * * root /usr/local/bin/seguridad
	17 1 30 * * root /usr/local/bin/seguridad

Esto ejecutará nuestros scripts dos veces al mes, el día 15 a las 1:33 y el día 30 a las 1:17. Mi ordenador está casi siempre encendido, esté o no en casa (entre otras cosas, para que funcione el mldonkey), por lo que las horas nocturnas son buenas para que el ordenador se encargue de estas cosas: yo no estoy usándolo, y él no tiene nada mejor que hacer. Solo tengo que ocuparme de ejecutar el script "cdsalva" el día 1 y el 16 para pasarlo todo a CD. Claro que más adelante haremos que él nos lo recuerde, claro. :-)

Pero las cosas a su tiempo, porque el siguiente paso será crear copias de seguridad incrementales diarias, naturalmente también desatendidas. Eso queda para otro día, ¿vale?. :-)

21/09/2004

Hoy un par de cambios menores. Uno para preparar la creación de copias incrementales, el otro para que el script de grabación de error si el tamaño de la imagen es excesivo. También haremos que solo root pueda ejecutar los scripts. Al fin y al cabo, uno de ellos será ejecutado siempre por el cron, automáticamente. Y el otro permite sacar datos de otros usuarios a CD. A mi no me importa porque es un ordenador privado, pero dice la teoría que esa sería una labor crítica que solo root debería hacer.

Empezamos por modificar el script "seguridad", el que ejecutará siempre cron.

--------------Comienza script "seguridad" -------------
#!/bin/bash
# Este script sirve para hacer copias de seguridad
# usando grupos de CD-RW rotatorios
# ---------------------------------

DIRS1="/datos/textos \
       /datos/personal \
       /datos/profesional"
DIRS2="/datos/compartidos \
       /datos/evolution_lacofi \
       /datos/evolution_maria \
       /datos/externos"
DIRS3="/home \
       /etc \
       /root \
       /datos/dosemu \
       /datos/imagen"
TEMP="/datos/seguridad/zap"
FECHA=`date +%d%b%Y`
IMAGEN="/datos/seguridad/cd"
RIP="arranque/rip.img"
CAT="arranque/catalogo.cat"
ESTADO="/var/log/estado.log"
CONTROL="/var/log/backup"

if [ "$UID" = "0" ]; then

if [ "$1" = "reset" ]; then
	if [ -f /var/log/volcado_duro.dir ]; then
		echo "El status es: "`cat /var/log/volcado_duro.dir`
	else
		echo "impar" > /var/log/volcado_duro.dir
	fi
	if [ -d "/datos/seguridad" ]; then
		echo "/datos/seguridad existe"
	else
		echo "Creando /datos/seguridad"
		mkdir /datos/seguridad
	fi
	if [ -d "/datos/seguridad/cd" ]; then
		echo "/datos/seguridad/cd existe"
	else
		echo "Creando /datos/seguridad/cd"
		mkdir /datos/seguridad/cd
	fi
	if [ -d "/datos/seguridad/zap" ]; then
		echo "/datos/seguridad/zap existe"
	else
		echo "Creando /datos/seguridad/zap"
		mkdir /datos/seguridad/zap
	fi
	if [ -d "/datos/seguridad/zap/arranque" ]; then
		echo "/datos/seguridad/zap/arranque existe"
	else
		echo "Creando /datos/seguridad/zap/arranque"
		mkdir /datos/seguridad/zap/arranque
		echo "Debes colocar en /datos/seguridad/zap/arranque"
		echo "una imagen de rescate, preferiblemente RIP"
	fi
	echo "El registro ha sido reseteado" > $ESTADO
	echo "-----------------------------" >> $ESTADO
	exit
fi

# Poner todo a cero
rm -Rf /datos/seguridad/zap/*.tar.bz2
rm -Rf /datos/seguridad/cd/*.iso
rm -Rf /var/log/md5_*.log
rm -Rf $CONTROL*.log

# Ahora creamos ficheros tar con la copia de seguridad
# y luego los pasamos a imágenes ISO, listas para grabar
for NUM in 1 2 3
do
	echo "Creando serie$NUM-$FECHA.tar.bz2" >> $ESTADO
	tar cjf $TEMP/serie$NUM-$FECHA.tar.bz2 --listed-incremental=$CONTROL$NUM.log \
		`eval echo '$DIRS'$NUM` &> /dev/null
	status=$?
	echo "La compresión se ejecutó con código $status" >> $ESTADO
	mkisofs -r -o $IMAGEN/serie$NUM-$FECHA.iso \
		-b $RIP -c $CAT $TEMP &> /dev/null
	status=$?
	echo "La imagen ISO se creó con código $status" >> $ESTADO
	sumamd5=`md5sum $IMAGEN/serie$NUM-$FECHA.iso | awk '{print $1}'`
	echo $sumamd5 >> /var/log/md5_$NUM.log
	rm $TEMP/*.tar.bz2
done

if [ -f "$IMAGEN/serie1-$FECHA.iso" ] || 
  	[ -f "$IMAGEN/serie2-$FECHA.iso" ] || 
  	[ -f "$IMAGEN/serie3-$FECHA.iso" ]; then
	echo "Pendiente" > /var/log/volcado_cd.dir
	echo "Copia de seguridad almacenada en el dico duro" >> $ESTADO
	echo "La copia de seguridad no está a salvo" >> $ESTADO
fi

else
	echo "¡Solo root puede hacer eso!"
fi	
--------------Final de script "seguridad" -------------

Y ahora modificamos el script "cdsalva".

--------------Inicio del script "cdsalva" -------------
#!/bin/sh

numero=1
grupo=`cat /var/log/volcado_duro.dir`
equipo="dev=1,0,0"
opciones="speed=16 driveropts=burnproof"
estado="/var/log/estado.log"
carga="-eject"
valido="si"
declare -i megas

if [ "$UID" = "0" ]; then
echo "----------------------------" >> $estado
echo "La última serie fue $grupo" >> $estado
if [ "$grupo" = "impar" ] ; then
	grunum="2"
	echo "par" > /var/log/volcado_duro.dir
else
	grunum="1"
	echo "impar" > /var/log/volcado_duro.dir
fi
echo "La serie actual es `cat /var/log/volcado_duro.dir`" >> $estado
echo "----------------------------" >> $estado
for archivo in `find /datos/seguridad/cd/* -print`
do
	# Un CD-RW no puede sobrepasar los 700 Mb
	megas=`du -m $archivo | awk '{print $1}'`
	if [ "$megas" -gt 700 ]; then
		echo "El CD-RW $grunum/$numero tendría $megas Mb" >> $estado
		echo "Operación cancelada" >> $estado
		echo "El CD-RW $grunum/$numero tendría $megas Mb"
		echo "Revisa tus configuraciones o no podrás sacar nada a CD"
		valido="no"
	fi
	if [ "$valido" = "si" ]; then
	echo "Grabando" > /var/log/volcado_cd.dir
	echo "Grabando CD-RW $grunum/$numero, de $megas Mb" >> $estado
	echo -e " "
	echo -e "-----------------------------"
	echo -e "Introduce el CD-RW "$numero" del grupo "$grupo "("$grunum"/"$numero")"
	echo -e "y pulsa la tecla Intro"
	read seleccion
	echo -e "Borrando el CD-RW"
	nice -20 cdrecord $equipo blank=fast > /dev/null 2>&1
	echo -e "Grabando la imagen "$archivo
	nice -20 cdrecord $opciones $carga $equipo $archivo > /dev/null 2>&1
	status=$?
	if [ "$status" -eq 0 ]; then
		echo "Salvado" > /var/log/volcado_cd.dir
		echo "Grabado con éxito. Copia a salvo." >> $estado
	fi
	# Mi CD-RW no puede leer el CD recien grabado salvo
	# que se saque y se vuelva a cargar. Por eso, el comando cdrecord anterior lo
	# sacó con la opción -eject, y ahora hay que volver a cargarlo con -load
	# para que se pueda leer y comprobar el md5. Al ejecutar el script habrá un 
	# movimiento de recarga automática de la bandeja del CD.
	carga="-load"
	nice -20 cdrecord $carga $equipo
	# Mi sistema usa autofs, con lo que monta automáticamente todos los CD. Si
	# vosotros no usais autofs, tendreis que añadir un comando que lo haga:
	# mount /mnt/cdrw 
	echo -e $rojo"Comprobando "$nocolor"el CD-RW "$grunum/$numero
	echo "Comprobando CD-RW $grunum/$numero" >> $estado
	nbloques=`df -k /mnt/cdrw | grep cdrw | cut -c 25-30`
	md5cd=`dd if=/dev/scd0 count=$nbloques bs=1024 | md5sum | awk '{print $1}'`
	echo "El MD5 del CD-RW $grunum/$numero es $md5cd" >> $estado
	md5iso=`cat /var/log/md5_$numero.log`
	echo "El MD5 de la iso $grunum/$numero es $md5iso" >> $estado
	# y salvo que useis autofs, también un comando que desmonte
	# antes de abrir la bandeja para cambiar el CD:
	# umount /mnt/cdrw
	carga="-eject"
	nice -20 cdrecord $carga $equipo
	fi
	numero=`expr $numero + 1`
done
echo "Hecho" >> $estado
else
	echo "¡Solo root puede hacer eso"
fi
-------------- Final del script "cdsalva" -------------

Con una sencilla opción --listed-incremental, hemos añadido ya lo necesario para preparar las copias incrementales. Ahora, nuestro fichero "seguridad" quincenal no solo creará un tar y lo pasará a ISO, sino que también creará un fichero de control en el que registra qué se ha guardado, y que podrá usar otro script para las copias diarias.

Habrá que crear ese nuevo script, claro, pero eso queda para otra vez.

26/09/2004

Hoy vamos a dar poner en marcha las copias incrementales. Primero, alguna pequeña modificación en el script "seguridad". Hay que crear el directorio /datos/seguridad/diario, bien a mano, o bien usando el propio script con la opción reset, una vez modificado.

--------------Comienza script "seguridad" -------------
#!/bin/bash
# Este script sirve para hacer copias de seguridad
# usando grupos de CD-RW rotatorios
# ---------------------------------

DIRS1="/datos/textos \
       /datos/personal \
       /datos/profesional"
DIRS2="/datos/compartidos \
       /datos/evolution_lacofi \
       /datos/evolution_maria \
       /datos/externos"
DIRS3="/home \
       /etc \
       /root \
       /datos/dosemu \
       /datos/imagen"
TEMP="/datos/seguridad/zap"
FECHA=`date +%d%b%Y`
IMAGEN="/datos/seguridad/cd"
DIARIO="/datos/seguridad/diario"
RIP="arranque/rip.img"
CAT="arranque/catalogo.cat"
ESTADO="/var/log/estado.log"
CONTROL="/var/log/backup"

if [ "$UID" = "0" ]; then

if [ "$1" = "reset" ]; then
	if [ -f /var/log/volcado_duro.dir ]; then
		echo "El status es: "`cat /var/log/volcado_duro.dir`
	else
		echo "impar" > /var/log/volcado_duro.dir
	fi
	if [ -d "/datos/seguridad" ]; then
		echo "/datos/seguridad existe"
	else
		echo "Creando /datos/seguridad"
		mkdir /datos/seguridad
	fi
	if [ -d "/datos/seguridad/cd" ]; then
		echo "/datos/seguridad/cd existe"
	else
		echo "Creando /datos/seguridad/cd"
		mkdir /datos/seguridad/cd
	fi
	if [ -d "/datos/seguridad/zap" ]; then
		echo "/datos/seguridad/zap existe"
	else
		echo "Creando /datos/seguridad/zap"
		mkdir /datos/seguridad/zap
	fi
	if [ -d "/datos/seguridad/diario" ]; then
		echo "/datos/seguridad/diario existe"
	else
		echo "Creando /datos/seguridad/zap"
		mkdir /datos/seguridad/diario
	fi
	
	if [ -d "/datos/seguridad/zap/arranque" ]; then
		echo "/datos/seguridad/zap/arranque existe"
	else
		echo "Creando /datos/seguridad/zap/arranque"
		mkdir /datos/seguridad/zap/arranque
		echo "Debes colocar en /datos/seguridad/zap/arranque"
		echo "una imagen de rescate, preferiblemente RIP"
	fi
	echo "El registro ha sido reseteado" > $ESTADO
	echo "-----------------------------" >> $ESTADO
	exit
fi

# Poner todo a cero
rm -Rf /datos/seguridad/zap/*.tar.bz2
rm -Rf /datos/seguridad/cd/*.iso
rm -Rf /var/log/md5_*.log
rm -Rf $CONTROL*.log
rm -Rf $DIARIO/*.tar.bz2

# Ahora creamos ficheros tar con la copia de seguridad
# y luego los pasamos a imágenes ISO, listas para grabar
for NUM in 1 2 3
do
	echo "Creando serie$NUM-$FECHA.tar.bz2" >> $ESTADO
	tar cjf $TEMP/serie$NUM-$FECHA.tar.bz2 \
		--listed-incremental=$CONTROL$NUM.log \
		`eval echo '$DIRS'$NUM` &> /dev/null
	status=$?
	echo "La compresión se ejecutó con código $status" >> $ESTADO
	mkisofs -r -o $IMAGEN/serie$NUM-$FECHA.iso \
		-b $RIP -c $CAT $TEMP &> /dev/null
	status=$?
	echo "La imagen ISO se creó con código $status" >> $ESTADO
	sumamd5=`md5sum $IMAGEN/serie$NUM-$FECHA.iso | awk '{print $1}'`
	echo $sumamd5 >> /var/log/md5_$NUM.log
	rm $TEMP/*.tar.bz2
done

if [ -f "$IMAGEN/serie1-$FECHA.iso" ] || 
  	[ -f "$IMAGEN/serie2-$FECHA.iso" ] || 
  	[ -f "$IMAGEN/serie3-$FECHA.iso" ]; then
	echo "Pendiente" > /var/log/volcado_cd.dir
	echo "Copia de seguridad almacenada en el dico duro" >> $ESTADO
	echo "La copia de seguridad no está a salvo" >> $ESTADO
fi

else
	echo "¡Solo root puede hacer eso!"
fi	
--------------Final de script "seguridad" -------------

Ahora vamos a crear un nuevo script, llamado "completar", que será el encargado de hacer las copias de seguridad diarias:

--------------Inicio de script "completar" ------------
#!/bin/sh

DIRS1="/datos/textos \
		/datos/personal \
		/datos/profesional"
DIRS2="/datos/compartidos \
		/datos/evolution_lacofi \
		/datos/evolution_maria \
		/datos/externos" 
DIRS3="/home \
		/etc \
		/root \
		/datos/dosemu \
		/datos/imagen " 
TEMP="/datos/seguridad/zap"
FECHA=`date +%d%b%Y`
IMAGEN="/datos/seguridad/cd"
DIARIO="/datos/seguridad/diario"
ESTADO="/var/log/estado.log"
CONTROL="/var/log/backup"
if [ "$UID" = "0" ]; then
	for NUM in 1 2 3
	do
		tar cjf $DIARIO/$FECHA-diario$NUM.tar.bz2 \
		--listed-incremental=$CONTROL$NUM.log `eval echo '$DIRS'$NUM` &> /dev/null
		status=$?
		echo "----------------------------------------------" >> $ESTADO
		echo "Se ha añadido el fichero incremental" >> $ESTADO
		echo "diario$NUM-$FECHA.tar.bz2" >> $ESTADO
		echo "con el código de salida $status" >> $ESTADO
	done
else
	echo "¡Solo root debería hacer eso!"
fi
--------------Final de script "completar" -------------

Este script lo activamos usando cron, y para ello modificaremos nuestro /etc/crontab dejandolo así (cambios en cian y negrita):

	SHELL=/bin/bash
	PATH=/sbin:/bin:/usr/sbin:/usr/bin
	MAILTO=root
	HOME=/
 
	# run-parts
	01 * * * * root run-parts /etc/cron.hourly
	02 4 * * * root run-parts /etc/cron.daily
	22 4 * * 0 root run-parts /etc/cron.weekly
	42 4 1 * * root run-parts /etc/cron.monthly
	33 1 15 * * root /usr/local/bin/seguridad
	17 1 30 * * root /usr/local/bin/seguridad
	11 0 * * * root /usr/local/bin/completar

De esta forma, el script "seguridad" se encargará de borrar todas las copias diarias situadas en /datos/seguridad/diario, y luego hacer una copia completa. Esto ocurre dos veces al mes. Mientras tanto, cada día, el script "completar" va añadiendo copias diarias de los ficheros añadidos o modificados desde el día anterior, y seguirá haciéndolo hasta que toque copia completa y el script "seguridad" lo vuelva a borrar.

Pero nos queda algo importante: modificar nuestro script "cdsalva" de tal forma que nos permita sacar a CD también las copias incrementales. Para ello usaremos un CD-RW aparte (un cuarto CD), que se grabará en formato multisesión, lo que nos permite ir añadiendo datos a medida que se vayan creando más backups incrementales. Pero eso lo haremos otro día. ;-)

27/09/2004

Hoy vamos a completar nuestro script "cdsalva", de tal forma que sirva no solo para grabar los CD-RW con las copias totales, sino también el CD-RW con las copias incrementales. Y no solo eso, sino que decida él solito cuándo toca lo uno o lo otro, y la mejor forma de hacerlo. Para que nuestro script pueda tomar esas decisiones, usará un pequeño fichero de control que los otros scripts han ido creando: "/var/log/volcado_duro.dir". Aquí es donde empezamos a ver su auténtica utilidad. Así quedaría, como siempre con los cambios en cian y negrita:

--------------Inicio de script "cdsalva" -------------
#!/bin/sh

numero=1
grupo=`cat /var/log/volcado_duro.dir`
equipo="dev=1,0,0"
opciones="speed=16 driveropts=burnproof"
multisesion="-R -J -r -T"
temporal="/datos/seguridad/cd/diario.iso"
estado="/var/log/estado.log"
carga="-eject"
registro=`cat /var/log/volcado_cd.dir`
diario="/datos/seguridad/diario"
declare -i megas

if [ "$registro" = "Pendiente" ]; then
	valido="si"
else
	valido="no"
fi

# Copias de seguridad incrementales

if [ "$registro" = "Actualizando" ]; then
	echo -e "Actualizando las copias incrementales" >> $estado
	echo -e "Grabando CD-RW 4 Multisesión. Iniciando Multisesión"
	echo -e " "
	echo -e "-----------------------------"
	echo -e "Introduce el CD-RW 4 Multisesión para "
	echo -e "Copias Incrementales y pulsa la tecla Intro"
	read seleccion
	echo -e "Preparando la copia de archivos"
	codigo=`cdrecord -msinfo $equipo`
	mkisofs $multisesion -C $codigo -N -M /dev/scd0 -o $temporal $diario
	status=$?

	if [ "$status" -eq 0 ]; then
		echo "Preparada copia incremental con salida $status" >> $estado
	fi

	echo -e "Grabando nuevos archivos en CD-RW"
	nice -20 cdrecord $opciones -multi $carga $equipo -data $temporal > /dev/null 2>&1
	status=$?

	if [ "$status" -eq 0 ]; then
		echo "Grabada copia incremental con salida $status." >> $estado
		rm -Rf $diario/*.tar.bz2
		rm -Rf /datos/seguridad/cd/diario.iso
	fi
fi

if [ "$registro" = "Salvado" ]; then
	echo "Actualizando" > /var/log/volcado_cd.dir
	echo -e "--------------------------------------" >> $estado
	echo -e "Empezando a hacer copias incrementales" >> $estado
	echo -e "Grabando CD-RW 4 Multisesión. Iniciando Multisesión"
	echo -e " "
	echo -e "-----------------------------"
	echo -e "Introduce el CD-RW 4 Multisesión para "
	echo -e "Copias Incrementales y pulsa la tecla Intro"
	read selecccion
	echo -e "Preparando la copia de archivos"
	mkisofs -V "Incremental" $multisesion -o $temporal $diario
	status=$?

	if [ "$status" -eq 0 ]; then
		echo "Preparada copia incremental con salida $status" >> $estado
	fi

	echo -e "Borrando el CD-RW"
	nice -20 cdrecord $equipo blank=fast > /dev/null 2>&1
	echo -e "Grabando copias diarias."
	nice -20 cdrecord $opciones -multi $carga $equipo -data $temporal > /dev/null 2>&1
	status=$?

	if [ "$status" -eq 0 ]; then
		echo "Grabada copia incremental con salida $status." >> $estado
		echo "Iniciada nueva serie incremental" >> $estado
		echo "--------------------------------" >> $estado
		rm -Rf $diario/*.tar.bz2
		rm -Rf $temporal
	fi
fi


# Y ahora las copias totales
if [ "$UID" = "0" ]; then #------ pero solo si eres root

  if [ "$valido" = "si" ]; then
    echo "----------------------------" >> $estado
    echo "La última serie fue $grupo" >> $estado
    if [ "$grupo" = "impar" ] ; then
	    grunum="2"
	    echo "par" > /var/log/volcado_duro.dir
    else
	    grunum="1"
	    echo "impar" > /var/log/volcado_duro.dir
    fi
    echo "La serie actual es `cat /var/log/volcado_duro.dir`" >> $estado
    echo "----------------------------" >> $estado
  fi
  
  # Un bucle de oro ----------------------------------------
  for archivo in `find /datos/seguridad/cd/* -print 2>/dev/null`
  do             
	# Un CD-RW no puede sobrepasar los 700 Mb
	megas=`du -m $archivo | awk '{print $1}'`

	if [ $megas -gt 700 ]; then
		echo -e "El CD-RW $grunum/$numero tendría $megas Mb" >> $estado
		echo -e "Operación cancelada" > $estado
		echo -e "El CD-RW $grunum/$numero tendría $megas Mb"
		echo -e "Revisa tus configuraciones o no podrás sacar nada a CD"
		valido="no"
	fi

	if [ "$valido" = "si" ]; then
	echo "Grabando" > /var/log/volcado_cd.dir
	echo "Grabando CD-RW $grunum/$numero, de $megas Mb" >> $estado
	echo -e " "
	echo -e "-----------------------------"
	echo -e "Introduce el CD-RW "$numero" del grupo "$grupo "("$grunum"/"$numero")"
	echo -e "y pulsa la tecla Intro"
	read seleccion
	echo -e "Borrando el CD-RW"
	nice -20 cdrecord $equipo blank=fast > /dev/null 2>&1
	echo -e "Grabando el archivo "$archivo
	nice -20 cdrecord $opciones $carga $equipo $archivo > /dev/null 2>&1
	status=$?

	if [ "$status" -eq 0 ]; then
		echo "Salvado" > /var/log/volcado_cd.dir
		echo "Grabado con éxito. Copia a salvo." >> $estado
		rm -Rf $archivo
	fi

	# Mi CD-RW no puede leer el CD recien grabado salvo
	# que se saque y se vuelva a cargar. Por eso, el comando cdrecord anterior lo
	# sacó con la opción -eject, y ahora hay que volver a cargarlo con -load
	# para que se pueda leer y comprobar el md5. Al ejecutar el script habrá un 
	# movimiento de recarga automática de la bandeja del CD.
	carga="-load"
	nice -20 cdrecord $carga $equipo > /dev/null 2>&1
	# Mi sistema usa autofs, con lo que monta automáticamente todos los CD. Si
	# vosotros no usais autofs, tendreis que añadir un comando que lo haga:
	# mount /mnt/cdrw 
	echo -e "Comprobando el CD-RW "$grunum/$numero
	echo "Comprobando CD-RW $grunum/$numero" >> $estado
	nbloques=`df -k /mnt/cdrw | grep cdrw | cut -c 25-30`
	md5cd=`dd if=/dev/scd0 count=$nbloques bs=1024 | md5sum | awk '{print $1}'`
	echo "El MD5 del CD-RW $grunum/$numero es $md5cd" >> $estado
	md5iso=`cat /var/log/md5_$numero.log`
	echo "El MD5 de la iso $grunum/$numero es $md5iso" >> $estado
	# y salvo que useis autofs, también un comando que desmonte
	# antes de abrir la bandeja para cambiar el CD:
	# umount /mnt/cdrw
	carga="-eject"
	nice -20 cdrecord $carga $equipo > /dev/null 2>&1
	fi
	
	# ---------sumamos uno a NUM y reiniciamos el bucle
	numero=`expr $numero + 1`

  done
  echo "Hecho" >> $estado

else # ------------- Si no eres root...
	echo "¡Solo root puede hacer eso!"
fi # --------------- Y aquí termina

--------------Final de script "cdsalva" --------------

Con esto, los tres scripts son ya completamente funcionales y a través de crontab se ocupan ellos solitos de todo. Lo único que hay que hacer es ejecutar al final de la jornada el script "cdsalva". Él nos pedirá lo que necesita: los CD-RW de la serie adecuada, para hacer copias completas, o solo el CD-RW número 4 para hacer copias incrementales que se van añadiendo sucesivamente mediante multisesión. Si un día nos olvidamos de hacerlo, no pasa nada, porque el próximo día copiará simplemente los ficheros incrementales del día y también los previos, si hay alguno.

Eso sí, aún se pueden hacer múltiples cambios estéticos, como añadir colores a la salida en consola, o depurar los registros para que queden más bonitos. No pondré aquí esas chorradas, pero si quieres echarles un vistazo tú mismo, solo tienes que bajarte el fichero tal y como funciona actualmente en mi ordenador. Aquí tienes los scripts:

  1. seguridad
  2. cdsalva
  3. completar

Y recuerda que, si quieres más seguridad, el directorio /datos/seguridad debería estar realmente situado en otra partición, solo accesible por root. Y mejor si permanece montada solo el tiempo necesario para que funcionen los scripts. Pero en fin, eso ya depende de manías personales...

Que lo disfrutes. ;-)

6/10/2004

Nuestro script está funcionando sin problemas, de forma desatendida salvo para hacer funcionar el script "cdsalva" una vez al día. La cosa parece que funciona, y lo hace bien. Pero todo en esta vida es mejorable, naturalmente. ¿Qué tal, por ejemplo, un interfaz gráfico?. ¿Y hacer que la salida log sea coloreada?. :-)

Se puede hacer ambas cosas, claro que sí. Eso y más. Para ello solo necesitamos el programa zenity o gdialog, incluido en casi todas las distribuciones actuales. Zenity permite a un script bash normal sacar cuadros de diálogo gnome, para información o para introducir datos.

Hoy cambiaremos nuestro script "cdsalva" para conseguir varias cosas:

  1. Que los log se guarden coloreados (más agradables a la vista cuando sean consultados).
  2. Que si arrancamos el script en un entorno gráfico, ya sea desde un icono de nuestro escritorio, o desde una terminal, nos muestre cuadros de diálogo gráficos, en los que nos pida los CD o nos diga lo que está haciendo, incluso con barras de progreso, como los programas de verdad. ;-)
  3. Que siga siendo compatible con consola, de tal forma que si lo arrancamos desde una shell remota, o desde una verdadera consola, el script lo detecte solito y prescinda de Zenity, enviando todos los mensajes a la salida estandar (a veces uso mis scripts desde el otro ordenador, de forma remota, por ejemplo porque lo esté usando mi esposa).
  4. Que no necesite usar contraseña, a pesar de que usa permisos privilegiados (estoy hablando de sudo, claro).
  5. Además, crearemos un alias para facilitar la consulta.

Puedes volver a ver los scripts, tal y como están ahora, justo debajo. Observa especialmente los cambios en "cdsalva" y las referencias a zenity. Observa cómo uso las variables (en especial $TERM para que el script decida cuándo usar zenity y cuándo la salida estandar.

  1. seguridad
  2. cdsalva
  3. completar

Bien, ahora hay que modificar el fichero /etc/sudoers y meter una entrada tal que así:

	lacofi	ALL= NOPASSWD:	/datos/programas-usuario/cdsalva

Y ahora creamos un icono en nuestro escritorio que ejecute el comando "sudo /datos/programas-usuario/cdsalva". Ya está, ahora basta con hacer click para que se ponga en marcha el script, con cuadros de diálogo gráficos y todo.

Y por último, creamos un alias, metiendo en nuestro ".bashrc" o donde sea, el siguiente comando:

	alias status='cat /var/log/estado.log'

Con lo cual, basta con teclear "status", para que nos salga nuestro log coloreado en el que poder comprobar cómo han ido nuestras copias de seguridad.

  *

El sistema definitivo (Disco duro externo)

Hoy en día, los discos duros externos USB de alta capacidad (160 Gb o más) se han vuelto tan comunes y tan baratos que se han convertido en el mejor sistema de copia de seguridad que puede existir: cabe todo, se pueden desenchufar y llevar a otro lado, nos liberan del uso de ningún compresor y de la falta de espacio... todo son ventajas. Si tienes la oportunidad cómprate uno, ya sea firewire o USB 2.0. Te aseguro que no te arrepentirás.

Y por si fuera poco, nuestro script de copia de seguridad también se simplifica brutalmente. Casi es un juego de niños.

Para un disco duro externo Firewire de 160 Gb, utilizo fdisk y lo divido en varias particiones: una de 60 Gb para almacenar música, programas, y datos viejos que tenía dispersos por montones de CD, otra de 30 Gb para una partición de seguridad que llamaremos "lacie_uno", otra de 30 Gb para otra partición de seguridad que llamaremos "lacie_dos", y el resto (unos 40 Gb) lo dejamos ahí sin particionar para cuando necesitemos más sitio, porque ahora no hace ninguna falta. Después, creamos un sistema de ficheros ext3 en ambas particiones usando el comando "mke2fs -j /dev/sdf2 && mke2fs -j /dev/sdf3". Suponiendo, naturalmente, que nuestro disco duro externo está en /dev/sdf.

Ahora creamos los directorios /mnt/lacie_uno y /mnt/lacie_dos. Y añadimos las entradas apropiadas en /etc/fstab, tal que así:

/dev/sdf2       /mnt/lacie_uno          ext3    user,noauto,ro          0 0
/dev/sdf3       /mnt/lacie_dos          ext3    user,noauto,ro          0 0

Observad un par de cosas: Ambas particiones se crean con la opción NOAUTO. Eso significa que siempre estarán DESmontadas y NO accesibles. Se pueden montar a mano ("mount /mnt/lacie_uno", por ejemplo), pero entonces lo harán con la opción RO, es decir, "solo lectura", con lo que siempre estarán a salvo de cualquier error, incluso cuando las estemos usando para recuperar algún dato. Naturalmente, nuestro script las montará en forma de "lectura escritura", pero siempre automatizado, siempre en forma desatendida, y solo el tiempo justo de hacer la copia de seguridad.

Ahora montamos las particiones y creamos los ficheros de control que vamos a necesitar. Lo hacemos como root.

[15:16:58/0][root@jeanette:~]# mount /dev/sdf2 /mnt/lacie_uno -t ext3 -o rw
[15:16:59/0][root@jeanette:~]# mount /dev/sdf2 /mnt/lacie_dos -t ext3 -o rw
[15:17:00/0][root@jeanette:~]# touch /mnt/lacie_uno/.lacie_uno 
[15:17:01/0][root@jeanette:~]# touch /mnt/lacie_dos/.lacie_dos
[15:17:02/0][root@jeanette:~]# mkdir /mnt/lacie_uno/espejo /mnt/lacie_dos/espejo
[15:17:04/0][root@jeanette:~]# umount /mnt/lacie_uno
[15:17:08/0][root@jeanette:~]# umount /mnt/lacie_dos
[15:17:10/0][root@jeanette:~]# echo 1 > /var/log/contador_salvaguarda.reg
[15:17:11/0][root@jeanette:~]# echo uno > /var/log/contador_serie.reg
[15:17:13/0][root@jeanette:~]# touch /var/log/seguridad.log
[15:17:15/0][root@jeanette:~]# touch /etc/seguridad.dir

Y ahora editamos el fichero /etc/seguridad.dir y metemos ahí todos los directorios que queremos salvar, uno por línea. Por ejempllo:

/home
/mnt/datos1
/mnt/datos2
/etc
/opt/programas-usuario
/opt/sunrise-0.42c
/root
/usr/local/bin

Y por último, el script. Mucho más sencillo que los anteriores, como veréis.

----------------- Comienza script "salvaguarda" -----------------------
 #!/bin/bash
# Primero definir una cuantas variables --------------------
declare -i numero
declare -i nivel
contador=/var/log/contador_salvaguarda.reg
marca=/var/log/contador_serie.reg
log=/var/log/seguridad.log
directorios=/etc/seguridad.dir
fecha=`date`
numero=`cat $contador`
serie=`cat $marca`
rojo=$'\e[1;31m'
verde=$'\e[1;32m'
amarillo=$'\e[1;33m'
cian=$'\e[1;36m'
normal=$'\e[0;39m'
abortar="no"
destino="/mnt/lacie_$serie/espejo"
nivel=30 
opcion=""

# Deducir donde estamos, exactamente -----------------------
if [ $numero -eq $nivel ]; then
	case $serie in 
		uno)
			echo dos > $marca
			dispositivo=sdf2
		;;
		dos)
			echo uno > $marca
			dispositivo=sdf3
		;;
		*)
			echo uno > $marca
			echo "$marca no tiene información válida." >> $log
			echo "Esto no debería estar pasando. Asumiremos serie uno." >> $log
			exit 1
		;;
	esac
	opcion="--delete --whole-file"
	numero=1
else
	case $serie in
		uno)
			dispositivo=sdf2
		;;
		dos)
			dispositivo=sdf3
		;;
		*)
			echo "$marca no tiene información válida." >> $log
			echo "Esto no debería estar pasando. Abortamos." >> $log
			exit 1
		;;
	esac
	opcion="--update"
	numero=`expr $numero+1`
fi
echo $numero > $contador

# Venga, vamos alla ----------------------------------------
if [ "$UID" = "0" ]; then 
	
	# Primero montar -----------------------------------
	echo " " >> $log
	echo "--- $fecha - Serie $serie / Nivel $numero ---" >> $log
	mount -t ext3 -o rw /dev/$dispositivo /mnt/lacie_$serie
	error=$?
	if [ $error -eq 0 ]; then
		echo "Se ha montado el disco duro externo /mnt/lacie_$serie" >> $log 
	else
		echo "No se pudo montar lacie_$serie!!" >> $log
		if [ -f /mnt/lacie_$serie/.lacie_$serie ]; then
			echo "Sin embargo parece estar ya montado. Seguimos." >> $log
		else
			echo "La unidad FireWire no funciona. COMPRUEBA ESO." >> $log
			abortar="si"
		fi
	fi

	# Ahora copiar -------------------------------------
	if [ "$abortar" = "no" ]; then
		for directorio in `cat $directorios`
		do
		rsync -qarzH $opcion $directorio $destino
		error=$?
		if [ $error -eq 0 ]; then
			echo "Sincronizado correctamente $directorio" >> $log
		else
			echo "Ha fallado la sincronizacion de $directorio" >> $log
		fi
		done
	fi
	# Y luego desmontar ---------------------------------

	umount /mnt/lacie_$serie
	if [ $error -eq 0 ]; then
		echo "Se ha desmontado el disco duro externo /mnt/lacie_$serie" >> $log
	else
		echo "No se pudo desmontar lacie_$serie!!" >> $log
	fi
	
# Y si no eres root, pues nada ------------------------------
else
	echo -ne "${rojo}Solo root${normal} puede usar este comando."
fi

function xwindow {
	zenity --info --info-text "Copia de seguridad actualizada"
}

function consola {
	echo  "Copia de seguridad actualizada"
}

if [ "$TERM" = "xterm" ] ||
	[ "$TERM" = "dumb" ]; then
	xwindow
else
	consola
fi
----------------- Termina script "salvaguarda" -----------------------

Este script debería pertenecer a root.root y tener permisos de ejecución para root. Y ya está. Solo tenemos que poner el script en nuestro crontab para que se ejecute todos los dias, por ejemplo a las 6 de la mañana, cuando no estorbe. Y él se encargará de todo, incluso de ir rotando las copias de seguridad.

  *

A vueltas con las copias de seguridad desatendidas automáticas

Como sabes, soy un fanático de las copias de seguridad, a veces hasta extremos realmente paranoicos. Pero no debería extrañarte, teniendo en cuenta que me gusta juguetear mucho con las configuraciones y las particiones del disco duro, así que es relativamente normal que tarde o temprano cometa un error infinitesimal. Sin entrar en detalles, la verdad es que mi paranoia respecto al backup me ha salvado el cuello tantas veces que ya he perdido la cuenta.

El caso es que he usado varios sistemas de backup automatizados a lo largo de los años. En CD, en DVD, en disco duro USB, pero siempre en forma de scripts automáticos y desatendidos que dejo metidos en crontab para que ellos se ocupen de todo. De hecho, esos scripts son tan desatendidos que nunca o casi nunca tengo que prestarles atención una vez funcionando. Están ahí, y ellos lo hacen todo... durante meses o años hasta que un día me doy cuenta de que necesito un dato atrasado. Una gozada, de verdad.

Lo que ahora te presento aquí es mi último script, esta vez hecho en lenguaje Perl. Es más sofisticado que los anteriores y hace alguna que otra virguería. Si te interesa, solo tienes que descargarlo. Probablemente puedas usarlo directamente con tan solo un par de modificaciones para adaptarlo a tu sistema.

El script es demasiado largo para ponertelo en pantalla, pero voy a contarte un poco lo que hace y cómo funciona.

Vale, en primer lugar tienes que crear tres ficheros que debes colocar en el directorio /etc/backup. El fichero /etc/backup/directorios.conf indica qué directorios quieres salvar a las copias de seguridad, así que debe contener algo así:

 
/home/lacofi
/home/maria
/home/imagen
/etc
/opt
/root
/usr/local/bin
/media/varios
/var/lib
/var/log

El fichero /etc/backup/excluidos.conf contiene aquellos ficheros o directorios que NO quieres que se salven a las copias de seguridad, por el motivo que sea. El listado puede ser en forma de expresiones regulares, así que ojito.

 
.gvfs
varios/fake/
wuala/direct/

Por ejemplo, el directorio /home/lacofi/wuala/direct no se copiaría aunque /home/lacofi sí está en el listado anterior. El motivo, es que ahí se monta una unidad de red comercial (una unidad en la nube, de esas).

Vale, ahora asegúrate de que creas el fichero de logs, que es donde irá registrándose todo lo sucedido en la sesión de backup. El script no saca ningún listado en pantalla: irá todo al log (se supone que funcionará siempre en el crontab).

 
[lacofi@selene ~]$ su
password:
[root@selene /home/lacofi]# touch /var/log/backup.log
[root@selene /home/lacofi]# exit
[lacofi@selene ~]$ 

El fichero /etc/backup/backup.conf es la madre del cordero porque contiene la configuración, y debe contener algo así:

 
# Configuración de backup
total=1
maximo=30
contador=30
nivelultra=60
serie=par
directorios=/etc/backup/directorios.conf
excluidos=/etc/backup/excluidos.conf
log=/var/log/seguridad.log
tipo=ext3
disco1=6d98f4d7-c644-43cc-8764-d1efe2bffa75
disco2=238e8f5c-844f-484e-9c5a-c742a7308b36
ultradev=ea8b4fd1-7bd1-4244-b5af-d25f6eaab544
ultrauni=/media/Lacie1Tb

Bueno, pues ahora te cuento lo que hace el script y de paso te explico lo que significan las entradas del fichero /etc/backup/backup.conf (es muy sencillo).

Lo primero que necesitas es un par de discos duros USB para hacer los backup. A ser posible, te recomiendo discos de 1Tb, aunque eso depende de la cantidad de datos que quieras salvar. Uno de esos discos será el principal. El otro será secundario y solo hará un backup muy de vez en cuando (en mi caso, una vez cada dos meses).

Toma el disco duro principal (de 1Tb) y reparticiona en dos mitades, de 500Gb cada una. Ahora mete dos entradas en /etc/fstab para que se monten como /media/Backup_par y /media/Backup_impar pero siempre en modo de solo lectura. Obviamente, los UUID que debes poner son los tuyos, no los mios, tanto en el fstab como en /etc/backup/backup.conf.

 
UUID=6d98f4d7-c644-43cc-8764-d1efe2bffa75 /media/Backup_impar	ext3	user,noauto,ro	0	0
UUID=238e8f5c-844f-484e-9c5a-c742a7308b36 /media/Backup_par	ext3	user,noauto,ro	0	0
UUID=ea8b4fd1-7bd1-4244-b5af-d25f6eaab544 /media/Lacie1Tb       ext3    user,noauto,rw  0       0

El disco duro secundario también debe estar en /etc/fstab, pero no necesita preparación. En realidad podría prescindirse de él, pero prefiero tenerlo para cubrir un caso de pura catástrofe: que el ordenador se averíe mientras hace el backup, dañándose a sí mismo y al mismo tiempo la tabla de particiones del disco de backup. Es una avería realmente complicada, pero vale más pensar mal. Bueno, en ese caso no lo perderías todo: aún queda el disco duro secundario con una copia de como mucho dos meses atrás.

Ahora se mete el script en el crontab (por ejemplo, puedes meterlo en /etc/cron.daily). Y se pondrá en marcha todos los días, por la noche. ¿Qué hace, exactamente ?.

Bueno, lo primero que hace es leer la configuración de /etc/backup/backup.conf. Ahí se encuentra con que el valor de "contador" coincide con el valor de "maximo", así que asume que hay que cambiar de set. Pone el contador en 1 y cambia la serie de par a impar. Luego desmonta el disco /media/Backup_impar, y lo vuelve a montar pero en modo de lectura y escritura. A continuación borra el directorio /media/Backup_impar/espejo y hace una compia de seguridad completa con rsync. Después desmonta /media/Backup_impar y lo vuelve a montar en modo de solo lectura, para que no pueda dañarse accidentalmente. Y a continuación, graba el fichero de log con un registro de todo lo que ha hecho.

Al día siguiente, volverá a hacer lo mismo. Pero esta vez verá que el valor de "contador" es 1, así que sigue con el mismo set "impar", y se limita a poner "contador" en 2. La copia de seguridad será solo incremental (únicamente los cambios). Y así será día tras día, hasta que "contador" vuelva a coincidir con "maximo" (entonces pondrá el "contador" en 1 y volverá a cambiar de set).

El valor de "total" se irá incrementando siempre en uno. Pero una vez cada dos meses su valor será un múltiplo entero de "nivelultra". Ese día, además de la copia de seguridad normal, hará otra copia extra completa en el disco duro secundario y luego borrará la precedente.

Y ya está. En caso de pérdida de datos, tendrás siempre una copia de seguridad actualizada hace menos de 24 horas. En caso de fallo lógico del disco de backup, aún te quedará el otro set con una copia actualizada hace menos de 1 mes. Y en caso de fallo físico del disco, aún tienes el otro disco actualizado hace menos de 2 meses.

Puedes consultar el fichero /var/log/seguridad.log si quieres ver lo que el script va haciendo día a día. Verías algo tal que así:

 
--- Sun Nov  1 07:58:56 CET 2009 - Serie impar / Nivel 14 (ejecución nº44) ---
Se ha montado la unidad /media/Backup_impar
Sincronizado /home/lacofi correctamente
Sincronizado /home/maria correctamente
Sincronizado /home/imagen correctamente
Sincronizado /etc correctamente
Sincronizado /opt correctamente
Sincronizado /root correctamente
Sincronizado /usr/local/bin correctamente
Sincronizado /media/varios correctamente
Sincronizado /var/lib correctamente
Sincronizado /var/log correctamente
La ocupación de la unidad impar es del 45% (194G de 459G)
 
--- Mon Nov  2 07:46:54 CET 2009 - Serie impar / Nivel 15 (ejecución nº45) ---
Se ha montado la unidad /media/Backup_impar
Sincronizado /home/lacofi correctamente
Sincronizado /home/maria correctamente
Sincronizado /home/imagen correctamente
Sincronizado /etc correctamente
Sincronizado /opt correctamente
Sincronizado /root correctamente
Sincronizado /usr/local/bin correctamente
Sincronizado /media/varios correctamente
Sincronizado /var/lib correctamente
Sincronizado /var/log correctamente
La ocupación de la unidad impar es del 45% (196G de 459G)

También puedes arrancar el script a mano con la opción "--simulacro". En ese caso, todo se ejecutará normalmente, pero el fichero de configuración no será actualizado, rsync será ejecutado en modo "--dry-run" (simulacro), y el registro de lo que se hace saldrá a pantalla y no al fichero de log.

Y además, cuando el script lea el fichero /etc/backup/backup.conf comprobará cuidadosamente todas las variables, y no se ejecutará si hay alguna errónea (dejará la queja apropiada en el log para que la veas).

Hay algún secretito más por ahí oculto. Pero dejo que lo descubras por tí mismo. Bájatelo y échale un vistazo. Considera el código como de dominio público. Y que lo disfrutes.


  • Sintaxis HTML 4.01 comprobada
  • Enlaces comprobados


Hecho con gvim * Hecho con CSS