文件监控系统设计(3)-"Linux"

今天简单讨论下Linux下文件系统的监控方法,我测试过的有如下三种方法,分布针对不同的kernel 版本:


1 , 使用inotify API (如果你的Linux kernel >= 2.6.13 ,恭喜你了, 可以直接调用这个新的API )

2 , 使用inotify device ( 2.4.27<= kernel <= 2.6.13 , 这个方法本质上和调用inotify API 是一样的,但是麻烦些, 要给kernel打patch )

3 , 使用文件轮询方式 ( 这个不适用特别的API, 适用所有kernel版本 )


方法1 : inotify API 的使用

这个方法我实在不想在这里多说了, 网上一搜一堆得资料 。 这个方法应该是目前监控Linux 文件系统变化最佳的途径, 毕竟像文件监控这种任务能得到操作系统提供的API的支持是最好不过的了,coding 很容易, 也节省系统资源,不需要像方法3 轮询那样时刻或者定期的检查文件。

man page: http://linux.die.net/man/7/inotify


方法2 : inotify device 的使用

inotfiy API是在2.6.13 的版本之后加入kernel的, 一些国外编写linux kernel的牛人制作的一个backported的patch, 使得部分早于2.6.13 的linux系统也可以使用类似于调用inotify API方法进行文件监控。

以下两篇文章不错:

http://www.developertutorials.com/tutorials/linux/monitor-linux-inotify-050531-1133/

http://lwn.net/Articles/142751/

看完上面两篇文章之后就可以对大概如何使用inotify device 的方式监控文件有个大致的了解了,如果你觉得还想继续深究,下面这个链接提供了一些现成的源码

http://www.kernel.org/pub/linux/kernel/people/rml/inotify/utils/

其中 inotify-utils-0.21.tar.gz  值得一看, 拿来修改后可以直接使用。


方法3 :文件轮询的方法

这几天我在网上搜索了很多linux下文件监控的方法,绝大部分是推荐使用inotify API的文章,另外也有提及使用signal的方法,尚未找到使用文件轮询的方式进行文件监控的文章,这里我会较为详细的介绍下这个方法。(此方法的最大优点是没有使用任何特别的API, 适用于绝大多数Linux版本)


一 设计思路

      通过文件对比的方法检测文件的Add,Delete, Modify


二 具体步骤

2.1 文件夹拷贝: 先把需要监控的文件夹完全复制一份,最为文件对比时的参考文件夹

2.2 文件"反向"检测:

使用readdir() 依次读取参考文件夹中的每个文件: 如果读取到的时文件夹,则递归调用此函数;

如果读取到的是文件,首先检测监控文件夹中对应的文件是否存在,如果不存在了,则认为此文件在监控文件夹中被删除,触发一个Delete 的事件,如果文件存在,继续检测 last write time 是否一致,如果last write time变化了, 说明此文件被修改;

所谓"反向" 的意思是说通过依次打开参考文件夹中的所有文件,来检测监控文件夹中对应的文件是否还存在或者被修改,显然,使用"反向”检测的方式只能检测文件的Delete 和 Modify,不能检测文件的Add。检测文件的Add 要通过"正向"检测的方法。

2.2 文件"反向"检测: 使用readdir() 依次读取监控文件夹中的每个文件:如果某个文件在对应的参考文件夹中不存在,则说明此文件是新添加的。


三 注意事项

3.1 开始监控前一定要先copy 监控文件夹作为对比用的参考文件夹

3.2 不能拿参考文件夹中文件的last write time 直接和监控文件的last write time 直接对比,因为参考文件夹中的last write time 是文件夹初始被拷贝创建的时间。所有要通过某种方式在监控开始前把监控文件夹中所有文件的last write time 提前记录下来,供监控对比时使用。我使用map来存储监控文件夹中所有文件的last write time。

3.3 注意递归的使用,要遍历文件夹中所有子文件夹的内容,一定要使用递归。

3.4 参考文件夹要和监控文件夹保持实时同步,比如在某轮监控中检测到一个新添加的文件,则同时要把此文件copy到参考文件夹,或者文件被修改,则要update  map中对应的last write time。 避免下一轮检测中报告同样的事件。


四 code 

