稀松平常的一天,小W在机器上部署好最新版本的代码就下班回家了。
第二天一上班,发现服务有异常,而主机上的一些关键文件莫名消失。查看日志没有发现其他人登录过。Nani?
怀疑是自己手残的小W,战战兢兢重新部署了一下。
结果第三天重蹈覆辙,文件又如约消失了!
难道是传说中的密室删除事件?这下小W慌了……
赶紧求助号称linux百事通的小X。
只见小X不慌不忙,打开编辑器输入了一些脚本,并且叮嘱他晚上下班的时候记得运行,明天早上过来看结果。
小W按照提示,重新部署了代码,然后在终端开启了一个tmux,启动了下面的命令:
bpftrace unlink.bpftrace
小W略有不安的睡了一觉,早上一看,文件果然又没有了。赶紧打开tmux看看有没有抓到信息,果然发现了一些蛛丝马迹:
Attaching 2 probes...
my-precious-file.txt deleted by command 'rm' with pid 3675070
... whose parent is 'sh' command with pid 3675069
... whose parent is 'cron' command with pid 3675068
... whose parent is 'cron' command with pid 1360
... whose parent is 'systemd' command with pid 1
啊哈!日志显示的很清晰,顺藤摸瓜,找到了crontab里一个在夜里2点定时清理临时文件的配置,但是命令行里有个bug,匹配到了不该删除的文件。
这下对小W来说就简单了,修正cron命令行以后,简单测试了一下——这次自己的文件就不会被删除了!
小W最后再上线一次代码,开心的等下班拉小X一起吃串去!
哦差点忘了,脚本在这里:
#include <linux/sched.h>
#include <linux/dcache.h>
tracepoint:syscalls:sys_enter_unlink,tracepoint:syscalls:sys_enter_unlinkat {
printf("%s deleted by command '%s' with pid %dn",
str(args->pathname), comm, pid);
$p = curtask->real_parent;
$i = 0;
while ($i < 100) {
$i++;
printf(" ... whose parent is '%s' command with pid %dn",
$p->comm,
$p->tgid);
$p = $p->parent;
if ($p == 0 || $p->tgid == 0) {
break;
}
}
}
Linux下文件的删除一般通过2个系统调用:unlink和unlinkat。内核的ftrace在系统调用出入口都有tracepoint,能够暴露传入的参数。
bpftrace程序可以在这些点上挂载一个BPF程序,采集和输出当时的状态。
所以我们在这两个系统调用入口处,打印被删除的文件名和父进程的信息,就不难找到删除操作的来源了。
那么,你已经知道怎样写一个抓取删除文件夹(rmdir)的脚本了吗?