2009-01-20 75 views
4

假设长时间运行的进程写入日志文件。假设日志文件无限期地保持打开状态。假设一个粗心的系统管理员删除该日志文件。程序是否可以检测到发生了这种情况?在POSIX系统上检测到该日志文件已被删除或截断?

是否安全地假设fstat()将报告已删除文件的链接计数为零?

截断,在我看来,有点棘手。部分取决于文件描述符是否在O_APPEND模式下运行。如果日志文件未与O_APPEND一起运行,那么程序日志描述符的当前写入位置不会改变,并且截断将删除前导字节,但程序将继续在“结束”处写入,从而留下幻影间隙零字节(它们读为零,但不一定占用磁盘空间)。

如果程序以O_APPEND运行,那么它将在文件末尾写入当前存在的文件末尾。观察截断的唯一方法是注意文件位置不在程序期望的位置 - 这又意味着明确跟踪该位置。

整体而言,我并不担心截断与删除一样,但任何想法都会受到欢迎。

+0

如何锁定而不是检测删除? – 2009-01-20 17:02:08

+0

@snot:我不明白这会有多大帮助。咨询锁定将被忽略;强制锁定是不寻常的 - 不是不可能的,但我不想。另外,应允许管理员在合理范围内操作日志。这可能是软件需要更好的“更改日志文件”机制。 – 2009-01-20 17:59:56

+0

文件名只是指向文件的指针,当没有指向文件时它将被删除。既然你总是可以添加一个链接然后删除另一个链接计数可能没有帮助,你可以做的最好的办法是检查文件名是否仍然存在,或者如果你的过程是唯一一个链接到文件。 – 2009-01-20 18:28:07

回答

3

检查fstat()返回链接计数为零将失败,如果文件被硬链接或重命名。我可能会定期比较stat()的inode编号与fstat()的编号。

我不确定截断。

tail -F检查删除和可能截断,所以我会检查它的来源,看看它是如何实现它。

1

你可以使用inotify来观察你的日志文件,监视它的文件系统事件。

2

假设不小心的系统管理员杀死进程。你真的想防止管理员做随机的事情吗?我想你只是在寻找一种不时启动新日志文件的方式,例如使用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。粗心的系统管理员可以在删除(或更好地重命名)旧的日志文件之后手动执行此操作。

-1

当一个文件关闭时,修改时间被改变。因此,使用stat()定期检查mtime是否可行。

1

响应于søren-holmanswer

当文件被关闭的修改时间被改变。

不似乎是正确的:

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的行为将取决于您的文件系统及其挂载选项。