代码之家  ›  专栏  ›  技术社区  ›  Jonathan Leffler

检测到日志文件在POSIX系统上已被删除或截断?

  •  4
  • Jonathan Leffler  · 技术社区  · 17 年前

    假设一个长时间运行的进程写入日志文件。假设日志文件无限期保持打开状态。假设一个粗心的系统管理员删除了该日志文件。程序能检测到这种情况吗?

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

    在我看来,截断有点棘手。在某种程度上,这取决于文件描述符是否在 O_APPEND 模式。如果日志文件未与一起运行 O_APPEND ,则程序日志描述符的当前写入位置不会改变,截断会删除前导字节,但程序会继续在“末尾”写入,留下虚零字节的间隙(它们读取为零,但不一定占用磁盘空间)。

    如果程序运行时 O_APPEND ,则它将在当前存在的文件末尾写入。观察截断的唯一方法是注意文件位置不是程序预期的位置,这反过来意味着要明确地跟踪该位置。

    总的来说,我更担心的是删除,而不是截断,但欢迎任何想法。

    5 回复  |  直到 12 年前
        1
  •  4
  •   Josh Kelley    17 年前

    检查一下 fstat() 如果文件被硬链接或重命名,则返回零链接计数将失败。我可能会定期进行比较 stat() 的索引节点号 fstat() s

    我不确定截断。

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

        2
  •  2
  •   sth    16 年前

    假设粗心的系统管理员终止了该进程。你真的想防止管理员做随机的事情吗?我想你只是在寻找一种方法来不时地启动一个新的日志文件,比如使用 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 在主程序中标记,以查看是否应重新打开日志文件。 这与以下工具配合得很好 轮替 ,它可以重命名旧的日志文件,然后调用 kill -s HUP $PID 。粗心的系统管理员可以在删除(或更好地重命名)旧日志文件后手动执行此操作。

        3
  •  1
  •   Jeff Bauer    17 年前

    你可以使用 inotify 监视日志文件,监视文件系统事件。

        4
  •  1
  •   Community Mohan Dere    9 年前

    作为回应 søren-holm s answer

    当文件关闭时,修改时间会更改。

    这似乎不正确:

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

        5
  •  -1
  •   Søren Holm    10 年前

    当文件关闭时,修改时间会更改。因此,使用stat()定期检查mtime是可行的。