 |
ewmscp
..
|
Go to the documentation of this file.
31 #include <sys/inotify.h>
33 #include <sys/types.h>
71 static const std::array<maskName,16> maskNamesArray {{{IN_ACCESS,
"IN_ACCESS"},
72 {IN_ATTRIB,
"IN_ATTRIB"},
73 {IN_CLOSE_WRITE,
"IN_CLOSE_WRITE"},
74 {IN_CLOSE_NOWRITE,
"IN_CLOSE_NOWRITE"},
75 {IN_CREATE,
"IN_CREATE"},
76 {IN_DELETE,
"IN_DELETE"},
77 {IN_DELETE_SELF,
"IN_DELETE_SELF"},
78 {IN_MODIFY,
"IN_MODIFY"},
79 {IN_MOVE_SELF,
"IN_MOVE_SELF"},
80 {IN_MOVED_FROM,
"IN_MOVED_FROM"},
81 {IN_MOVED_TO,
"IN_MOVED_TO"},
83 {IN_IGNORED,
"IN_IGNORED"},
84 {IN_ISDIR,
"IN_ISDIR"},
85 {IN_Q_OVERFLOW,
"IN_Q_OVERFLOW"},
86 {IN_UNMOUNT,
"IN_UNMOUNT"}
88 return(maskNamesArray);
91 static void print(std::ostream& out, uint32_t mask) {
92 bool needComma =
false;
95 if (mask & item.bits) {
106 for (
const auto& listItem : list) {
108 if (item.name == listItem) {
137 inline int wd()
const {
146 inline uint32_t
len()
const {
149 inline const char *
name()
const {
156 return sizeof(inotify_event) +
len();
164 out <<
"(" << aEvent.
mask() <<
")";
181 static std::remove_reference<decltype(
getRenameEvents())>::type renameEvents;
202 void* newEvent = malloc(theEvent->size());
203 memcpy(newEvent, theEvent, theEvent->size());
228 it.second.doCopyIfNeeded();
241 (*it)->then < clock_type::now() - std::chrono::seconds(5)) {
268 clock_type::time_point
then) {
272 result.first->second.then =
then;
275 return &(result.first->second);
287 static std::remove_reference<decltype(
getWatchedDirs())>::type watchedDirs;
311 if (result.second ==
false) {
312 result.first->second->parent =
nullptr;
313 result.first->second =
this;
321 static void dump(std::ostream& out) {
325 item.second->buildPath(path);
326 out <<
errPrefix << item.first <<
": " << path <<
"\n";
328 out <<
errPrefix <<
"end list of watched directories.\n";
343 const std::string& aName) {
388 id = inotify_add_watch(
fd, path.c_str(),
391 | IN_MOVED_TO | IN_MOVED_FROM
399 if (
id == -1 && (errno == ENOENT || errno == ENOTDIR)) {
408 if (result.second ==
false) {
426 auto retval = inotify_rm_watch(
fd,
id);
428 if (retval && errno == EINVAL) {
440 *
errStream <<
errPrefix <<
"oops " <<
getPath() <<
" still has child " << child.second->getPath() <<
", deleting it" << std::endl;
495 const std::string& name,
507 auto retval = lstat(path.c_str(), &statbuf);
509 if (retval && (errno == ENOENT || errno == ENOTDIR)) {
515 if (!S_ISDIR(statbuf.st_mode)) {
522 if (watchedDir->getId() == -1) {
528 std::list<std::string> subdirs;
530 auto result = opendir(path.c_str());
532 if (result ==
nullptr && (errno == ENOENT || errno == ENOTDIR)) {
539 while (
auto entry = readdir(dir)) {
540 if (entry->d_name[entry->d_name[0] !=
'.' ? 0 : entry->d_name[1] !=
'.' ? 1 : 2] ==
'\0') {
543 if (entry->d_type == DT_DIR) {
544 subdirs.emplace_back(entry->d_name);
549 for (
auto& subname : subdirs) {
559 std::chrono::system_clock::time_point t) {
560 stream << std::chrono::duration_cast<std::chrono::duration<double>>(t.time_since_epoch()).count();
576 std::exit(EXIT_SUCCESS);
579 std::exit(EXIT_FAILURE);
600 std::cout << std::fixed;
602 void sendCommand(
const char *command, std::chrono::system_clock::time_point when,
603 const std::string& path) {
604 std::cout << command <<
' ' << when <<
' ' << path <<
delimiter;
608 void sendCommand(
const char *command, std::chrono::system_clock::time_point when,
609 const std::string& path1,
const std::string& path2) {
617 auto now = clock_type::now();
639 "use NUL as delimiter between requests",
false);
641 "keepalive timeout (seconds)", 120);
649 int main(
int argc,
const char* argv[]) {
650 std::ios::sync_with_stdio(
false);
653 "directories to be watched");
655 "use DIRECTORY as work dir");
657 "regexp for files to be ignored for IN_CLOSE_WRITE");
659 "be verbose",
false);
661 "file to write errors to");
664 "extra events to listen to");
667 "extra events beside IN_CLOSE_NOWRITE hat trigger a copy");
669 parser.fParse(argc, argv);
678 std::cerr <<
"events of type ";
680 std::cerr <<
" (" << std::hex << copyTriggerBits << std::dec <<
") will trigger a copy event\n";
686 pidFile << getpid() <<
"\n";
700 for (
const auto& directory : directories) {
707 char buffer[4096 * 256] __attribute__ ((aligned(__alignof__(
struct inotify_event))));
724 auto event = copy->getEvent();
728 if (event->mask() & IN_MOVED_TO) {
742 auto meAsDirChild = parent->
getChild(event->name());
754 auto result = poll(&pfd, 1, 1000);
757 if (errno == EINTR) {
765 for (
const auto& directory : directories) {
767 auto retval = stat(directory.c_str(), &statbuf);
769 if (retval != 0 && errno == ENOENT) {
780 -1,
"read inotification");
781 auto now = clock_type::now();
784 *
errStream <<
errPrefix << now <<
" got " << bytes <<
" bytes of notification" << std::endl;
791 event =
event->next()) {
793 *
errStream <<
errPrefix <<
"watch id " <<
event->wd() <<
" @ " << (
void*)event <<
" with name " << event->name() << *
event << std::endl;
796 if (event->mask() & IN_IGNORED) {
800 if (event->mask() & IN_Q_OVERFLOW) {
808 if (watchedDir ==
nullptr) {
809 *
errStream <<
errPrefix <<
"unknown watch id " <<
event->wd() <<
" @ " << (
void*)event <<
" with name " << event->name() << *
event << std::endl;
813 watchedDir->buildPath(path, event->name());
815 if (event->mask() & (IN_MOVED_TO | IN_MOVED_FROM)) {
819 std::string partnerPath;
823 const std::string* fromPath;
824 const std::string* toPath;
826 if (event->mask() & IN_MOVED_TO) {
827 from = partner->getEvent();
829 fromPath = &partnerPath;
833 to = partner->getEvent();
835 toPath = &partnerPath;
840 if (fromAsDirChild) {
849 if (event->mask() & copyTriggerBits) {
855 if (event->mask() & IN_CREATE) {
867 if (event->mask() & IN_DELETE_SELF) {
868 watchedDir->invalidateId();
876 if (event->mask() & IN_DELETE) {
893 }
catch (
const std::exception& e) {
static std::multiset< inotify_event_copy *, decltype(cmp) * > & getSingleEvents()
return a multiset containing single inotify_event_copys ordered by time
class that contains the parser, i.e. does that option handling
represents a watched directory inside a directory tree.
static void doCopyLeftovers()
options::single< bool > verbose
static std::map< int, watchedDirType * > & getWatchedDirs()
void buildPath(std::string &path, const std::string &aName)
fill path with the full path to a file in this directory
std::string name
basename of the directory, i.e. not the full path
options::single< std::string > workDir
static inotify_event_copy * getPartner(const inotify_event_wrapper *event, clock_type::time_point then)
get partner of a half rename event.
class specialisation for options of type bool
maskName(uint32_t aBits, const std::string &aName)
clock_type::time_point then
the time the event was recorded
virtual void fAddToRange(rangeValueType aValue)
add a value to the range of allowed values
T badval(T call, t badvalue, const Args &... args)
template function to wrap system calls that return a special bad value on failure
void buildPath(std::string &path)
fill path with the fill path to this directory
flushDomain(commandSender &aSender)
const char * name() const
smart copy class for inotify_event.
friend std::ostream & operator<<(std::ostream &out, const inotify_event_wrapper &aEvent)
std::remove_reference< decltype(getSingleEvents())>::type::iterator singleEventIterator
iterator into the multiset containing the single events
void sendCommand(const char *command, std::chrono::system_clock::time_point when, const std::string &path1, const std::string &path2)
static size_t size()
return number of watched directories
decltype(children.begin()) childIterator
iterator in parent's child map that holds this object
virtual bool fIsSet() const
check if this option was set, regardless of from command line or config file
wrapper for inotify_event with nicer interface.
static std::atomic< bool > dumpRequested(false)
std::ostream * errStream(nullptr)
static void forget(inotify_event_copy *what)
deletes a copy.
watchedDirType * getChild(const std::string &aName)
static options::single< bool > nullDelimiter
options::single< std::string > errPrefix('\0', "errPrefix", "prefix for error messages")
std::chrono::system_clock clock_type
void getAdoptedBy(const std::string &newName, watchedDirType *newParent)
move to a new place in the directory tree.
decltype(getWatchedDirs().begin()) watchIterator
iterator in the map that holds this object
~inotify_event_wrapper()=delete
std::ostream & getStream()
void sendCommand(const char *command, std::chrono::system_clock::time_point when, const std::string &path)
watchedDirType(const std::string &aName, int aFd, watchedDirType *aParent=nullptr)
construct a new watchedDir.
static void sigUsrHandler(int)
watchDirectoryReturnType
special enum for the return values of watchDirectory.
static void pidFileRemover()
static void print(std::ostream &out, uint32_t mask)
void becomeChildOfMyParent(watchedDirType *aParent)
set object a child of the parent.
~watchedDirType()
destruct watchedDir.
class to flush output on scope basis.
const inotify_event_wrapper * next() const
void doCopyIfNeeded()
make a real copy of this inotify_event.
static options::single< std::string > pidFileName('p', "pidFile", "name of PID file")
watchDirectoryReturnType watchDirectory(int fd, const std::string &name, watchedDirType *parent=nullptr)
watch a directory.
static const std::array< maskName, 16 > maskNames()
static void addToRange(options::container< std::string > &opt)
int main(int argc, const char *argv[])
main inotify_watch function.
static uint32_t maskFromList(const options::container< std::string > &list)
static void setExtraEventMask(int extraMask)
inotify_event_wrapper()=delete
static bool empty()
test if any directories are watched
std::string getPath()
return full path to this dir, for use in error messages only
clock_type::time_point timeStamp() const
static options::single< unsigned > keepaliveTimeout
static inotify_event_copy * getStaleSingles()
get the next old inotify_event.
static std::map< uint32_t, inotify_event_copy > & getRenameEvents()
return a map which contains the inotify_event_copys
clock_type::time_point lastSendTime
static void dump(std::ostream &out)
const inotify_event_wrapper * event
pointer to the event, either the original or a copy
inotify_event_copy(const inotify_event_wrapper *aEvent)
const inotify_event_wrapper * getEvent() const
static void sigPipeHandler(int)
int fd
fd for the inotify instance, needed for destructor
static bool cmp(const inotify_event_copy *lhs, const inotify_event_copy *rhs)
function to order inotify_event_copys by time
std::map< std::string, watchedDirType * > children
void good0(T call, const Args &... args)
template function to wrap system calls that return 0 on success
static watchedDirType * get(int aId)
get a watcheDir by the inotify id
std::ostream & operator<<(std::ostream &stream, std::chrono::system_clock::time_point t)
static const inotify_event_wrapper * fromChar(char *pointer)
static void sigTermHandler(int)
class used to send messages via stdout.
bool isCopy
true if it's a copy. We can't use singleEventIterator as this might be getSingleEvents()....
static int extraEventMask