假设一个长时间运行的进程写入一个日志文件。 假设日志文件无限期地保持打开状态。 假设一个粗心的系统pipe理员删除那个日志文件。 程序能检测到这种情况发生了吗?
假设fstat()
会报告已删除文件的链接计数为零是否安全?
截断,在我看来,有点棘手。 部分取决于文件描述符是否以O_APPEND
模式运行。 如果日志文件没有用O_APPEND
运行,那么程序日志描述符的当前写入位置不会改变,截断将删除前导字节,但是程序继续在“结束”处写入,留下幻影间隙零字节(它们读为零,但不一定占用磁盘空间)。
如果程序使用O_APPEND
运行,那么它将在当前存在的文件末尾写入。 观察截断的唯一方法是注意文件位置不在程序期望的位置 – 这又意味着明确地跟踪该位置。
总的来说,我并不担心截断和删除,但任何想法都会受到欢迎。
如果文件被硬连接或重命名,检查fstat()
返回链接计数为零将失败。 我可能会定期比较stat()
的inode号码和fstat()
的号码。
我不确定截断。
tail -F
检查删除和可能截断,所以我会检查它的来源,看看它是如何实现它。
假设这个粗心的系统管理员杀死了这个进程。 你真的想防止管理员做随机的事情吗? 我想你只是寻找一种方式来不时开始一个新的日志文件,如使用logrotate
。 在那里提供一种方法来手动让程序重新打开日志文件就足够了。 标准的方法是在程序中监听HUP-Signal,如果到达,重新打开日志文件:
#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
中的f_sighup
标志,看是否应该重新打开日志文件。 这对于logrotate
这样的工具来说很好,它可以重命名旧的日志文件,然后调用kill -s HUP $PID
。 粗心的系统管理员可以在删除(或者更好的重命名)旧的日志文件之后手动执行此操作。
你可以使用inotify来监视你的日志文件,监视文件系统事件。
回答søren-holm的回答
当文件关闭时,修改时间被改变。
这似乎不正确:
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
的行为将取决于您的文件系统及其挂载选项。
当文件关闭时,修改时间被改变。 所以定期检查使用stat()的mtime会工作。