阻止程序写入已删除的文件

阻止程序写入已删除的文件

我们的一个系统有一个不断增长的日志文件(我们将解决),但目前应用程序所有者将删除该文件,然后rm等待下一个维护窗口重新启动。我发现距离下一个维护时段还有几周时间,而且磁盘利用率为 100%。

遵循以下指导这个帖子我找到了该文件并将其截断。现在的问题是程序/进程似乎没有在任何地方写入日志。让此进程停止使用旧文件并开始使用“新文件”的最佳方法是什么?

# find /proc/*/fd -ls | grep  '(deleted)'|grep path
112567191    0 l-wx------   1 user1 group1       64 Feb 20 14:10 /proc/27312/fd/2 -> /path/file.log\ (deleted)

# > "/proc/27312/fd/2"

# find /proc/*/fd -ls | grep  '(deleted)'|grep path
112567191    0 l-wx------   1 user1 group1        64 Feb 20 14:10 /proc/27312/fd/2 -> /path/file.log\ (deleted)

 # stat /path/file.log
   File: ‘/path/file.log’
   Size: 0               Blocks: 0          IO Block: 4096   regular empty file
 Device: 811h/2065d      Inode: 2890717     Links: 1
 Access: (0644/-rw-r--r--)  Uid: (54322/loc_psoft)   Gid: (54321/oinstall)
 Context: unconfined_u:object_r:unlabeled_t:s0
 Access: 2019-02-20 12:44:42.738686325 -0500
 Modify: 2019-02-08 11:38:19.741494973 -0500
 Change: 2019-02-08 11:38:19.741494973 -0500
  Birth: -

# stat /proc/27312/fd/2
  File: ‘/proc/27312/fd/2’ -> ‘/path/file.log (deleted)’
  Size: 64              Blocks: 0          IO Block: 1024   symbolic link
Device: 3h/3d   Inode: 112567191   Links: 1
Access: (0300/l-wx------)  Uid: (54322/loc_psoft)   Gid: (54321/oinstall)
Context: unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
Access: 2019-02-20 14:10:45.155518866 -0500
Modify: 2019-02-20 14:10:45.154518886 -0500
Change: 2019-02-20 14:10:45.154518886 -0500
 Birth: -

目前我没有磁盘空间问题,我只有日志未写入的问题。

更新1: 可以使用 找到 PID lsof +L1|grep $path,并且它也位于“hold”文件路径中proc/$PID/fd/N。我还无法向决策者推销中断,无论是作为 ainit 6还是kill 1 $PID。我将尝试在其他地方重现这个问题,并在这里给出一些我已经挖掘出来的建议。

答案1

有问题的程序将必须被更改,或者简单地重新启动。

似乎正在发生的情况是,程序正在打开一个文件句柄以写入日志,并在此期间保持该同一个文件句柄打开。如果文件被删除,正如您所描述的,它会被“暂停”,并且实际上仍然会被写入,直到文件句柄关闭。

如果您可以更改程序以将其更改为(伪代码):

LogFileHandle = OpenFileHandle( Logfile, 'wa' )
UpdateLog( log_entry ) {
    LogFileHandle.Write( log_entry )
}
do_literally_everything_forever()
LogFileHandle.Close()

到(伪代码):

UpdateLog( log_entry ) {
    LogFileHandle = OpenFileHandle( Logfile, 'wa' )
    LogFileHandle.Write( log_entry )
    LogFileHandle.Close()
}
do_literally_everything_forever()

这将解决问题。

如果不能,那么不需要重新启动整个系统,rm一旦所有打开文件句柄的进程都已关闭(或者更具体地说,它们的文件句柄已关闭),已编辑的文件将真正消失。

如果发送 SIGHUP,大多数编写良好的守护进程都会偶然循环其文件句柄(请阅读程序的文档!)。但简单地停止(或终止)并重新启动程序也将释放所有打开的文件句柄。

答案2

您可以尝试使用调试器附加到该进程,并强制将其文件描述符 2 重定向到其他地方:

gdb -batch -p PID -ex 'p $f=open("/path/to/log", 01101, 0666), dup2($f, 2), close($f)'

替换PIDpid您的进程的,以及"/path/to/log"应重定向 fd 2 (stderr) 的文件。01101O_WRONLY|O_CREAT|O_TRUNC0666如果进程的 umask 不正确,您可以将权限更改为更具限制性的内容。该进程可能正在缓冲,并且其输出可能不会立即出现在 stderr 重定向的文件中。

这是一个黑客行为。青年MMV

答案3

根本问题似乎是文件的索引节点在删除后与写入日志的软件使用的相同。恢复文件很容易,但这会生成一个带有新索引节点的新文件,并且该进程将继续写入原始文件。我还没有找到一种方法来交换索引节点,以便日志记录程序关闭已删除的文件并开始使用新文件。这就是为什么需要终止进程或重新启动。

作为临时解决方案,将当前上下文复制/proc/$PID/fd/#到原始日志位置似乎是最佳解决方案。在尝试使用@mosvy提出的解决方案后,我找到了另一种方法

# nohup tail -c +0 -f /proc/$PID/fd/# > /path/file.log &

两个经常出现的参考文献之一来自Linux.com其中涵盖了发生的情况以及如何恢复静态文件。本文中引用了第二个超级用户邮政。

答案4

是否可以重新启动应用程序服务?如果是,您是否尝试重新启动它。这应该释放旧的 PID 并创建一个新的 PID。

这还应该重置应用程序服务,并且我相信它应该将日志写入上述文件。

相关内容