Linux使用inotify监控文件事件

本文通过编写C语言程序,利用inotify API实现对指定目录及其子目录的文件创建、删除和修改事件监控,利用链表维护受监控的子目录和文件。程序利用nftw遍历目录树,确保子目录变化时监控列表同步更新。

inotify允许应用程序监控文件事件。
inotify API有以下关键步骤:

  1. 应用程序使用inotify_init()来创建一inotify实例,返回的文件描述符用于在后续操作中指代该实例。
  2. 应用程序使用inotify_add_watch()向inotify实例(由步骤1创建)的监控列表添加条目,并指定监控的事件集合。
  3. 应用程序针对inotify文件描述符执行read()操作获得事件通知。每次read都会返回一个或多个inotify_event结构,其中各自记录了处于inotify实例监控之下的某一路径名所发生的事件。

知道了具体监控流程之后,我们来做道题,巩固学习的知识!

练习题

题目描述

编写一程序,针对其命令行参数所指定的目录,记录所有的文件创建、删除和改名操作。该程序所能够监控指定目录下所有子目录中的事件。获得所有子目录的列表需使用nftw()。当在目录树下添加或删除了子目录时,受监控的子目录集合应能保持同步更新。

解答

要想子目录添加或删除文件,能让监控子目录更新。即在子目录下添加文件,这个文件就要添加到监控列表中。这需要用一个数据结构来记录。这里我用的链表来记录监控的文件和对应监控描述符,看具体代码:

#define _XOPEN_SOURCE 600
#include<sys/inotify.h>
#include<stdio.h>
#include<limits.h>
#include<string.h>
#include<ftw.h>
#include<unistd.h>
#include<sys/stat.h>
#include<malloc.h>

#define BOOL short
#define FALSE 0
#define TRUE 1

/*链表结点包含监控文件与对应监控描述符,随着监控的更新来更新该链表*/
typedef struct WatchdiscriptorFile
{
    
    int wd;
    char pathname[256];
    BOOL head;
    struct WatchdiscriptorFile* next;
    struct WatchdiscriptorFile* pre;
}wdfnode;


/*插入结点操作,每次添加目录或文件,将其纳入监控中,并尾插到链表中*/
void PushNode(wdfnode* head,int wd,const char* pathname)
{
    if(head == NULL || head->head == FALSE)
    {
        printf("Push node failed !\n");
        return;
    }
    wdfnode* node =(wdfnode*)malloc(sizeof(wdfnode));
    node->head = FALSE;
    node->wd = wd;
    
    memcpy(node->pathname,pathname,strlen(pathname));


    node->pre = head->pre;
    node->next = head;
    head->pre->next = node;
    head->pre = node;

}
/*查找结点,根据监控描述符查找*/
wdfnode* FindNode(wdfnode* head,int wd)
{
    if(head == NULL)return NULL;
    wdfnode* node = head->next;
    while ((node->head != TRUE) && (node->wd != wd))
    {
        node = node->next;
    }
    return node->head == TRUE ? NULL : node;
}
/*文件或目录删除,则删除对应结点*/
void DelNode(wdfnode* head,wdfnode* node)
{
    if((head == NULL) || (node == NULL))return;
    if(head->next == node)
    {
        free(node);
        head->next = head;
        head->pre = head;
    }else{
        
        node->next->pre = node->pre;
        node->pre->next = node->next;
    }
}
static wdfnode* wdf = NULL;//链表头结点

static int inotifyFd;//监控文件描述符

/*显示受监控文件或目录的事件:打开、创建、删除、移动*/
static void displayInotifyEvent(struct inotify_event *i)
{
    printf(" wd = %2d; ",i->wd);
    if(i->cookie > 0)
    {
        printf("cookie =%4d;",i->cookie);
    }
    
    if (i->mask & IN_CREATE)printf("mask = IN_CREATE\n");
    if (i->mask & IN_DELETE)printf("mask = IN_DELETE\n");
    if (i->mask & IN_DELETE_SELF)printf("mask = IN_DELETE_SELF\n");
    
    if (i->mask & IN_MODIFY)printf("mask = IN_MODIFY\n");
    if (i->mask & IN_MOVE_SELF)printf("mask = IN_MOVE_SELF\n");
    if (i->mask & IN_MOVED_FROM)printf("mask = IN_MOVED_FROM\n");
    if (i->mask & IN_MOVED_TO)printf("mask = IN_MOVED_TO\n");
    if (i->mask & IN_OPEN)printf("mask = IN_OPEN\n");
    

    
    if(i->len > 0)printf("name = %s\n",i->name);
    
}

