/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set et sw=2 ts=2: */ /*************************************************************************** * inotify.cc * * Wed Jan 6 09:58:47 CET 2010 * Copyright 2010 Bent Bisballe Nyeng * deva@aasimon.org ****************************************************************************/ /* * This file is part of Pracro. * * Pracro is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Pracro is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Pracro; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "inotify.h" #include #include #include #include #include #include #define TEST(x, m) ((x & m) == m) static std::string mask2asc(uint32_t mask) { // printf("mask: %x\n", mask); std::string str; if(TEST(mask, IN_ACCESS)) str += "File was accessed (read)."; if(TEST(mask, IN_ATTRIB)) str += "Metadata changed (permissions, timestamps, extended attributes, etc.)."; if(TEST(mask, IN_CLOSE_WRITE)) str += "File opened for writing was closed."; if(TEST(mask, IN_CLOSE_NOWRITE)) str += "File not opened for writing was closed."; if(TEST(mask, IN_CREATE)) str += "File/directory created in watched directory."; if(TEST(mask, IN_DELETE)) str += "File/directory deleted from watched directory."; if(TEST(mask, IN_DELETE_SELF)) str += "Watched file/directory was itself deleted."; if(TEST(mask, IN_MODIFY)) str += "File was modified."; if(TEST(mask, IN_MOVE_SELF)) str += "Watched file/directory was itself moved."; if(TEST(mask, IN_MOVED_FROM)) str += "File moved out of watched directory."; if(TEST(mask, IN_MOVED_TO)) str += "File moved into watched directory."; if(TEST(mask, IN_OPEN)) str += "File was opened."; if(TEST(mask, IN_ALL_EVENTS)) str += "All the above."; return str; } static inline bool isdir(const char *name) { struct stat s; stat(name, &s); return S_ISDIR(s.st_mode); } INotify::Event::Event(std::string name, uint32_t mask) { this->_name = name; this->_mask = mask; } bool INotify::Event::isCloseEvent() { return TEST(_mask, IN_CLOSE_WRITE) || TEST(_mask, IN_CLOSE_NOWRITE); } bool INotify::Event::isOpenEvent() { return TEST(_mask, IN_OPEN) || TEST(_mask, IN_CREATE); } bool INotify::Event::isModifyEvent() { return TEST(_mask, IN_MODIFY); } bool INotify::Event::isDeleteEvent() { return TEST(_mask, IN_DELETE) || TEST(_mask, IN_DELETE_SELF); } std::string INotify::Event::name() { return _name; } uint32_t INotify::Event::mask() { return _mask; } INotify::INotify() { fd = inotify_init1(O_NONBLOCK); if(fd == -1) { perror("Inotify init failed.\n"); return; } } INotify::~INotify() { if(fd != -1) close(fd); } void INotify::add(std::string name, uint32_t mask) { int _fd = inotify_add_watch(fd, name.c_str(), mask); if(_fd == -1) { perror("INotify: Add watch failed!"); return; } // dirmap[fd] = name; } void INotify::addRecursive(std::string name, uint32_t mask) { /* int fd = this->add(name, mask); return fd; */ } void INotify::readEvents() { char buf[sizeof(struct inotify_event)]; ssize_t r = 0; while( (r = read(fd, buf, sizeof(buf))) > 0) { struct inotify_event *event = (struct inotify_event *)buf; /* printf("--- Event ---:\n"); if(event->len) printf("Name: %s\n", event->name); printf("Descriptor: %d\n", event->wd); printf("Events:\n%s\n", mask2asc(event->mask).c_str()); */ eventlist.push_back(Event(event->name, event->mask)); /* // Add watch on new subfolder. if(TEST(event->mask, IN_CREATE) && isdir(event->name)) { std::string fullpath = dirmap[event->wd] + "/" + event->name; int fd = inotify_add_watch(watch, fullpath.c_str(), IN_ALL_EVENTS); dirmap[fd] = fullpath; } */ /* // Remove watch on deleted subfolder. if(TEST(event->mask, IN_DELETE) && isdir(event->name)) { inotify_add_watch(watch, event->name, IN_ALL_EVENTS); } */ } } bool INotify::hasEvents() { readEvents(); return eventlist.size() > 0; } INotify::Event INotify::getNextEvent() { readEvents(); if(eventlist.size()) { Event event = eventlist.front(); eventlist.pop_front(); return event; } return Event("", 0); } void INotify::remove(int fd) { inotify_rm_watch(this->fd, fd); } void INotify::remove(std::string name) { // this->remove(rlookup(map, name)) } #ifdef TEST_INOTIFY //deps: debug.cc //cflags: -I.. //libs: #include "test.h" #include #define _DIR "/tmp" #define _FILE _DIR"/inotify_test" TEST_BEGIN; INotify inotify; // Create file FILE *fp = fopen(_FILE, "w"); TEST_NOTEQUAL(fp, NULL, "Testing if able to write file"); fprintf(fp, "something"); fclose(fp); inotify.add(_FILE); // Append to file fp = fopen(_FILE, "a"); TEST_NOTEQUAL(fp, NULL, "Testing if able to write file"); TEST_TRUE(inotify.hasEvents(), "Test if the open event was triggered."); TEST_TRUE(inotify.getNextEvent().isOpenEvent(), "Test if the event was an open event."); fprintf(fp, "else"); fflush(fp); TEST_TRUE(inotify.hasEvents(), "Test if the append event was triggered."); TEST_TRUE(inotify.getNextEvent().isModifyEvent(), "Test if the event was a modified event."); fclose(fp); TEST_TRUE(inotify.hasEvents(), "Test if the close event was triggered."); TEST_TRUE(inotify.getNextEvent().isCloseEvent(), "Test if the event was a close event."); // Overwrite file fp = fopen(_FILE, "w"); TEST_NOTEQUAL(fp, NULL, "Testing if able to write file"); TEST_TRUE(inotify.hasEvents(), "Test if the open event was triggered."); TEST_TRUE(inotify.getNextEvent().isOpenEvent(), "Test if the event was an open event."); fprintf(fp, "else"); fflush(fp); TEST_TRUE(inotify.hasEvents(), "Test if the append event was triggered."); TEST_TRUE(inotify.getNextEvent().isModifyEvent(), "Test if the event was a modified event."); fclose(fp); TEST_TRUE(inotify.hasEvents(), "Test if the close event was triggered."); TEST_TRUE(inotify.getNextEvent().isCloseEvent(), "Test if the event was a close event."); // Delete file unlink(_FILE); TEST_EQUAL(inotify.hasEvents(), true, "Test if the delete event was triggered."); TEST_EQUAL(inotify.getNextEvent().isDeleteEvent(), true, "Test if the event was a delete event."); mask2asc(0); TEST_END; #endif/*TEST_INOTIFY*/