00001
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "FileEventWatcher.hpp"
00030 #include "Debug.hpp"
00031 #include "Exception.hpp"
00032 #include "Util.hpp"
00033 #include "UtilTime.hpp"
00034 #include <boost/bind.hpp>
00035 #include <boost/filesystem/operations.hpp>
00036 #include <sys/time.h>
00037 #include <sys/types.h>
00038 #include <sys/stat.h>
00039 #include <fcntl.h>
00040 #include <linux/input.h>
00041 #include <unistd.h>
00042 #include <sys/inotify.h>
00043 #include <unistd.h>
00044 #include <errno.h>
00045 #include <sys/socket.h>
00046 #include <sys/un.h>
00047
00048 using namespace std;
00049 using namespace boost;
00050 using namespace H;
00051
00053
00055
00060 #define READ_BUF_SIZE 65536
00061
00066 #define NOTIFY_EVENT_SIZE (sizeof(struct inotify_event))
00067
00072 #define NOTIFY_READ_BUF_SIZE 1024 * (NOTIFY_EVENT_SIZE + 16)
00073
00078 #define DEVICE_NAME_BUF_SIZE 1024
00079
00084 #define RETRY_FAIL_WAIT_NSECS 100000000
00085
00090 #define MAX_RETRIES 5
00091
00096 #define POLL_TIMEOUT 1000
00097
00099
00101
00105 DeviceInfo::DeviceInfo() {
00106 DeviceIDBusType = -1;
00107 DeviceIDProduct = -1;
00108 DeviceIDVendor = -1;
00109 DeviceIDVersion = -1;
00110 }
00111
00115 DeviceInfo::DeviceInfo(std::string deviceName, std::string fileName, int deviceIDBusType, int deviceIDVendor, int deviceIDProduct, int deviceIDVersion, int fileDescriptor) {
00116 DeviceName = deviceName;
00117 FileName = fileName;
00118 DeviceIDBusType = deviceIDBusType;
00119 DeviceIDVendor = deviceIDVendor;
00120 DeviceIDProduct = deviceIDProduct;
00121 DeviceIDVersion = deviceIDVersion;
00122 FileDescriptor = fileDescriptor;
00123 }
00124
00128 DeviceInfo::DeviceInfo(const DeviceInfo & DeviceInformation) {
00129 DeviceName = DeviceInformation.DeviceName;
00130 FileName = DeviceInformation.FileName;
00131 DeviceIDBusType = DeviceInformation.DeviceIDBusType;
00132 DeviceIDVendor = DeviceInformation.DeviceIDVendor;
00133 DeviceIDProduct = DeviceInformation.DeviceIDProduct;
00134 DeviceIDVersion = DeviceInformation.DeviceIDVersion;
00135 FileDescriptor = DeviceInformation.FileDescriptor;
00136 }
00137
00141 DeviceInfo::~DeviceInfo() {
00142 }
00143
00147 FileEventWatcher::FileEventWatcher() {
00148 mPolling = false;
00149 if ((mInotifyFD = inotify_init()) < 0)
00150 throw H::Exception("Failed to Initialize Inotify!\n - Inotify must be compiled into the kernel, or installed as a kernel module\n - Set kernel options CONFIG_INOTIFY, and CONFIG_INOTIFY_USER to yes", __FILE__, __FUNCTION__, __LINE__);
00151 }
00152
00156 FileEventWatcher::~FileEventWatcher() {
00157 removeAllWatchDescriptors();
00158 if (mInotifyFD != -1)
00159 close(mInotifyFD);
00160 }
00161
00165 FileWatchee::FileWatchee() {
00166 WatchType = WATCH_IN;
00167 fd = -1;
00168 Events = POLLIN;
00169 }
00170
00174 FileWatchee::FileWatchee(std::string fileName, FileWatchType watchType, short events, int fileDescriptor, int watchDescriptor, std::string deviceName, int deviceIDBusType, int deviceIDVendor, int deviceIDProduct, int deviceIDVersion) :
00175 DeviceInfo(deviceName, fileName, deviceIDBusType, deviceIDVendor, deviceIDProduct, deviceIDVersion, fileDescriptor)
00176 {
00177 WatchType = watchType;
00178 Events = events;
00179 fd = fileDescriptor;
00180 wd = watchDescriptor;
00181 if (fd < 0)
00182 DeviceType = WATCH_INOTIFY;
00183 else
00184 DeviceType = WATCH_POLL;
00185 }
00186
00190 FileWatchee::~FileWatchee() {
00191 if (fd > -1)
00192 close(fd);
00193 }
00194
00196
00198
00205 boost::shared_ptr<FileWatchee> FileEventWatcher::addFileToWatch(std::string FileName, FileWatchType WatchType, std::string DefaultDeviceName) {
00206
00207 shared_ptr<FileWatchee> pDupWatchee = getWatcheeByPath(FileName);
00208 if (pDupWatchee) {
00209
00210
00211 cdbg << "Already Watching File [" << FileName << "]" << endl;
00212 return shared_ptr<FileWatchee>();
00213 }
00214 pDupWatchee.reset();
00215 cdbg1 << "Adding File [" << FileName << "] to Watch List with Mode [" << WatchType << "]" << endl;
00216
00217
00218 int flags = 0;
00219 string ModeString;
00220 short events = 0;
00221 switch (WatchType) {
00222 case WATCH_IN:
00223 flags = O_RDONLY;
00224 events = POLLIN;
00225 ModeString = "Read";
00226 break;
00227 case WATCH_OUT:
00228 flags = O_WRONLY;
00229 events = POLLOUT;
00230 ModeString = "Write";
00231 break;
00232 case WATCH_INOUT:
00233 flags = O_RDWR;
00234 events = POLLIN | POLLOUT;
00235 ModeString = "Read / Write";
00236 break;
00237 case WATCH_INVALID:
00238 throw H::Exception("Invalid Watch Type specified on [" + FileName + "]", __FILE__, __FUNCTION__, __LINE__);
00239 }
00240
00241
00242 filesystem::path FilePath(FileName);
00243 if (!filesystem::exists(FilePath))
00244 throw H::Exception("Path [" + FileName + "] does not exist, or cannot open (perms?) with Mode [" + ModeString + "]", __FILE__, __FUNCTION__, __LINE__);
00245
00246
00247 int fd = -1;
00248 int wd = -1;
00249 char DeviceName[DEVICE_NAME_BUF_SIZE] = {'\0'};
00250 unsigned short DeviceIDs[4] = {-1, -1, -1, -1};
00251 if (filesystem::is_directory(FilePath)) {
00252
00253 if ((wd = inotify_add_watch(mInotifyFD, FileName.c_str(), IN_CREATE | IN_DELETE | IN_DELETE_SELF)) == -1)
00254 throw H::Exception("Failed to add Watch on Directory [" + FileName + "]", __FILE__, __FUNCTION__, __LINE__);
00255 fd = -wd;
00256 mInotifyWDs.push_back(wd);
00257 strcpy(DeviceName, "Directory");
00258 } else {
00259
00260 int retry;
00261 for (retry = 0; retry < MAX_RETRIES; retry ++) {
00262 if ((fd = open(FileName.c_str(), flags)) == -1) {
00263 cdbg1 << "New Device [" << FileName << "] Open Failed -- Retrying" << endl;
00264 UtilTime::nanoSleep(RETRY_FAIL_WAIT_NSECS);
00265 continue;
00266 }
00267 break;
00268 }
00269 if (retry == MAX_RETRIES) {
00270
00271 cerr << "Failed to Open [" << FileName << "] for [" << ModeString + "] -- Check Permissions!" << endl;
00272 return shared_ptr<FileWatchee>();
00273 }
00274
00275
00276 if (ioctl(fd, EVIOCGNAME(sizeof(DeviceName)), DeviceName) < 0) {
00277 cdbg3 << "Failed to Get Device Name for [" + FileName + "]" << endl;
00278 strcpy(DeviceName, DefaultDeviceName.c_str());
00279 }
00280
00281
00282 if (ioctl(fd, EVIOCGID, DeviceIDs) < 0)
00283 cdbg3 << "Failed to Get Device IDs for [" + FileName + "]" << endl;
00284 }
00285
00286 cdbg1 << "Watching Device [" << FileName << "]: " << DeviceName << endl;
00287 shared_ptr<FileWatchee> pWatchee(new FileWatchee(FileName, WatchType, events, fd, wd, DeviceName, DeviceIDs[0], DeviceIDs[1], DeviceIDs[2], DeviceIDs[3]));
00288 mWatchees.insert(make_pair(fd, pWatchee));
00289 buildPollFDArrayFromWatchees();
00290 onFileEventRegister(pWatchee);
00291
00292 return pWatchee;
00293 }
00294
00300 boost::shared_ptr<FileWatchee> FileEventWatcher::addUnixSocketToWatch(std::string FileName, std::string DeviceName) {
00301
00302 string ModeString = "Read";
00303 short events = POLLIN;
00304 FileWatchType WatchType = WATCH_IN;
00305
00306
00307 filesystem::path FilePath(FileName);
00308 if (!filesystem::exists(FilePath))
00309 throw H::Exception("Path [" + FileName + "] does not exist, or cannot open (perms?) with Mode [" + ModeString + "]", __FILE__, __FUNCTION__, __LINE__);
00310
00311
00312 struct sockaddr_un Addr;
00313 Addr.sun_family = AF_UNIX;
00314 strcpy(Addr.sun_path, FileName.c_str());
00315
00316
00317 int fd;
00318 if ((fd = socket(AF_UNIX,SOCK_STREAM, 0)) == -1) {
00319 cdbg1 << "Failed to Creat Socket for [" << FileName << "] for [" << ModeString + "] -- Check Permissions!" << endl;
00320 return shared_ptr<FileWatchee>();
00321 }
00322
00323
00324 if (connect(fd,(struct sockaddr *) &Addr,sizeof(Addr)) == -1) {
00325 cdbg1 << "Failed to Connect to [" << FileName << "] for [" << ModeString + "] -- Check Permissions!" << endl;
00326 close(fd);
00327 return shared_ptr<FileWatchee>();
00328 }
00329
00330 cdbg1 << "Watching Unix Socket [" << FileName << "]: " << DeviceName << endl;
00331 shared_ptr<FileWatchee> pWatchee(new FileWatchee(FileName, WatchType, events, fd, -1, DeviceName, -1, -1, -1, -1));
00332 mWatchees.insert(make_pair(fd, pWatchee));
00333 buildPollFDArrayFromWatchees();
00334 onFileEventRegister(pWatchee);
00335
00336 return pWatchee;
00337 }
00338
00342 void FileEventWatcher::buildPollFDArrayFromWatchees() {
00343
00344 mPollFDs.clear();
00345
00346
00347 struct pollfd PollFD;
00348 PollFD.fd = mInotifyFD;
00349 PollFD.events = POLLIN | POLLOUT;
00350 PollFD.revents = 0;
00351 mPollFDs.push_back(PollFD);
00352
00353
00354 apply_func(mWatchees, &FileEventWatcher::buildPollFDArrayFunctor, this);
00355 }
00356
00361 void FileEventWatcher::buildPollFDArrayFunctor(std::pair< int, boost::shared_ptr<FileWatchee> > WatcheePair) {
00362 boost::shared_ptr<FileWatchee> pWatchee = WatcheePair.second;
00363 if ( (!pWatchee) || (pWatchee->fd < 0) )
00364 return;
00365
00366
00367 struct pollfd PollFD;
00368 PollFD.fd = pWatchee->fd;
00369 PollFD.events = pWatchee->Events;
00370 PollFD.revents = 0;
00371 mPollFDs.push_back(PollFD);
00372 }
00373
00379 FileWatchType FileEventWatcher::getType(int Index) {
00380 if (mPollFDs[Index].revents & POLLIN)
00381 return WATCH_IN;
00382 else if (mPollFDs[Index].revents & POLLOUT)
00383 return WATCH_OUT;
00384 else if (mPollFDs[Index].revents & POLLOUT & POLLIN)
00385 return WATCH_INOUT;
00386 else
00387 return WATCH_INVALID;
00388 }
00389
00395 boost::shared_ptr<FileWatchee> FileEventWatcher::getWatcheeByFileDescriptor(int fd) {
00396 return mWatchees[fd];
00397 }
00398
00404 boost::shared_ptr<FileWatchee> FileEventWatcher::getWatcheeByPath(std::string FileName) {
00405 map< int, shared_ptr<FileWatchee> >::iterator iter;
00406 for (iter = mWatchees.begin(); iter != mWatchees.end(); iter ++) {
00407 shared_ptr<FileWatchee> pWatchee = iter->second;
00408 if (!pWatchee) {
00409 mWatchees.erase(iter);
00410 continue;
00411 }
00412 if (pWatchee->FileName == FileName)
00413 return pWatchee;
00414 }
00415
00416
00417 return shared_ptr<FileWatchee>();
00418 }
00419
00425 boost::shared_ptr<FileWatchee> FileEventWatcher::getWatcheeByWatchDescriptor(int wd) {
00426 return mWatchees[-wd];
00427 }
00428
00434 void FileEventWatcher::readFromFile(int fd, DynamicBuffer<char> & Buffer) {
00435 char ReadBuffer[READ_BUF_SIZE];
00436 ssize_t BytesRead;
00437 do {
00438 if ((BytesRead = read(fd, ReadBuffer, READ_BUF_SIZE)) <= 0)
00439 throw H::DeviceDisconnectException();
00440 Buffer.addToBuffer(ReadBuffer, BytesRead);
00441 } while (BytesRead == READ_BUF_SIZE);
00442 }
00443
00448 void FileEventWatcher::handleEventsOnFile(struct pollfd & item) {
00449 if (item.fd == mInotifyFD) {
00450 if (item.revents & POLLERR) {
00451 cdbg << "Error detected on inotify device" << endl;
00452 } else if ( (item.revents & POLLIN) || (item.revents & POLLPRI) ) {
00453
00454 char ReadBuffer[NOTIFY_READ_BUF_SIZE];
00455 int BytesRead = read(mInotifyFD, ReadBuffer, NOTIFY_READ_BUF_SIZE);
00456 if (BytesRead < 0)
00457 if (errno == EINTR)
00458 return;
00459 else
00460 throw H::Exception("Failed to Read from Inotify Device!", __FILE__, __FUNCTION__, __LINE__);
00461
00462
00463 int BytesHandled = 0;
00464 while (BytesHandled < BytesRead) {
00465 struct inotify_event * event;
00466 event = (struct inotify_event *) &ReadBuffer[BytesHandled];
00467 BytesHandled += NOTIFY_EVENT_SIZE + event->len;
00468
00469 shared_ptr<FileWatchee> pWatchee = getWatcheeByWatchDescriptor(event->wd);
00470 if (!pWatchee) {
00471 cerr << "Unhandled inotify event: " << event->name << endl;
00472 continue;
00473 }
00474
00475 if (event->mask & IN_ACCESS)
00476 cout << "Access" << endl;
00477 if (event->mask & IN_ATTRIB)
00478 cout << "Attrib" << endl;
00479 if (event->mask & IN_CLOSE_WRITE)
00480 cout << "CloseWrite" << endl;
00481 if (event->mask & IN_CLOSE_NOWRITE)
00482 cout << "CloseNoWrite" << endl;
00483 if (event->mask & IN_CREATE)
00484 onFileEventCreate(pWatchee, pWatchee->FileName + "/" + event->name, event->name);
00485 if ( (event->mask & IN_DELETE) || (event->mask & IN_DELETE_SELF) ) {
00486
00487 shared_ptr<FileWatchee> pActualWatchee = getWatcheeByPath(pWatchee->FileName + "/" + event->name);
00488 if (!pActualWatchee) {
00489 cdbg2 << "Delete inotify event on unhandled file: " << event->name << endl;
00490 continue;
00491 }
00492 onFileEventDelete(pActualWatchee, pActualWatchee->FileName, event->name);
00493 }
00494 if (event->mask & IN_MODIFY)
00495 cout << "Modify" << endl;
00496 if (event->mask & IN_MOVE_SELF)
00497 cout << "MoveSelf" << endl;
00498 if (event->mask & IN_MOVED_FROM)
00499 cout << "MovedFrom" << endl;
00500 if (event->mask & IN_MOVED_TO)
00501 cout << "MovedTo" << endl;
00502 if (event->mask & IN_OPEN)
00503 cout << "Open" << endl;
00504 if (event->mask & IN_UNMOUNT)
00505 cout << "Unmount" << endl;
00506 }
00507 }
00508 } else if (item.fd >= 0) {
00509 if (item.revents & POLLERR) {
00510 shared_ptr<FileWatchee> pWatchee = getWatcheeByFileDescriptor(item.fd);
00511 if (pWatchee) {
00512 cdbg5 << "Error detected on poll device: " << item.fd << " -- " << pWatchee->FileName << endl;
00513 onFileEventDisconnect(pWatchee);
00514 removeWatchee(pWatchee);
00515 } else {
00516 cdbg5 << "Error detected on poll device: " << item.fd << endl;
00517 buildPollFDArrayFromWatchees();
00518 }
00519 } else if ( (item.revents & POLLIN) || (item.revents & POLLPRI) ) {
00520
00521 shared_ptr<FileWatchee> pWatchee = getWatcheeByFileDescriptor(item.fd);
00522 if (!pWatchee) {
00523 cdbg5 << "Unhandled file event on fd [" << item.fd << "]" << endl;
00524 buildPollFDArrayFromWatchees();
00525 return;
00526 }
00527 try {
00528 DynamicBuffer<char> Buffer;
00529 readFromFile(item.fd, Buffer);
00530 if (pWatchee)
00531 onFileEventRead(pWatchee, Buffer);
00532 } catch (H::DeviceDisconnectException & e) {
00533 if ( (pWatchee) && (pWatchee->DeviceType == WATCH_POLL) ) {
00534 onFileEventDisconnect(pWatchee);
00535 removeWatchee(pWatchee);
00536 } else
00537 cout << "Disconnect from Unknown Device" << endl;
00538 }
00539 }
00540 }
00541 }
00542
00549 void FileEventWatcher::onFileEventCreate(boost::shared_ptr<FileWatchee> pWatchee, std::string FullPath, std::string FileName) {
00550
00551 }
00552
00559 void FileEventWatcher::onFileEventDelete(boost::shared_ptr<FileWatchee> pWatchee, std::string FullPath, std::string FileName) {
00560
00561 }
00562
00567 void FileEventWatcher::onFileEventDisconnect(boost::shared_ptr<FileWatchee> pWatchee) {
00568
00569 }
00570
00576 void FileEventWatcher::onFileEventRead(boost::shared_ptr<FileWatchee> pWatchee, DynamicBuffer<char> const & ReadBuffer) {
00577
00578 }
00579
00584 void FileEventWatcher::onFileEventRegister(boost::shared_ptr<FileWatchee> pWatchee) {
00585
00586 }
00587
00591 void FileEventWatcher::onFileEventWatchBegin() {
00592
00593 }
00594
00598 void FileEventWatcher::onFileEventWatchEnd() {
00599
00600 }
00601
00606 void FileEventWatcher::removeWatchDescriptor(int wd) {
00607 inotify_rm_watch(mInotifyFD, wd);
00608 }
00609
00613 void FileEventWatcher::removeAllWatchDescriptors() {
00614 apply_func(mInotifyWDs, &FileEventWatcher::removeWatchDescriptor, this);
00615 }
00616
00621 void FileEventWatcher::removeWatchee(boost::shared_ptr<FileWatchee> pWatchee) {
00622 if (!pWatchee)
00623 return;
00624 map< int, shared_ptr<FileWatchee> >::iterator iter;
00625 bool removed = false;
00626 for (iter = mWatchees.begin(); iter != mWatchees.end(); iter ++) {
00627 if (!iter->second)
00628 continue;
00629 if (iter->second->fd == pWatchee->fd) {
00630 cdbg4 << "Removed Watchee [" << pWatchee->FileName << "]" << endl;
00631 mWatchees.erase(iter);
00632 removed = true;
00633 break;
00634 }
00635 }
00636 if (removed)
00637 buildPollFDArrayFromWatchees();
00638 }
00639
00646 void FileEventWatcher::shutdown() {
00647 mPolling = false;
00648 }
00649
00655 void FileEventWatcher::watchForFileEvents() {
00656 if (mPollFDs.size() == 0) {
00657 cdbg << "FileEventWatcher :: watchForFileEvents -- No file to watch!" << endl;
00658 return;
00659 }
00660
00661
00662 onFileEventWatchBegin();
00663
00664 cdbg1 << "FileEventWatcher :: Watching [" << (int) mPollFDs.size() << " Files] for Events..." << endl;
00665 mPolling = true;
00666 int ret;
00667 do {
00668
00669 if ((ret = poll(&mPollFDs[0], mPollFDs.size(), POLL_TIMEOUT)) == -1) {
00670
00671 cdbg1 << "Poll error: " << strerror(errno) << endl;
00672
00673
00674
00675 onFileEventWatchEnd();
00676 return;
00677 }
00678
00679 if (!ret)
00680
00681 continue;
00682
00683
00684 cdbg5 << "Processing File Events..." << endl;
00685 apply_func(mPollFDs, &FileEventWatcher::handleEventsOnFile, this);
00686 } while (mPolling);
00687 cdbg1 << "FileEventWatcher :: Done Watching for File Events" << endl;
00688
00689
00690 onFileEventWatchEnd();
00691 }