inotify監控linux目錄變化


工作中我們可能會遇到一些問題,比如系統部署過程中配置文件在多個主機之間的同步問題,或是和其他系統對接的時候,需要以其他系統輸出的文件作為輸入的時候,這時需要我們實時的監控文件目錄的變化,用以做出響應。通常我們可能的選擇是實時的監測目錄信息,不斷去獲取目錄信息來判斷文件目錄是否有變化。但在linux系統下,系統內核提供了一個機制Inotify,用以通知文件目錄的變化。

Inotify 是一個 Linux特性,它監控文件系統操作,比如讀取、寫入和創建。Inotify 反應靈敏,用法非常簡單,並且比 cron 任務的繁忙輪詢高效得多。詳細說明大家可以查詢相關資料。

使用Inotify的基本步驟

一、初始化,並指定監控目錄

int fd=inotify_init();//初始化
int wd=inotify_add_watch(fd,path.c_str(),IN_MODIFY|IN_CREATE|IN_DELETE);//添加監視

 

下面分享我在工作實現的一個簡單監控目錄小程序:

程序主要包括三個文件:filemonitor.h,filemonitor.cpp和main.cpp

 1 #ifndef FILEMONITOR_H
 2 #define FILEMONITOR_H
 3 #include<iostream>
 4 #include<map>
 5 #include<vector>
 6 using namespace std;
 7 enum EventType{
 8     CREATE,
 9     DELETE,
10     MODIFY
11 };
12 
13 typedef struct{
14     string path;//路徑
15     EventType type;//事件類型
16     int fileType;//0表示文件夾,1表示文件
17 } Event;
18 typedef struct{
19     string path;//文件路徑
20     int expire;//過期時間
21 } FileInfo;
22 class FileMonitor
23 {
24 private:
25     string path;//監控根目錄
26     int fd;//句柄
27     map<int,string> monitorMap;//監控列表
28     long expire;//文件過期時間
29     vector<FileInfo *> fileList;
30     void monitorSubFolder(string path);
31     void (*listener)(const Event  *);
32     void deleteFile();
33 public:
34     FileMonitor(string path,void (*listener)(const Event  *),int expire=180);
35     ~FileMonitor();
36     int start();
37 };
38 
39 #endif // FILEMONITOR_H
  1 #include "filemonitor.h"
  2 #include<string.h>
  3 #include<iostream>
  4 #include <stdio.h>
  5 #include <dirent.h>
  6 #include<stdlib.h>
  7 #include<sys/types.h>
  8 #include<sys/inotify.h>
  9 #include<time.h>
 10 using namespace std;
 11 #define EVENT_SIZE  ( sizeof (struct inotify_event) )
 12 #define BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )
 13 FileMonitor::FileMonitor(string path,void (*listener)(const Event  *),int expire)
 14 {
 15     this->listener=listener;
 16     this->expire=60;//expire*24*60*60;
 17     fd=inotify_init();
 18     if(fd<0)
 19     {
 20         cout<<"innotify_init failed"<<endl;
 21     }
 22     monitorSubFolder(path);
 23 }
 24 int FileMonitor::start()
 25 {
 26 
 27     char buffer[BUF_LEN];
 28     bool start=true;
 29     while(start)
 30     {
 31         int length=0,i=0;
 32         length=read(fd,buffer,BUF_LEN);
 33         if(length<0)
 34         {
 35             cout<<"read none"<<endl;
 36         }
 37         while ( i < length )
 38         {
 39             struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];
 40             if ( event->len &&event->name[0]!='.'&&event->name[strlen(event->name)-1]!='~') //同時過濾隱藏文件和臨時文件
 41             {
 42                 if ( event->mask & IN_CREATE )
 43                 {
 44                     Event *args=new Event;
 45                     if ( event->mask & IN_ISDIR )
 46                     {
 47                         string path=monitorMap[event->wd]+"/"+event->name;
 48                         args->path=path;
 49                         args->fileType=0;
 50                         args->type=CREATE;
 51                         monitorSubFolder(path);
 52                     }
 53                     else
 54                     {
 55                         string path=monitorMap[event->wd]+"/"+event->name;
 56                         args->path=path;
 57                         args->fileType=1;
 58                         args->type=CREATE;
 59                         time_t now;
 60                         now=time(NULL);
 61                         FileInfo * fileInfo=new FileInfo();//將文件信息添加到監控列表中
 62                         fileInfo->path=path;
 63                         fileInfo->expire=now+expire;//設置過期時間
 64                         if(strcmp(event->name,"stop")==0)//如果新建文件名為stop則退出程序
 65                         {
 66                             fileInfo->expire=now;//設置過期時間
 67                             start=false;
 68                         }
 69                         fileList.push_back(fileInfo);
 70                     }
 71                     if(listener!=NULL)
 72                     {
 73                         listener(args);
 74                     }
 75                 }
 76                 else if ( event->mask & IN_DELETE )
 77                 {
 78                     Event *args=new Event;
 79                     if ( event->mask & IN_ISDIR )
 80                     {
 81                         string path=monitorMap[event->wd]+"/"+event->name;
 82                         args->path=path;
 83                         args->fileType=0;
 84                         args->type=DELETE;
 85                         inotify_rm_watch(fd,event->wd);//移除監視
 86                         monitorMap.erase(event->wd);//
 87                     }
 88                     else {
 89                         string path=monitorMap[event->wd]+"/"+event->name;
 90                         args->path=path;
 91                         args->fileType=1;
 92                         args->type=DELETE;
 93                     }
 94                     if(listener!=NULL)
 95                     {
 96                         listener(args);
 97                     }
 98                 }
 99                 else if ( event->mask & IN_MODIFY )
100                 {
101                     Event *args=new Event;
102                     if ( event->mask & IN_ISDIR )
103                     {
104                         string path=monitorMap[event->wd]+"/"+event->name;
105                         args->path=path;
106                         args->fileType=0;
107                         args->type=MODIFY;
108                     }
109                     else {
110                         string path=monitorMap[event->wd]+"/"+event->name;
111                         args->path=path;
112                         args->fileType=1;
113                         args->type=MODIFY;
114                     }
115                     if(listener!=NULL)
116                     {
117                         listener(args);
118                     }
119                 }
120 
121             }
122             i += EVENT_SIZE + event->len;
123         }
124         deleteFile();
125     }
126     return 0;
127 }
128 //監控子目錄
129 void FileMonitor::monitorSubFolder(string path)
130 {
131     int wd=inotify_add_watch(fd,path.c_str(),IN_MODIFY|IN_CREATE|IN_DELETE);
132     monitorMap[wd]=path;//添加到監視列表
133     struct dirent* ent = NULL;
134     DIR *pDir;
135     pDir=opendir(path.c_str());
136     if(pDir==NULL)
137     {
138         cout<<"invalid path"<<endl;
139         return;
140     }
141     while (NULL != (ent=readdir(pDir)))
142     {
143         if(ent->d_type==4){
144             if(ent->d_name[0]!='.')
145             {
146                 string subPath=path+"/"+ent->d_name;
147                 monitorSubFolder(subPath);
148             }
149         }else{
150             if(listener!=NULL)
151             {
152                 int len=strlen(ent->d_name);
153                 if(ent->d_name[0]!='.'&&ent->d_name[len-1]!='~')
154                 {
155                     string filename=path+"/"+ent->d_name;
156                     Event *args=new Event;
157                     args->path=filename;
158                     args->fileType=1;
159                     args->type=CREATE;
160                     time_t now;
161                     now=time(NULL);
162                     FileInfo * fileInfo=new FileInfo();//將文件信息添加到監控列表中
163                     fileInfo->path=filename;
164                     fileInfo->expire=now+expire;//設置過期時間
165                     fileList.push_back(fileInfo);
166                     listener(args);
167                 }
168 
169             }
170 
171         }
172     }
173 }
174 void FileMonitor::deleteFile()
175 {
176     time_t now;
177     now=time(NULL);
178     for(vector<FileInfo *>::iterator it=fileList.begin();it!=fileList.end();)
179     {
180         if((*it)->expire<=now)
181         {
182             if(!remove((*it)->path.c_str()))
183             {
184                 cout<<(*it)->path<<" expired has been deleted"<<endl;
185                 it=fileList.erase(it);
186             }else{
187                 cout<<"delete "<<(*it)->path<<" failure"<<endl;
188                 it++;
189             }
190         }else
191         {
192             it++;
193         }
194     }
195 }
196 FileMonitor::~FileMonitor()
197 {
198     for (map<int, string>::iterator i=monitorMap.begin(); i!=monitorMap.end(); i++)
199     {
200         inotify_rm_watch(fd,i->first);//移除監視
201     }
202     monitorMap.clear();
203     close(fd);
204 }

 

如果大家不想自己寫代碼實現文件監控,現在也有一個工具能夠幫助我們監控文件目錄變化,這就是inotify-tools,這個工具配合rsync可以實現文件實時同步的功能。

下面給大家介紹一下這個工具的使用方法

測試主機地址:A、192.168.20.9
B、192.168.20.20
測試目的將A主機目錄/home/dmx/sync/test/的內容同步到B主機 /home/d5000/dmx/test目錄下
任意修改A主機同步目錄下的內容,對應B主機的目錄也會自動修改。

需要安裝的軟件有
inotify-tools-3.14.tar.gz
rsync-3.0.9.tar.gz
軟件裝的基本步驟
1、解壓 進到解壓后的目錄
2、執行./configure
3、執行make
4、執行make install
服務端配置
服務端需要安裝rsync和inotify
1、創建密碼文件
echo "d5000">rsync.passwd
其中"d5000"表示登錄B主機的密碼,這里用戶名不需要指定,稍后將會在腳本中設置
2、創建執行腳本
測試腳本位於startSync.sh
#!/bin/bash
SRC='/home/dmx/sync/test/'
DES='d5000@192.168.20.20::web'
/usr/local/bin/inotifywait -mr --timefmt '%d/%m/%y %H:%M' --format '%T %w %f' -e modify,delete,create,attrib ${SRC} |while read DATE TIME DIR FILE
do
FILECHAGE=${DIR}${FILE}
/home/dmx/rsync-3.0.9/rsync -av --progress --delete --password-file=/home/dmx/rsync-3.0.9/rsync.passwd $SRC $DES #執行同步命令
echo "At ${TIME} on ${DATE}, file $FILECHAGE was backed up via rsync" >> /home/dmx/sync/rsyncd.log
done
腳本說明:
SRC表示A主機同步的目錄,DES表示B主機的地址,其中web表示一個模塊,名稱可以自定義
--password-file=/home/dmx/rsync-3.0.9/rsync.passwd指定密碼文件的位置。其中,密碼文件只能被當前運行腳本的用戶訪問,對應的命令是:chmod 600 rsync.passwd
/home/dmx/sync/rsyncd.log指定日志文件的存放位置
如果需要同步到多台主機,需要指定不同的DES,同時執行多條同步命令
腳本示例:
#!/bin/bash
SRC='/home/dmx/sync/test/'
DES1='d5000@192.168.20.20::web1'
DES2='d5000@192.168.20.21::web2'
/usr/local/bin/inotifywait -mr --timefmt '%d/%m/%y %H:%M' --format '%T %w %f' -e modify,delete,create,attrib ${SRC} |while read DATE TIME DIR FILE
do
FILECHAGE=${DIR}${FILE}
/home/dmx/rsync-3.0.9/rsync -av --progress --delete --password-file=/home/dmx/rsync-3.0.9/rsync.passwd $SRC $DES1 #執行同步命令
/home/dmx/rsync-3.0.9/rsync -av --progress --delete --password-file=/home/dmx/rsync-3.0.9/rsync.passwd $SRC $DES2 #執行同步命令
echo "At ${TIME} on ${DATE}, file $FILECHAGE was backed up via rsync" >> /home/dmx/sync/rsyncd.log
done
客戶端配置
客戶端只需要安裝rsync
1、創建密碼文件
echo "d5000:d5000">rsync.passwd
格式為"用戶名:密碼"
2、創建rsync.conf配置文件
客戶端配置文件內容
uid = nobody
gid = nobody
use chroot = no
max connections = 10
strict modes = yes
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsync.lock
log file = /var/log/rsyncd.log
[web]#web表示模塊,這里和A主機指定的模塊相同
path = /home/d5000/dmx/test #表示本地同步的目錄
comment = web file
ignore errors
read only = no
write only = no
hosts allow =192.168.20.9 #允許連接的主機地址
hosts deny = *
list = false
uid = root
gid = root
auth users = d5000 #登錄用戶
secrets file = /home/d5000/dmx/rsync-3.0.9/rsync.passwd #密碼文件位置
密碼文件必須只能被運行rsync的用戶訪問,創建時以運行用戶創建,然后執行chmod 600 rsync.passwd
運行./rsync --daemon --config=/home/d5000/dmx/rsync-3.0.9/rsync.conf

這樣就配置完成了。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM