前因
公司的ubuntu终端产品上线后出现串口打印机断断续续打印不规则字符的现象。维护人员认为是打印机的问题,厂家来了后当面测试确认不是产品问题。后来我接手处理,发现是因为触控驱动程序不间断的挨个打开串口并写入数据。在这个过程中接触了一些Linux下的文件监控工具。我们对文件的监控需求分类大致为:
- 特定文件正在被那些进程操作 (lsof)
- 特定进程正在操作那些文件 (lsof)
- 实时监控特定文件一段时间 (auditd)
- 特定的程序在执行过程中会操作那些文件 (strace)
Linux下“一切皆文件”,监控文件,就能掌控系统。
使用到的命令/工具
lsof
这个命令主要用于查询当前打开的文件,太复杂了,关于其的完整介绍请man lsof或者访问,这里只介绍几种常见用法。
- 特定用户打开的文件
root@zzz:/tmp# lsof -u root
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root cwd DIR 8,4 4096 2 /
systemd 1 root rtd DIR 8,4 4096 2 /
systemd 1 root txt REG 8,4 1538764 543000 /lib/systemd/systemd
systemd 1 root mem REG 8,4 18076 523252 /lib/i386-linux-gnu/libuuid.so.1.3.0
systemd 1 root mem REG 8,4 293412 523076 /lib/i386-linux-gnu/libblkid.so.1.1.0
systemd 1 root mem REG 8,4 13828 523102 /lib/i386-linux-gnu/libdl-2.23.so
systemd 1 root mem REG 8,4 472368 523199 /lib/i386-linux-gnu/libpcre.so.3.13.2
systemd 1 root mem REG 8,4 1786484 523084 /lib/i386-linux-gnu/libc-2.23.so
- 特定进程打开的文件
root@zzz:/tmp# lsof -p [pid of process]
- 特定文件被谁打开
root@zzz:/tmp# lsof -t [file path]
一些编辑器在编辑文件的时候,并不是保持原有文件的打开状态,而是载入内容后关闭文件,这种情况下lsof是不会列出该编辑该文件的编辑器的。
- 网络连接
这个命令会列出所有的网络连接
root@zzz:/tmp# lsof -i
特定协议、端口上运行的程序
root@zzz:/tmp# lsof -i TCP:1-23
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root 137u IPv6 19817 0t0 TCP *:ssh (LISTEN)
如果它能实时监控某个文件就完美了,虽然可以通过watch来不断调用它,但无论间隔多短,都不是实时监控,在使用auditd之前,我是通过多次运行lsof才逮住了往串口设备写数据的进程,有一定的偶然性(这个进程打开串口写入数据后会马上关闭串口)
auditd
Linux下的审计服务,需要安装配置,关于它的介绍可参考Linux man page,这里我们只介绍如何使用它来监控特定文件。首先要在监控规则中加入文件:
root@zzz/tmp:#auditctl -w /tmp/test -p warx -k [my_test]
其语法为:
auditctl -w path_to_file -p permissions -k key_name
r — read access to a file or a directory.
w — write access to a file or a directory.
x — execute access to a file or a directory.
a — change in the file’s or directory’s attribute
其中-k 参数为可选,产生一个易标识的日志入口关键字,方便查找。
可通过命令查询到该规则:
root@zzz:/tmp# auditctl -l
-w /tmp/test -p rwxa -k [my_test]
日志文件默认会记录在/var/log/audit/audit.log,其格式请参考格式说明。
准备测试:
root@zzz:/tmp# echo asd >> /tmp/test
查找相关数据:
root@zzz:/tmp# ausearch -i -k [my_test]
----
type=PROCTITLE msg=audit(2019年02月28日 11:35:37.328:4574) : proctitle=bash
type=PATH msg=audit(2019年02月28日 11:35:37.328:4574) : item=1 name=/tmp/test inode=521784 dev=08:04 mode=file,644 ouid=root ogid=root rdev=00:00 nametype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0
type=PATH msg=audit(2019年02月28日 11:35:37.328:4574) : item=0 name=/tmp/ inode=521218 dev=08:04 mode=dir,sticky,777 ouid=root ogid=root rdev=00:00 nametype=PARENT cap_fp=none cap_fi=none cap_fe=0 cap_fver=0
type=CWD msg=audit(2019年02月28日 11:35:37.328:4574) : cwd=/mnt/data/opensource
type=SYSCALL msg=audit(2019年02月28日 11:35:37.328:4574) : arch=i386 syscall=open success=yes exit=3 a0=0x9531ae8 a1=O_WRONLY|O_CREAT|O_TRUNC a2=0666 a3=0x9531ae8 items=2 ppid=9774 pid=9775 auid=unset uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts13 ses=unset comm=bash exe=/bin/bash key=[my_test]
使用ausearch命令搜索审计记录的时候,使用-i或者–interpret选项将自动将二进制转变为等价的可读信息。对比一下搜索输出信息,先执行命令:
root@zzz:/tmp# cat /tmp/test
直接使用ausearch -f /tmp/zf:
type=SYSCALL msg=audit(1551323460.883:1712): arch=40000003 syscall=5
success=yes exit=3 a0=bfa3f27a a1=8000 a2=0 a3=b7efc000 items=1 ppid=9288
pid=11204 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0
fsgid=0 tty=pts12 ses=4294967295 comm="cat" exe="/bin/cat" key="[test]
使用了-i参数后ausearch -i -f /tmp/zf:
type=SYSCALL msg=audit(2019年02月28日 11:11:00.883:1712) :
arch=i386 syscall=open success=yes exit=3 a0=0xbfa3f27a a1=O_RDONLY
a2=0x0 a3=0xb7efc000 items=1 ppid=9288 pid=11204 auid=unset uid=root
gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root
tty=pts12 ses=unset comm=cat exe=/bin/cat key=[test]
其中的关键信息:
syscall=open
a1=O_RDONLY
exe=/bin/cat
strace
这个命令的详细语法请参考General Commands Manual或者Linux man page。这里我们拿strace来跟踪程序执行过程中要访问那些文件,有点意外吧。
例如,我们要检查geany这个程序在执行中是否打开了/etc/ld.so.cache这个文件(这个命令默认会输出到标准错误,所以需要现将其重定向到标准输出):
root@zzz:/tmp# strace -f -e trace=open geany 2>&1 |grep /etc/ld.so.cache
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 6
[pid 18598] open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 10
[pid 18598] open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 10
[pid 18598] open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 10
[pid 18598] open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 10
[pid 18598] open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 10
[pid 18598] open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 10
[pid 18603] open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
[pid 18603] open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
[pid 18603] open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
[pid 18604] open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
其他参考工具
- inotify-tools 它好像只能告诉你文件被操作了,到底是谁不知道
- loggedfs 无法指定文件,只能接受路径参数