Skip to content

Instantly share code, notes, and snippets.

@caiorss
Created October 27, 2020 18:49
Show Gist options
  • Save caiorss/6e3fc8a39a3a6089ad7002738e4d509a to your computer and use it in GitHub Desktop.
Save caiorss/6e3fc8a39a3a6089ad7002738e4d509a to your computer and use it in GitHub Desktop.
Linux-only Inotify API sample code
#include <iostream>
#include <string>
#include <cstring> // strerror()
#include <memory>
#include <cassert>
#include <map>
// ---- Unix-specific headers ----------//
#include <unistd.h>
#include <limits.h>
// ---- Linux-specific headers --------//
#include <sys/inotify.h>
constexpr int FAILURE = -1;
void exit_on_failure(int fd, const char* error_message)
{
if(fd != FAILURE){ return; }
int err = errno;
std::fprintf( stderr, " [ERROR] %s ; errno = %d ; errno-msg = %s \n"
, error_message, err, strerror(err));
// Execute exit system call and terminate this process.
exit(1);
}
int main(int argc, char** argv)
{
// Create inotify instance, returning file descriptor
int fd_inotify = ::inotify_init();
exit_on_failure(fd_inotify, "Cannot create Inotify instance");
using WatcheFileDescriptor = int;
using DirectoryPath = std::string;
auto watch_table = std::map<WatcheFileDescriptor, DirectoryPath>{};
if(argc < 2){
std::fprintf(stderr, " [ERROR] Requires one or more directories to be watched. \n");
return EXIT_FAILURE;
}
for(int i = 1; i < argc; i++)
{
const char* directory = argv[i];
// Create watch instance, returning a file descriptor for the watcher
int fd_watch = inotify_add_watch(fd_inotify, directory, IN_ALL_EVENTS);
// Error checking
std::string error_msg = std::string("Cannot create watcher for this directory: ") + directory;
exit_on_failure(fd_watch, error_msg.c_str());
watch_table[fd_watch] = directory;
std::fprintf( stdout, " [INFO] Watching directory => wd = %d ; directory = %s \n"
, fd_watch, directory);
}
// Maximum number of events
constexpr size_t MAX_EVENTS = 1024;
// Size of data structure inotify_event in bytes
constexpr size_t EVENT_SIZE = sizeof(struct inotify_event);
// Lenght of buffer for events storage in bytes
constexpr size_t BUFFER_LENGTH = MAX_EVENTS * ( EVENT_SIZE + NAME_MAX + 1 );
std::uint8_t buffer[BUFFER_LENGTH];
// -------------- Inotify event loop ----------------//
for(;;)
{
// std::fprintf(stderr, " [INFO] Waiting for Inotify event \n");
// nn_read => signed number of bytes read from the file descriptor
// It is (-1) in the case of errrors.
ssize_t nn_read = read(fd_inotify, buffer, BUFFER_LENGTH);
assert(nn_read != -1 && "Placeholder for future error handling.");
ssize_t offset = 0;
// Iterate over all events
while( offset < nn_read )
{
auto event = reinterpret_cast<inotify_event*>(buffer + offset);
auto directory = watch_table[event->wd];
// Create file
if(event->mask & IN_CREATE && !(event->mask & IN_ISDIR) )
std::fprintf(stdout
, " [INFO] CREATE file event => (file = %s ) ; (dir = %s) \n"
, event->name, directory.c_str());
// Create directory event
if(event->mask & IN_CREATE && event->mask & IN_ISDIR )
std::fprintf( stdout
, " [INFO] CREATE directory event => (file = %s ) ; (dir = %s) \n"
, event->name, directory.c_str());
// Delete file or directory event.
if(event->mask & IN_DELETE)
std::fprintf( stdout, " [INFO] DELETE file event => (file = %s ) ; (dir = %s) \n"
, event->name, directory.c_str());
// Modify file
if( event->mask & IN_MODIFY && !(event->mask & IN_ISDIR) )
std::fprintf( stdout
, " [INFO] MODIFY file event => (file = %s ) ; (dir = %s) \n"
, event->name, directory.c_str());
// Event happens when file system is unmounted.
if(event->mask & IN_IGNORED)
std::fprintf( stdout
, " [INFO] IGNRED file monitor removed or file system unmounted => dir = %s \n"
, directory.c_str());
offset = offset + EVENT_SIZE + event->len;
}
} // --------- End of Inotify event loop() --------------//
//free(event);
std::fprintf(stdout, " [INFO] Terminate application gracefully \n\n");
close(fd_inotify);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment