Предположим, что длительный процесс записывает в файл журнала. Предположим, что файл журнала остается открытым бесконечно. Предположим, что нерадивый системный администратор удаляет этот файл журнала. Может ли программа обнаружить, что это произошло?
Можно ли предположить, что fstat() сообщит о нулевом счетчике ссылок для удаленного файла?
Усечение, как мне кажется, немного сложнее. Частично это зависит от того, работает ли файловый дескриптор в режиме O_APPEND. Если файл журнала не запущен с O_APPEND, то текущая позиция записи дескриптора журнала программы не изменяется, и при усечении удаляются ведущие байты, но программа продолжает запись в «конце», оставляя пробел в воображаемом нулевые байты (они читаются как нули, но не обязательно занимают место на диске).
Если программа работает с O_APPEND, она будет писать в конец файла в том виде, в котором он существует в данный момент. Единственный способ наблюдать за усечением - это заметить, что позиция файла не там, где ее ожидала программа, что, в свою очередь, означает явное отслеживание этой позиции.
В целом, усечение меня не так волнует, как удаление, но любые мысли приветствуются.
@snot: Я не понимаю, как это могло бы сильно помочь. Консультативная блокировка будет проигнорирована; принудительная блокировка необычна - возможна, но я бы предпочел не делать этого. Кроме того, администраторам должно быть разрешено манипулировать журналами в разумных пределах. Возможно, программе нужен более совершенный механизм «файла журнала изменений».
Имена файлов - это просто указатели на файлы, когда ничто не указывает на файл, он удаляется. Поскольку вы всегда можете добавить ссылку, а затем удалить другую, счетчик ссылок может не помочь, лучшее, что вы, вероятно, можете сделать, - это проверить, существует ли имя файла или ваш процесс единственный, у которого есть ссылка на файл.
@ Роберт: не совсем; перед освобождением файл должен быть закрыт всеми процессами. Если демон удерживает файл открытым, то дисковое пространство остается выделенным для файла, а индексный дескриптор не используется повторно, даже если нет имени. Вы не можете повторно связать файл без имени, по крайней мере, в классическом Unix.
Ничто из того, что вы сказали, не противоречит тому, что я сказал.
@Robert: хорошо, тогда я обозначу свое непонимание того, что вы сказали, как проблему, накладываемую 300-символьными ограничениями на комментарии.





Проверка того, что fstat() возвращает нулевое количество ссылок, завершится ошибкой, если файл был жестко связан или переименован. Вместо этого я бы, вероятно, периодически сравнивал номер inode stat() с номером fstat().
Я не уверен насчет усечения.
tail -F проверяет удаление и, возможно, усечение, поэтому я бы проверил его источник, чтобы увидеть, как он это реализует.
Хороший момент в том, что количество ссылок не равно нулю, если файл был жестко связан. Для моих целей переименование не проблема. Мне нужно подумать, является ли связывание проблемой - данные по-прежнему доступны через какое-то имя, что является ключевым моментом.
Вы можете использовать inotify для просмотра файла журнала, отслеживая его на предмет событий файловой системы.
Интересное программное обеспечение, но я смотрю на него с точки зрения программы, пишущей файл журнала, а не с точки зрения внешнего «потребителя» файла журнала.
inotify может помочь вам с любой задачей, но он специфичен для Linux.
Предположим, неосторожный системный администратор убивает процесс. Вы действительно хотите защитить себя от случайных действий администратора? Я думаю, вы просто ищете способ время от времени создавать новый файл журнала, например, используя logrotate. Здесь достаточно предоставить возможность вручную разрешить программе повторно открыть файл журнала. Стандартный способ сделать это - прослушать HUP-сигнал в программе и повторно открыть файл журнала, если он поступит:
#include <signal.h>
volatile int f_sighup;
void sighup_handler() {
f_sighup = 1;
}
void trap_sighup() {
struct sigaction sa;
int rv;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = &sighup_handler;
rv = sigaction(SIGHUP, &sa, NULL);
if (-1 == rv) {
fprintf(stderr, "warning: setting SIGHUP signal handler failed");
}
}
int main() {
f_sighup = 0;
trap_sighup();
...
}
Затем регулярно проверяйте флаг f_sighup в основной программе, чтобы увидеть, нужно ли повторно открывать файл журнала.
Это хорошо работает с такими инструментами, как logrotate, которые могут переименовывать старый файл журнала и затем вызывать kill -s HUP $PID. А неосторожный сисадмин может сделать это вручную, удалив (а лучше переименовав) старый файл журнала.
Это должно быть «sig_atomic_t volatile f_sighup = 0;»?
Наверное, не повредит, хотя я бы предположил, что int всегда атомарен. volatile может быть хорошей идеей в любом случае.
Когда файл закрывается, время модификации изменяется. Поэтому периодически проверяйте mtime, используя stat ().
Если посмотреть на спецификацию POSIX close(), можно утверждать, что close() не влияет на время модификации файлов. Напротив, спецификация POSIX для write() действительно требует изменения времени модификации. Итак, по крайней мере теоретически, если вы пишете в файл, скажем, в 09:00:00 и оставляете файл открытым без дальнейших изменений до 15:00:00, время последнего обновления останется 09:00:00.
В ответ на отвечать от Søren-Holm
When a file is closed the modification time is changed.
это не кажется правильным:
import os
from time import sleep
TMPF = '/tmp/f'
def print_stats():
print("%s, %s" % (os.stat(TMPF).st_mtime, os.stat(TMPF).st_ctime))
sleep(1.1)
print("Opening...")
with open(TMPF, 'w') as f:
print_stats()
print("Writing...")
os.write(f.fileno(), 'apple')
print_stats()
print("Flushing...")
f.flush()
print_stats()
print("Closing...")
print_stats()
Производит:
Opening...
1483052647.08, 1483052647.08
Writing...
1483052648.18, 1483052648.18
Flushing...
1483052648.18, 1483052648.18
Closing...
1483052648.18, 1483052648.18
По общему признанию, здесь творится немного волшебства Python; что write() не имеет достаточных гарантий для автоматической очистки, но суть в том, что mtime обновляется при изменении файла, а не при его закрытии. Поведение ctime будет зависеть от вашей файловой системы и ее параметров монтирования.
Если посмотреть на спецификацию POSIX close(), можно утверждать, что close() не влияет на время модификации файлов. Напротив, спецификация POSIX для write() действительно требует изменения времени модификации. Итак, по крайней мере теоретически, если вы пишете в файл, скажем, в 09:00:00 и оставляете файл открытым без дальнейших изменений до 15:00:00, время последнего обновления останется 09:00:00.
Как насчет блокировки вместо обнаружения удаления?