Restaurar un archivo abierto

Restaurar un archivo abierto

Tengo un problema interesante que puede tener solución o no, pero me encantaría tenerla si es posible:

En Solaris se eliminó un archivo de registro abierto, que aún continúa llenándose mientras se ejecuta el proceso, pero ahora es inaccesible para todas las demás herramientas como cat, tailetc.

¿Hay alguna forma de restaurar la entrada en el directorio de este archivo mientras todo sigue funcionando?

Respuesta1

Esto es factible, con algunos trucos y limitaciones (necesita privilegios de root).

Primero encuentre qué descriptor de archivo está usando la aplicación para escribir el archivo de registro y luego cree un enlace simbólico en la ubicación del archivo de registro anterior y apunte a la entrada del archivo /proc, por ejemplo:

ln -s /var/tmp/file.log /proc/12345/fd/3

La primera limitación es que si el archivo solo estaba abierto para escritura por parte del proceso, su permiso no permitirá que un usuario sin privilegios lea su contenido. Sin embargo, el root y los usuarios con el privilegio file_dac_read no se verán afectados. Alternativamente, puede utilizar un proceso para copiar el contenido del archivo tailcomo sugiere Gilles en su comentario. p.ej:

tail -c +1 -f /proc/12345/fd/5 > /var/tmp/file.log

El segundo problema es que todo el contenido del archivo se perderá ( ln -s) o parte de él ( tail -c 1 -f) cuando el proceso lo cierre o salga.

Una solución alternativa es utilizar un programa que supervise este evento y haga una copia de seguridad del archivo antes de que se llame al cierre.

Las herramientas probables para hacer el trabajo son dtrace, truss, mdb o dbx.

Aquí hay una prueba de concepto usando dtrace en Solaris 10.

#!/bin/ksh
#
# This dtrace script is monitoring a file descriptor for a given process
# and copy its content to the given path when the file is closed.
#

pid=${1:?"$0: Usage: pid fd path"}
fd=${2:?}
path=${3:?}
[[ -f $path ]] && { echo "$path exists"; exit 1; }
dtrace -w -n '
syscall::close:entry
/pid=='$pid' && arg0=='$fd'/
{
        stop();
        system("cp /proc/%d/fd/%d %s",pid,arg0,"'"$path"'");
        system("prun %d",pid);
        exit(0);
}'

información relacionada