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

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

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

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

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

    在我看来,截断稍微复杂一些。在某种程度上,它取决于文件描述符是否在 O_APPEND 模式。如果日志文件不与一起运行 O-追加 ,则程序日志描述符的当前写入位置不变,截断会删除前导字节,但程序会继续在“结尾”写入,留下幻象零字节的间隙(它们读取为零,但不一定占用磁盘空间)。

    如果程序运行时 O-追加 ,然后它将在文件末尾写入当前存在的内容。观察截断的唯一方法是注意文件位置不是程序预期的位置——这反过来意味着显式地跟踪该位置。

    总的来说,我不太担心删减,但任何想法都是受欢迎的。

    5 回复  |  直到 8 年前
        1
  •  4
  •   Josh Kelley    16 年前

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

    我不确定是否要截断。

    tail -F 检查是否删除或者是否截断,所以我将检查它的源代码以了解它是如何实现的。

        2
  •  2
  •   sth    14 年前

    假设粗心的系统管理员杀死了进程。你真的想保护管理员不做随机的事情吗?我猜你只是在寻找一种方法来不时地开始一个新的日志文件,比如使用 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    16 年前

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

        4
  •  1
  •   Community CDub    7 年前

    响应 søren-holm 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
    

    不可否认,里面有一点巨蟒的魔力;那 write() 不能合理保证自动刷新,但关键是修改文件时更新mtime,而不是关闭文件时更新mtime。行为 ctime 将取决于您的文件系统及其装载选项。

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

    关闭文件后,修改时间将更改。因此,使用stat()定期检查mtime将起作用。