代码为3个源文件, scm.h scm_main.cpp scm.cpp 。

编译执行后可以实现对某个文件夹内文件Add, Delete, Modify事件的监控 。

注意:程序执行前先copy要监控的文件夹。 把监控文件夹和参考文件夹的path作为可执行程序的参数传入。另外还需要建立一个backup的空文件夹,程序运行时会吧删除的或者被修改的original file 存放在backup的文件夹内


头文件 scm.h

#ifndef _SCM_
#define _SCM_
#include <string>
#include <map>
#define BACKUP_DIR "/home/joshua/backup"
using namespace std;

extern char dir_mon[255];
extern char dir_bak[255];
extern map<string, time_t> modify_time;

void mon_routine ();
int dir_lookup (char *, char *, int);
void file_lookup (char *, char *, char *, int);
int mtime_init (char *);
void sys_time ();
void sys_hostname ();
#endif


主文件 scm_main.cpp

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <string>
#include <dirent.h>
#include <unistd.h>
#include <map>
#include "scm.h"


using namespace std;

char dir_mon[255]={0}; // directory to monitor
char dir_bak[255]={0};  //  directory as reference

map<string, time_t> modify_time;

int main (int argc, char **argv)
{
    // pass arguments as directory path
    memcpy(dir_mon, argv[1], strlen(argv[1]));
    memcpy(dir_bak, argv[2], strlen(argv[2]));
      
    // initialize modify_time
    if (mtime_init(dir_mon) == -1)
    {
        printf("mtime_init fail");
        return -1;
    }
    
    printf(" Monitoring begin !\n");

    while (1)
    {
        // monitor routine every 5 sec
        mon_routine ();
        sleep (5);
    }
    
    return 0;
}


函数 scm.cpp

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/ioctl.h>
#include <string>
#include <errno.h>
#include <map>
#include "scm.h"


using namespace std;




void mon_routine ()
{
// lookup from backup_dir to monitor_dir, detect file delete or modify, flag=0
    dir_lookup (dir_mon, dir_bak, 0);
// lookup from monitor_dir to backup_dir, detect file add, flag =1
    dir_lookup (dir_bak, dir_mon, 1);
    return ;
}

int dir_lookup (char *dir, char *dir_open, int flag)
{
    DIR *odir;
    struct dirent* dent;
    int len;
    

// Open  directory
    if ((odir = opendir(dir_open)) == NULL)
    {
        printf ("Dir %s ", dir_open);
        perror ("Couldn't be open:");
        return -1;
    }

// Lookup files in this directory one-by-one
    while ((dent = readdir(odir)) != NULL)
    {
    // special file "." ".."
        if ( strcmp(dent->d_name, ".") == 0 ||
             strcmp(dent->d_name, "..") == 0 )
        {
            continue;
        }
        len = strlen(dent->d_name);
    // regular file need to be checked
        if ( dent->d_type == DT_REG &&
             dent->d_name[0] != '.' &&        // get rid of hiden file
             !(dent->d_name[len-1] == 'p' &&  // get rid of temporary file
               dent->d_name[len-2] == 'm' &&
               dent->d_name[len-3] == 't' &&
               dent->d_name[len-4] == '.')&&
             dent->d_name[len-1] != '~'         // get rid of temporary file
           )
        {
            char pathname_o[255]={0};
            char pathname[255]={0};
            sprintf(pathname_o, "%s/%s", dir_open, dent->d_name);
            sprintf(pathname, "%s/%s", dir, dent->d_name);
            // check out if this file is deleted , modified or added , controlled by flag bit
            file_lookup (pathname, pathname_o, dent->d_name, flag);
        }
            
        
    // check out directory recursively
        if (dent->d_type == DT_DIR )
        {
            char subdir_open[255]={0};
            char subdir[255]={0};
            sprintf(subdir_open, "%s/%s", dir_open, dent->d_name);
            sprintf(subdir, "%s/%s", dir, dent->d_name);

            // check out if this dir exist, if not , create it
            struct stat statbuf_m;
            if (stat(subdir, &statbuf_m) == -1)
            {
                // dir does not exist, which means a dir is created
                   char cmd[255]={0};
                sprintf(cmd, "mkdir %s", subdir);
                if(system(cmd) == -1)
                {
                    perror("CMD Fail:");
                }
            }

            // directory lookup recursively, controlled by flag bit
            dir_lookup (subdir, subdir_open, flag);
        }
    }
    closedir(odir);
    return 0;
}

