When dealing with Unix-style filesystems there must be a data structure that is capable of describing an
object from that filesystem, such as a file or a directory.
Linux is no exception, so here comes the Inode.
The data it represents includes access time, modification time, file owner, permissions and much more.
Directories are a special type of file. They consist in lists of names, or labels, assigned to inodes in the filesystem.
In addition, they also need means of representing themselves and how to get to their parent level.
Here comes the "./" and the "../", respectively.
What if you need a way of keeping track of all these possible metadata operations on files and directories?
Well, if that is the case, then you are thinking about the Kernel's Inotify subsystem. It acts as an extension to the
filesystem, which provides information on changes to that filesystem. It is worth mentioning that the current inotify
is a replacement for the dnotify, as of kernel version 2.6.13 (29-08-2005).
An example of the importance of the inotify subsystem relies on the simple fact that your file explorer
(Dolphin, Nautilus, Thunar, Xfe, etc.) will simply update and redraw its view based on file/directory changes.
Like the "update" on the total directory space, file modification time, etc.
To see inotify in action, here is a simple program that prints to the screen any modification you make to the provided
file as the command line argument.
/*
* Test kernel's inotify subsystem
*/
#include
#include
#include
#include
#include
#include
#include
#ifndef MAXIMUM_LENGHT
#define MAXIMUM_LENGHT 255
#endif
#ifndef BUFFER
#define BUFFER (2048)
#endif
static void printEvent(struct inotify_event *);
int main(int argc, char *argv[]) {
int inotify_fd;
int watcher;
int watched;
char buffer[BUFFER];
ssize_t num_read;
char *ptr;
struct inotify_event *event;
if (argc < 2 || strcmp(argv[1], "--help") == 0 )
fprintf(stdout, "Usage: %s file|dir\n", argv[0]);
// ask for our inotify instance
inotify_fd = inotify_init();
// tell the kernel what file(s) are of interest (from cmd line)
for (watched = 1; watched < argc; watched++) {
/* Watch all events */
watcher = inotify_add_watch(inotify_fd, argv[watched], IN_ALL_EVENTS);
if (watcher == -1) {
fprintf(stderr,"error:inotify_add_watch");
exit(EXIT_FAILURE);
}
fprintf(stdout,"%s watched by %d\n", argv[watched], watcher);
}
// read events forever
for (;;) {
num_read = read(inotify_fd, buffer, BUFFER);
if (num_read == 0) {
fprintf(stderr,"read() from inotify fd returned 0!");
exit(EXIT_FAILURE);
}
if (num_read == -1) {
fprintf(stderr, "read error!");
exit(EXIT_FAILURE);
}
fprintf(stdout,"Read %ld bytes from inotify descriptor\n", (long) num_read);
/* Process all of the events in buffer returned by read() */
for (ptr = buffer; ptr < buffer + num_read; ) {
event = (struct inotify_event *) ptr;
printEvent(event);
/* go to next event */
ptr += sizeof(struct inotify_event) + event->len;
}
}
exit(EXIT_SUCCESS);
}
static void printEvent(struct inotify_event *e) {
// Do we have any "saved" information?
if (e->cookie > 0)
fprintf(stdout, "cookie = 4%d; ", e->cookie);
fprintf(stdout, "Inotify Bit Mask = ");
if (e->mask & IN_OPEN) fprintf(stdout, "IN_OPEN");
if (e->mask & IN_ISDIR) fprintf(stdout, "IN_ISDIR");
if (e->mask & IN_ACCESS) fprintf(stdout, "IN_ACCESS");
if (e->mask & IN_ATTRIB) fprintf(stdout, "IN_ATTRIB");
if (e->mask & IN_DELETE) fprintf(stdout, "IN_DELETE");
if (e->mask & IN_CREATE) fprintf(stdout, "IN_CREATE");
if (e->mask & IN_MODIFY) fprintf(stdout, "IN_MODIFY");
if (e->mask & IN_IGNORED) fprintf(stdout, "IN_IGNORED");
if (e->mask & IN_UNMOUNT) fprintf(stdout, "IN_UNMOUNT");
if (e->mask & IN_MOVED_TO) fprintf(stdout, "IN_MOVED_TO");
if (e->mask & IN_MOVE_SELF) fprintf(stdout, "IN_MOVE_SELF");
if (e->mask & IN_Q_OVERFLOW) fprintf(stdout, "IN_Q_OVERFLOW");
if (e->mask & IN_MOVED_FROM) fprintf(stdout, "IN_MOVED_FROM");
if (e->mask & IN_DELETE_SELF) fprintf(stdout, "IN_DELETE_SELF");
if (e->mask & IN_CLOSE_WRITE) fprintf(stdout, "IN_CLOSE_WRITE");
if (e->mask & IN_CLOSE_NOWRITE) fprintf(stdout, "IN_CLOSE_NOWRITE");
fprintf(stdout, "\n");
/* Is there a name? */
if ( e->len > 0)
fprintf(stdout, " name = %s\n", e->name);
To run this program, you just need to compile it
gcc -o inotify_test inotify_example.c
And fire it up against any file you want...
./inotify_test inotify_example.c
If you simply save that file being watched, you can see the program printing information regarding changes.
inotify_example.c watched by 1
Read 16 bytes from inotify descriptor
Inotify Bit Mask = IN_OPEN
Read 16 bytes from inotify descriptor
Inotify Bit Mask = IN_ACCESS
Read 16 bytes from inotify descriptor
Inotify Bit Mask = IN_CLOSE_NOWRITE
Read 32 bytes from inotify descriptor
Inotify Bit Mask = IN_MODIFY
Inotify Bit Mask = IN_OPEN
Read 16 bytes from inotify descriptor
Inotify Bit Mask = IN_MODIFY
Read 16 bytes from inotify descriptor
Inotify Bit Mask = IN_CLOSE_WRITE