Linux 内核 2.6.13 以后,引入了 inotify 文件系统监控功能,通过 inotify 可以对敏感目录设置事件监听。这样的功能被也被包装成了一个文件监控神器 inotify-tools。
使用 inotify 进行文件监控的过程:
除了以上的核心过程,一个文件监控系统还需要包含:监控文件的获取、监控事件的解析和数据补充。
inotify 文件事件监控核心部分所涉及的 API 如下(包含在
read 每次通过文件描述符读取的 inotify 事件队列中一个事件,事件的 mask 标记了文件发生的事件。inotify 事件的数据结构如下:
- /* 创建 inotify 实例,获取文件描述符 fd */
- int inotify_init(void);//初始化一个新的 inotify 实例,返回一个与新的 inotify 事件队列关联的文件描述符
- int inotify_init1(int flags);//如果flags为0,功能与inotify_init()相同
-
- /* 添加 watch */
- int inotify_add_watch(int fd, const char *pathname, uint32_t mask); //对于在pathname 中指定位置的文件,添加一个新的 watch,或者修改一个现有的 watch
- int inotify_rm_watch(int fd, int wd);//从 inotify 中删除现有 watch 实例
-
- /* 读取文件事件 */
- ssize_t read(int fd, void *buf, size_t count);//尝试从inotify 事件队列关联的文件描述符fd读取多达count个字节到从buf开始的缓冲区中。成功时,返回读取的字节数(零表示文件结尾),文件位置按此数字前进。
-
- /* 关闭文件描述符 */
- int close(int fd);//关闭一个inotify 事件队列关联的文件描述符,使其不再引用任何文件
read 每次通过文件描述符读取的 inotify 事件队列中一个事件,事件的 mask 标记了文件发生的事件。inotify 事件的数据结构如下:
- struct inotify_event {
- int wd; /* 文件的监控描述符 */
- uint32_t mask; /* 文件事件的掩码 */
- uint32_t cookie; /* 重命名事件相关的唯一整数。对于所有其他事件类型,cookie 设置为 0 */
- uint32_t len; /* 文件名称的长度 */
- char name[]; /* 被监控的文件名称 */
- };
inotify 事件 mask 的宏定义:
- #define IN_ACCESS 0x00000001 /* File was accessed. */
- #define IN_MODIFY 0x00000002 /* File was modified. */
- #define IN_ATTRIB 0x00000004 /* Metadata changed. */
- #define IN_CLOSE_WRITE 0x00000008 /* Writtable file was closed. */
- #define IN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed. */
- #define IN_OPEN 0x00000020 /* File was opened. */
- #define IN_MOVED_FROM 0x00000040 /* File was moved from X. */
- #define IN_MOVED_TO 0x00000080 /* File was moved to Y. */
- #define IN_CREATE 0x00000100 /* Subfile was created. */
- #define IN_DELETE 0x00000200 /* Subfile was deleted. */
- #define IN_DELETE_SELF 0x00000400 /* Self was deleted. */
- #define IN_MOVE_SELF 0x00000800 /* Self was moved. */
inotify 没有实现对目录的递归监控,需要自己添加这部分的功能,因此要判断文件类型,对于常规文件和目录文件分别进行处理。
Linux 下的文件元信息,可以通过 stat() 读取,st_mode 字段记录了文件的类型,取值 S_IFDIR、S_IFREG 分别表示目录文件和常规文件。
- struct stat {
- dev_t st_dev; /* ID of device containing file */
- ino_t st_ino; /* Inode number */
- mode_t st_mode; /* File type and mode */
- nlink_t st_nlink; /* Number of hard links */
- /* 此处省略部分数据 */
- };
Linux 下 使用 readdir 打开目录获取目录信息,此函数返回一个 dirent 结构体,它的 d_type 字段记录了打开目录下的子文件的类型
- struct dirent {
- ino_t d_ino; /* Inode number */
- off_t d_off; /* Not an offset; see below */
- unsigned short d_reclen; /* Length of this record */
- unsigned char d_type; /* Type of file; not supported by all filesystem types */
- char d_name[256]; /* Null-terminated filename */
- };
d_type 字段取值如下:
- enum
- {
- DT_UNKNOWN = 0,
- # define DT_UNKNOWN DT_UNKNOWN
- DT_FIFO = 1,
- # define DT_FIFO DT_FIFO
- DT_CHR = 2,
- # define DT_CHR DT_CHR
- DT_DIR = 4,
- # define DT_DIR DT_DIR //目录文件
- DT_BLK = 6,
- # define DT_BLK DT_BLK
- DT_REG = 8,
- # define DT_REG DT_REG //常规文件
- DT_LNK = 10,
- # define DT_LNK DT_LNK
- DT_SOCK = 12,
- # define DT_SOCK DT_SOCK
- DT_WHT = 14
- # define DT_WHT DT_WHT
- };
文件监控 demo:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- using std::string;
-
- string event_str[12] =
- {
- "IN_ACCESS", //文件被访问
- "IN_MODIFY", //文件修改
- "IN_ATTRIB", //文件元数据修改
- "IN_CLOSE_WRITE",
- "IN_CLOSE_NOWRITE",
- "IN_OPEN",
- "IN_MOVED_FROM", //文件移动from
- "IN_MOVED_TO", //文件移动to
- "IN_CREATE", //文件创建
- "IN_DELETE", //文件删除
- "IN_DELETE_SELF",
- "IN_MOVE_SELF"
- };
-
- class FileMonitor
- {
- public:
- void start_watch(int size, char *file_list[]);
- int watch_dir(const char *dir_path);
- FileMonitor();
- ~FileMonitor();
- private:
- int fd;
- };
-
- FileMonitor::FileMonitor()
- {
- fd = inotify_init1(IN_NONBLOCK);//创建inotify实例,返回与该实例相关的文件描述符 fd
- if (fd == -1) {
- std::cerr<<"Error: inotifiy initial failed !"<
- exit(EXIT_FAILURE);
- }
- }
-
- FileMonitor::~FileMonitor()
- {
- if (fd > 0)
- close(fd);
- }
-
- void FileMonitor::start_watch(int size, char *file_list[])
- {
- struct stat file_info;
- int wd, file_type, event_list_len;
- struct inotify_event *event;
- char buf[8192] __attribute__ ((aligned(__alignof__(struct inotify_event))));
-
- for (int i=1; i < size; i++)
- {
- stat(file_list[i], &file_info);
- if (file_info.st_mode & S_IFREG)//普通文件直接添加 watch
- {
- wd = inotify_add_watch(fd, file_list[i], IN_ALL_EVENTS);
- if (wd == -1)
- {
- std::cerr<<"Error: cannot watch "<
" !"< - exit(EXIT_FAILURE);
- }
- }
- if (file_info.st_mode & S_IFDIR) //目录文件需要遍历,为目录中的所有文件添加 watch
- watch_dir(file_list[i]);
- }
-
- //std::cout<<"start listening for events"<
- int event_len = sizeof(struct inotify_event);
-
- //读取inotify事件队列中的事件
- while (1)
- {
- if ((event_list_len = read(fd, buf, sizeof(buf))) > 0)
- for (char *ptr = buf; ptr < buf+event_list_len; ptr += event_len + event->len){
- event = (struct inotify_event *)ptr;
- //解析文件事件
- for (int i = 1; i < 12; i++){
- if ((event->mask >> i) & 1){
- std::cout<
name<<": "<-1]< - /*
- * event->name获取的是相对路径,获取绝对路径需要额外进行路径存储
- *
- * 这里可以针对敏感文件设置告警
- */
- }
- }
- }
- }
- }
-
- int FileMonitor::watch_dir(const char *dir_path)
- {
-
- DIR *dir;//打开的目录
- struct dirent *dir_container;//打开目录内容
- int wd, path_len, count = 0;
- string path = dir_path, path_str, prnt_path[1000];//当前目录、临时变量、子目录数组
-
- if ((dir = opendir(dir_path)) == NULL)
- {
- std::cerr<<"Error: cannot open directory '"<
"' !"< - return -1;
- }
-
- wd = inotify_add_watch(fd, dir_path, IN_ALL_EVENTS);//为目录添加监控
- if (wd == -1)
- {
- std::cerr<<"Error: cannot watch '"<
"' !"< - return -1;
- }
-
- while((dir_container = readdir(dir)) != NULL)
- {
- path_len = path.length();
- path_str = path[path_len-1];
- if (path_str.compare( "/") != 0)
- path += "/";
- path_str = path + (string)dir_container->d_name;//子文件绝对路径
- //std::cout<<"path: "<
- if (dir_container->d_type == DT_REG)
- {
- inotify_add_watch(fd, (char *)path_str.c_str(), IN_ALL_EVENTS);//常规文件直接添加监控
- continue;
- }
- if (dir_container->d_type == DT_DIR
- && strcmp(".", dir_container->d_name) != 0
- && strcmp(dir_container->d_name,"..") != 0)
- {//目录文件加入子目录数组,等待递归遍历
- prnt_path[count] = path_str;
- count++;
- }
- }
- closedir(dir);
- while (count > 0){
- count--;
- watch_dir(prnt_path[count].c_str());//递归遍历目录,添加监控
- }
- return 0;
- }
-
- int main(int argc, char* argv[])
- {
- if (argc < 2){
- std::cerr<<"Error: no watching file!"<
- exit(1);
- }
- FileMonitor monitor;
- monitor.start_watch(argc, argv);
- return 0;
- }
编译执行:
c++ -o test -std=c++11 test.cpp && ./test /var/log


参考:
inotify(7) - Linux manual page
-
相关阅读:
防水运动蓝牙耳机,分体式蓝牙耳机品牌推荐
CI/CD简介
一致性 hash 环
MongoDB副本集集群搭建
STM32 Cube MX以及STM32 H750 XBH6新建工程,HAL库,LL库
计算机毕业论文选题java毕业设计软件源代码strust2+mybatis客户关系系统[包运行成功]
React组件 - 实现侧滑删除
各种ui框架的 form校验 validator获取不到value
EATON XV-440-10TVB-1-13-1工业显示屏模块
DTC商业模式研报 | 创新DTC策略利于提升业务灵活性和数字化体验
-
原文地址:https://blog.csdn.net/SHELLCODE_8BIT/article/details/134479361