void file_lookup (char *file, char *file_o, char *file_name, int flag)
{
    struct stat statbuf_m;
    
    // file doesn not exist anymore
    if (stat(file, &statbuf_m) == -1 && flag == 0)
    {
        sys_time ();
        sys_hostname ();
        printf("%s was deleted!\n", file);
        
        
        // remove deleted file to backup dir
        char newpath[255]={0};
        sprintf(newpath, "%s/%s", BACKUP_DIR, file_name);
        if(rename(file_o, newpath) == -1)
        {
            perror("file backup fail:");
        }
        
        // delete the filepath in map
        modify_time.erase (file);

        return ;
    }
    
    // file was MODIFIED
    if (statbuf_m.st_mtime != modify_time[file] && flag == 0)
    {
        sys_time ();
        sys_hostname ();
        printf("%s was modified!\n", file);

        // move original file to backup dir
        char newpath[255]={0};
        sprintf(newpath, "%s/%s", BACKUP_DIR, file_name);
        if(rename(file_o, newpath) == -1)
        {
            perror("file backup fail:");
        }

        // update modify_time map
        modify_time[file] = statbuf_m.st_mtime;

        // copy modified file to sync dir
        char cmd[255]={0};
        sprintf(cmd, "cp \"%s\" \"%s\"", file, file_o);
        if (system(cmd) == -1)
        {
            perror(" Cmd fail\n");
        }

    }

    // a new file was added, this is implemented by directory check backwardly, controlled by flag bit
    if (stat(file, &statbuf_m) == -1 && flag == 1)
    {
        sys_time ();
        sys_hostname ();
        printf("%s was added!\n", file_o);

        // copy new file to sync_dir
        char cmd[255]={0};
        sprintf(cmd, "cp \"%s\" \"%s\"", file_o, file);
        if (system(cmd) == -1)
        {
            perror("CMD fail:\n");
        }
        
        // update new file mtime to map
        stat (file_o, &statbuf_m);
        modify_time[file_o] = statbuf_m.st_mtime;
    }

    
}






int mtime_init (char *dir_m)
{
    DIR *odir;
    struct dirent* dent;
    
    

// Open this backup directory
    if ((odir = opendir(dir_m)) == NULL)
    {
        printf ("Dir %s ", dir_m);
        perror ("Couldn't be open:");
        return -1;
    }

// Lookup files in this directory one-by-one
    while ((dent = readdir(odir)) != NULL)
    {
    // special file "." ".."
        if ( strcmp(dent->d_name, ".") == 0 ||
             strcmp(dent->d_name, "..") == 0 )
        {
            continue;
        }
    // record regular file last_modify_time
        if ( dent->d_type == DT_REG )
        {
            struct stat statbuf;
            char pathname_m[255]={0};
            sprintf(pathname_m, "%s/%s", dir_m, dent->d_name);
            stat (pathname_m, &statbuf);
            modify_time[pathname_m]=statbuf.st_mtime;
        }
            
        
    // check out directory recursively
        if (dent->d_type == DT_DIR )
        {
            char subdir_m[255]={0};
            sprintf(subdir_m, "%s/%s", dir_m, dent->d_name);
            if (mtime_init (subdir_m) == -1)
            {
                return -1;
            }
        }
    }
    closedir(odir);
    return 0;
}

    
void sys_time ()
{
    time_t time_now;
    struct tm *time_local;
    char tm[30]={0};
    
    time(&time_now);
    time_local = localtime (&time_now);
    sprintf(tm, "%d/%d/%d %d:%d:%d", time_local->tm_mon+1,
                     time_local->tm_mday,
                     time_local->tm_year+1900,
                     time_local->tm_hour,
                     time_local->tm_min,
                     time_local->tm_sec);
    printf ("%s", tm);
}

void sys_hostname ()
{
    char hostname[50]={0};
    gethostname (hostname, 50);
    printf (" %s ",hostname);
}








  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值