Как восстановить удалённый лог через файловый дескриптор
Живое вмешательство. File descriptors и GDB.
читать первым в телеграм читать первым в макс
Написать статью для #балансбатл навеяли 2 события разной давности:
- Вопрос на собесе со мной в качестве соискателя: «Что такое файл в Linux?»
- Прочтение поста Романа «Один рубит — семеро в хуй трубят».
Что такое файл в Linux, читателю я предлагаю изучить самостоятельно или Роману раскрыть эту тему подробнее.
Далее эту идею постараюсь протянуть тонкой нитью через статью «Один рубит — семеро в хуй трубят».
Кратко: на собесе Серёжа удалил /var/log/nginx/access.log, далее обсуждается несколько моментов:
а) «Серёжа, а почему файл access.log пропал и больше не появляется? Nginx-то в данный момент работает, запросы на него идут»;
б) «Где карта, Билли? Нам нужна карта!»;
в) «Внятного ответа не получил, что-то на уровне — он появится спустя сутки, когда logrotate отработает. Да-да… будем сутки без логов сидеть».
Отвечаем на вопросы и решаем проблемы
а) удаление файлов в Linux лишь убирает название файла из файла каталога, но inode с данными остаётся, пока его держит открытым хоть один процесс.
Так что это не проблема. Идём дальше.
б) и в) Помогаем Билли с картой и не сидим сутки без логов.
Пока процесс открыт, он продолжает держать fd (файловые дескрипторы), даже если файл удалён.
1. Проверим утверждение. Пишем скрипт
#!/bin/bash
echo $$ > /tmp/nginx.pid
exec 3> /tmp/access.log
while true; do
echo "$(date) Где карта Билли? Нам нужна карта!!!" >&3
sleep 1
done
2. Запускаем, читаем логи
$ ./simple.sh &
[2] 13502
$ tail -f /tmp/access.log
Sun Aug 10 23:15:42 MSK 2025 Где карта Билли? Нам нужна карта!!!
Sun Aug 10 23:15:43 MSK 2025 Где карта Билли? Нам нужна карта!!!
3. Удалим файл
rm /tmp/access.log
4. Процесс продолжает писать в файл
Смотрим (PID знаем, смотри выше):
$ ls -l /proc/13502/fd
total 0
lrwx------ 1 aflw aflw 64 Aug 10 23:23 0 -> /dev/pts/0
lrwx------ 1 aflw aflw 64 Aug 10 23:23 1 -> /dev/pts/0
lrwx------ 1 aflw aflw 64 Aug 10 23:23 2 -> /dev/pts/0
lr-x------ 1 aflw aflw 64 Aug 10 23:23 255 -> /home/aflw/planka-backup/simple.sh
l-wx------ 1 aflw aflw 64 Aug 10 23:23 3 -> '/tmp/access.log (deleted)'
Или так (тут PID схватим по-другому), через watch наглядно:
watch -n1 -d "lsof -p $(cat /tmp/nginx.pid) | grep deleted"
Every 1.0s: lsof -p 13502 | grep deleted
simple.sh 13502 aflw 3w REG 8,32 188825 17988 /tmp/access.log (deleted)
5. Билли возвращает карту
cp /proc/13502/fd/3 /tmp/access.log.restored
Вуаля! Дескриптор НЕ потерялся, процесс не сошёл с ума, логи восстановлены.
Что дальше? Файл-то удалён!
Представим, что у нас серьёзный PROD, тяжеленный «Билли»нговый демон СверхБербанка, где перезапуск = потеря соединений и часовое восстановление сессий!
Подкинем новый файл /tmp/access_new.log без перезапуска процесса. Будем использовать отладчик gdb.
1. Запускаем
sudo gdb -p $(cat /tmp/nginx.pid)
2. Выполняем внутри gdb
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04.2) 12.1
# открываем новый файл для логов
(gdb) call (int) open("/tmp/access_new.log", 66, 0644)
$1 = 4 # новый файловый дескриптор
(gdb) call (int) dup2(4, 3) # дублируем fd 4 в позицию 3.
$2 = 3
(gdb) call (int) close(4) # Закрываем временный fd 4
$3 = 0
(gdb) detach
Detaching from program: /usr/bin/bash, process 13502
[Inferior 1 (process 13502) detached]
(gdb) quit
3. Проверяем файловые дескрипторы процесса и лог
$ ls -l /proc/13502/fd
total 0
lrwx------ 1 aflw aflw 64 Aug 10 23:23 0 -> /dev/pts/0
lrwx------ 1 aflw aflw 64 Aug 10 23:23 1 -> /dev/pts/0
lrwx------ 1 aflw aflw 64 Aug 10 23:23 2 -> /dev/pts/0
lr-x------ 1 aflw aflw 64 Aug 10 23:23 255 -> /home/aflw/planka-backup/simple.sh
lrwx------ 1 aflw aflw 64 Aug 10 23:23 3 -> /tmp/access_new.log
$ tail -f /tmp/access_new.log
Mon Aug 11 00:17:54 MSK 2025 Где карта Билли? Нам нужна карта!!!