#define BUF_LEN (10*sizeof(struct inotify_event)+NAME_MAX+1)

struct stat file_stat;
uint32_t mask = IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN;

/*nftw()函数用到的回调函数,迭代访问所有文件和目录,并一一对其执行该函数*/
int func_oprate(const char* pathname,const struct stat* statbuf,int typeflag,struct FTW* ftwbuf)
{
    
    switch (statbuf->st_mode & S_IFMT)
    {
        int wd;
	    case (S_IFREG)://file or dir
	        
	        if((wd = inotify_add_watch(inotifyFd,pathname,mask)) != -1)
	        {
	            PushNode(wdf,wd,pathname);
	            printf("Watch %s using wd %d\n",pathname,wd);
	        }
	        break;
	    case (S_IFDIR):
	    
	        if((wd = inotify_add_watch(inotifyFd,pathname,mask)) != -1)
	        {
	            PushNode(wdf,wd,pathname);
	            printf("Watch %s using wd %d\n",pathname,wd);
	        }
	        break;
	    default:
	    printf("statbuf->st_mode is %d\n",statbuf->st_mode & S_IFMT);
	        break;
    }
    return 0;
}

int main(int argc, char const *argv[])
{
    int wd,j;
    char buf[BUF_LEN];
    ssize_t numRead;
    char *p;
    struct inotify_event *event;

    wdf = (wdfnode*)malloc(sizeof(wdfnode));
    wdf->next = wdf;
    wdf->pre = wdf;
    wdf->head = TRUE;

    if(argc < 2 || strcmp(argv[1],"--help") ==0)
    {
        printf("%s pathname... \n",argv[0]);
        return 0;
    }

    if(-1 == (inotifyFd = inotify_init()))
    {
        printf("error inotify_init");
        return 0;
    }
    printf("inotifyFd is %d\n",inotifyFd);
    for (j = 1; j < argc; j++)
    {
        int nopenfd = 10;
        int flags = 0;
        if(-1 == nftw(argv[j],func_oprate,nopenfd,flags))
        {
            printf("walk file return error\n");
            perror("walk file return error");
        }
    }
    printf("nftw finished \n");
    
    /*执行死循环,不断读取inotify_event,根据inotify_event来获知增删的文件,并更新链表*/
    for(;;)
    {
        
        numRead = read(inotifyFd,buf,BUF_LEN);
        
        if(numRead == 0)
        {
            printf("read() from inotify fd returned 0!");
        }
        if(numRead == -1)
        {
            printf("read() from inotify fd returned -1!");
        }
        printf("Read %ld bytes from inotify fd\n",(long)numRead);

        for(p = buf;p<buf+numRead;)
        {
            event = (struct inotify_event*)p;
            
            displayInotifyEvent(event);
            if (event->mask & IN_CREATE)
            {
                wdfnode* n = FindNode(wdf,event->wd);
                char filename[256];
                memset(filename,'\0',256);
                
                if(n == NULL){printf("not find Node !\n");}
                else{
                    memcpy(filename,n->pathname,strlen(n->pathname));
                    strcat(filename,"/");
                    strcat(filename,event->name);
                    }
                
                int wd = inotify_add_watch(inotifyFd,filename,mask);
                if(wd == -1)
                {
                    printf("not inotify_add_watch %s \n",event->name);
                }else{
                    PushNode(wdf,wd,filename);
                    printf("inotify_add_watch %s , using wd : %d\n",event->name,wd);
                }
            }
            if (event->mask & IN_DELETE_SELF)
            {
                wdfnode* n = FindNode(wdf,event->wd);
                printf("remove watch wd : %d , pathname is %s\n",n->wd,n->pathname);
                DelNode(wdf,n);
            }
            p += sizeof(struct inotify_event) + event->len;
        }
    }
    
    return 0;
}

结果

先创建一个测试目录,如下,testdir目录下有一个文件和六个目录,其中c目录下有一个目录c_t:
在这里插入图片描述
接着,执行监控程序,

在这里插入图片描述
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值