Restaurando um arquivo aberto

Restaurando um arquivo aberto

Tenho um problema interessante que pode ou não ter solução, mas adoraria ter uma, se possível:

No Solaris, um arquivo de log aberto foi removido, que ainda continua a ser preenchido enquanto o processo está em execução, mas agora está inacessível para todas as outras ferramentas como cat, tail, etc.

Existe alguma maneira de restaurar a entrada do diretório para este arquivo enquanto tudo continua funcionando?

Responder1

Isso é possível, com alguns hackers e limitações (você precisa de privilégios de root).

Primeiro descubra qual descritor de arquivo está usando o aplicativo para gravar o arquivo de log e depois crie um link simbólico no local do arquivo de log anterior e apontando para a entrada do arquivo /proc, por exemplo:

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

A primeira limitação é que se o arquivo foi aberto apenas para escrita pelo processo, sua permissão não permitirá que um usuário sem privilégios leia seu conteúdo. No entanto, root e usuários com privilégio file_dac_read não serão afetados. Alternativamente, você pode usar um processo para copiar o conteúdo do arquivo, tailcomo Gilles está sugerindo em seu comentário. por exemplo:

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

A segunda questão é que todo o conteúdo do arquivo será perdido ( ln -s) ou parte dele ( tail -c 1 -f) quando o processo o fechar ou encerrar.

Uma solução alternativa é usar um programa que monitore esse evento e faça backup do arquivo antes que o fechamento seja realmente chamado.

As ferramentas prováveis ​​para fazer o trabalho são dtrace, truss, mdb ou dbx.

Aqui está uma prova de conceito usando dtrace no 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);
}'

informação relacionada