The changes that you can track with Inotify could be the creation, deletion, or modification of directories and files. To use the Inotify API, you just need to include the header sys/inotify.h
in your C program.
Watches
A watch is at the core of Inotify. You use a watch to specify the directory or file you are interested in monitoring for events, and the events that you are interested in. For example, the watch function inotify_add_watch(fd, root, IN_CREATE | IN_MODIFY | IN_DELETE)
specifies that you want to monitor the directory specified in the string root
, and you want to monitor all file and directory creation, modification and deletion events, as specified by the watch mask, IN_CREATE | IN_MODIFY | IN_DELETE
in this directory. The valid watch masks that can be specified are found in linux/inotify.h
.
The return value of the above function is an integer that uniquely identifies this watch, and on error, a value of -1 is returned. fd
is the file descriptor that specifies the current Inotify instance. A watch is uniquely identified by the path name it is watching, so if you want to specify a different watch for the same path, you will have to call inotify_add_watch( )
with the same path, but with the new combination of events that you want to watch.
A simple notification program
The following C program shows a simple demo Inotify application, which watches a directory for file and directory creation, modification and deletion events:
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/inotify.h> #include <limits.h> #define MAX_EVENTS 1024 /*Max. number of events to process at one go*/ #define LEN_NAME 16 /*Assuming that the length of the filename won't exceed 16 bytes*/ #define EVENT_SIZE ( sizeof (struct inotify_event) ) /*size of one event*/ #define BUF_LEN ( MAX_EVENTS * ( EVENT_SIZE + LEN_NAME )) /*buffer to store the data of events*/ int main( int argc, char **argv ) { int length, i = 0, wd; int fd; char buffer[BUF_LEN]; /* Initialize Inotify*/ fd = inotify_init(); if ( fd < 0 ) { perror( "Couldn't initialize inotify"); } /* add watch to starting directory */ wd = inotify_add_watch(fd, argv[1], IN_CREATE | IN_MODIFY | IN_DELETE); if (wd == -1) { printf("Couldn't add watch to %s\n",argv[1]); } else { printf("Watching:: %s\n",argv[1]); } /* do it forever*/ while(1) { i = 0; length = read( fd, buffer, BUF_LEN ); if ( length < 0 ) { perror( "read" ); } while ( i < length ) { struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ]; if ( event->len ) { if ( event->mask & IN_CREATE) { if (event->mask & IN_ISDIR) printf( "The directory %s was Created.\n", event->name ); else printf( "The file %s was Created with WD %d\n", event->name, event->wd ); } if ( event->mask & IN_MODIFY) { if (event->mask & IN_ISDIR) printf( "The directory %s was modified.\n", event->name ); else printf( "The file %s was modified with WD %d\n", event->name, event->wd ); } if ( event->mask & IN_DELETE) { if (event->mask & IN_ISDIR) printf( "The directory %s was deleted.\n", event->name ); else printf( "The file %s was deleted with WD %d\n", event->name, event->wd ); } i += EVENT_SIZE + event->len; } } } /* Clean up*/ inotify_rm_watch( fd, wd ); close( fd ); return 0; }
Shown below is the sample output of compiling and running the above program:
$ gcc listing1.c $ ./a,.out /home/gene/ Watching:: /home/gene/ . . The file .xsession-errors was modified with WD 1 The file .xsession-errors was modified with WD 1 The file .xsession-errors was modified with WD 1 The directory foobar was Created.
The first step that you need to perform in your application is the initialisation of the Inotify instance using inotify_init( )
, which returns a file descriptor that will be used to add watches and read event data in the rest of the application.
Event data is returned as a structure variable of type struct inotify_event
(defined in linux/inotify.h
), which is read into a buffer using the standard read()
system call.
Finally, the watch is removed, and the Inotify instance is closed using the inotify_rm_watch()
and close()
function calls respectively.
Watching subdirectories
Unfortunately, there is no direct method to specify that you want all subdirectories under a particular directory to also be monitored. Hence, to watch subdirectories under a specified directory, you have to traverse each subdirectory and add a watch for each of them.
The following C program does the above, traversing only the subdirectories immediately under the specified directory, using functions from dirent.h
, and adds a watch for each. Subsequently, instead of printing events directly to stdout
, it logs it into a file, inotify_logger.log
.
/* Using Inotify to monitor the sub-dirs under the specifiied dir*/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/inotify.h> #include <dirent.h> #include <limits.h> #define MAX_LEN 1024 /*Path length for a directory*/ #define MAX_EVENTS 1024 /*Max. number of events to process at one go*/ #define LEN_NAME 16 /*Assuming that the length of the filename won't exceed 16 bytes*/ #define EVENT_SIZE ( sizeof (struct inotify_event) ) /*size of one event*/ #define BUF_LEN ( MAX_EVENTS * ( EVENT_SIZE + LEN_NAME )) /*buffer to store the data of events*/ /* Log file*/ FILE *fp_log; /* Add inotify watches to directories immediately under root * in addition to itself */ void add_watches(int fd, char *root) { int wd; char *abs_dir; struct dirent *entry; DIR *dp; dp = opendir(root); if (dp == NULL) { perror("Error opening the starting directory"); exit(0); } /* add watch to starting directory */ wd = inotify_add_watch(fd, root, IN_CREATE | IN_MODIFY | IN_DELETE); if (wd == -1) { fprintf(fp_log,"Couldn't add watch to %s\n",root); } else { printf("Watching:: %s\n",root); } /* Add watches to the Level 1 sub-dirs*/ abs_dir = (char *)malloc(MAX_LEN); while((entry = readdir(dp))) { /* if its a directory, add a watch*/ if (entry->d_type == DT_DIR) { strcpy(abs_dir,root); strcat(abs_dir,entry->d_name); wd = inotify_add_watch(fd, abs_dir, IN_CREATE | IN_MODIFY | IN_DELETE); if (wd == -1) printf("Couldn't add watch to the directory %s\n",abs_dir); else printf("Watching:: %s\n",abs_dir); } } closedir(dp); free(abs_dir); } /* Main routine*/ int main( int argc, char **argv ) { int length, i = 0; int fd; char buffer[BUF_LEN], root[MAX_LEN]; /*Check for supplied path to monitor*/ switch(argc) { case 1: printf("No directory specified. Will monitor the entire filesystem...\n\n"); strcpy(root,"/"); break; case 2: strcpy(root,argv[1]); if(root[strlen(root)-1]!='/') strcat(root,"/"); puts(root); break; default: printf("Ignoring all other arguments after the first\n"); } /* Set up logger*/ fp_log = fopen("inotify_logger.log","a"); if (fp_log == NULL) { printf("Error opening logger. All output will be redirected to the stdout\n"); fp_log = stdout; } fd = inotify_init(); if ( fd < 0 ) { perror( "Couldn't initialize inotify"); } /* Read the sub-directories at one level under argv[1] * and monitor them for access */ add_watches(fd,root); /* do it forever*/ while(1) { i = 0; length = read( fd, buffer, BUF_LEN ); if ( length < 0 ) { perror( "read" ); } /* Read the events*/ while ( i < length ) { struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ]; if ( event->len ) { if ( event->mask & IN_CREATE) { if (event->mask & IN_ISDIR) fprintf(fp_log,"%d DIR::%s CREATED\n", event->wd,event->name ); else fprintf(fp_log, "%d FILE::%s CREATED\n", event->wd, event->name); } if ( event->mask & IN_MODIFY) { if (event->mask & IN_ISDIR) fprintf(fp_log,"%d DIR::%s MODIFIED\n", event->wd,event->name ); else fprintf(fp_log,"%d FILE::%s MODIFIED\n", event->wd,event->name ); } if ( event->mask & IN_DELETE) { if (event->mask & IN_ISDIR) fprintf(fp_log,"%d DIR::%s DELETED\n", event->wd,event->name ); else fprintf(fp_log,"%d FILE::%s DELETED\n", event->wd,event->name ); } i += EVENT_SIZE + event->len; } } } /* Clean up*/ ( void ) close( fd ); return 0; }
Auto-compiling files
Now that you have a basic idea of how you can use Inotify in applications that may need to monitor the filesystem, let us do something fun, and possibly, quite useful. Using Inotify, let us monitor a directory, and whenever a C source file is created or modified in it, let’s invoke gcc
to compile the file automatically, and place the binaries in a separate bin/ directory under this directory. The source code of auto_compile.c
follows:
/* auto_compile.c*/ /* Automatic compilation of C code using Inotify * Assumptions: * argv[1]: where the source codes are stored * bin/ sub-directory: in which the binaries will be stored * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/inotify.h> #include <limits.h> #define MAX_EVENTS 1024 /*Max. number of events to process at one go*/ #define LEN_NAME 16 /*Assuming that the length of the filename won't exceed 16 bytes*/ #define EVENT_SIZE ( sizeof (struct inotify_event) ) /*size of one event*/ #define BUF_LEN ( MAX_EVENTS * ( EVENT_SIZE + LEN_NAME )) /*buffer to store the data of events*/ int main( int argc, char **argv ) { int length, i = 0, wd; int fd; char buffer[BUF_LEN],cur_dir[BUF_LEN]; char command[100],copy_cmd[100]; strcpy(command,"gcc -o "); /* Initialize Inotify*/ fd = inotify_init(); if ( fd < 0 ) { perror( "Couldn't initialize inotify"); } /* add watch to starting directory */ wd = inotify_add_watch(fd, argv[1], IN_CREATE | IN_MODIFY); if (wd == -1) { printf("Couldn't add watch to %s\n",argv[1]); } else { printf("Watching:: %s\n",argv[1]); } /* do it forever*/ while(1) { i = 0; length = read( fd, buffer, BUF_LEN ); if ( length < 0 ) { perror( "read" ); } while ( i < length ) { struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ]; if ( event->len ) { if ( event->mask & IN_CREATE) { if (event->mask & IN_ISDIR) printf( "The directory %s was Created.\n", event->name ); else { printf( "Compiling:: %s \n", event->name); /*save the current dir*/ getcwd(cur_dir,BUF_LEN); chdir(argv[1]); /* form string for gcc*/ strcat(command,"bin/"); strcat(command,event->name); strcat(command,".out "); strcat(command,event->name); /* execute gcc*/ system(command); /*change back to dir*/ chdir(cur_dir); strcpy(command,"gcc -o "); } } if ( event->mask & IN_MODIFY) { if (event->mask & IN_ISDIR) printf( "The directory %s was modified.\n", event->name ); else { printf( "Compiling:: %s \n", event->name); /*save the current dir*/ getcwd(cur_dir,BUF_LEN); chdir(argv[1]); /* form string for gcc*/ strcat(command,"bin/"); strcat(command,event->name); strcat(command,".out "); strcat(command,event->name); /* execute gcc*/ system(command); /*change back to dir*/ chdir(cur_dir); strcpy(command,"gcc -o "); } } i += EVENT_SIZE + event->len; } } } /* Clean up*/ inotify_rm_watch( fd, wd ); close( fd ); return 0; }
Given below is the output of compiling and running the above C program:
$ gcc auto_compile.c $ ./a.out /home/gene/codes/ Watching:: /home/gene/codes/ Compiling:: hello1.c Compiling:: hello2.c
Of course, you could easily modify the above code to compile/execute programs in other languages.
inotify-tools
inotify-tools is a C library, as well as a set of command-line programs providing a user-friendly interface for Inotify. One useful tool is inotifywatch
, which can be used to watch a directory, and is thus a more feature-rich version of our second exercise (the second program).
dnotify, inotify and fanotify
Inotify succeeded dnotify, and fanotify is slowly gathering momentum, having been introduced in kernel 2.6.36. See this LWN article for a description of it.
If you navigate to the kernel sources subdirectory under fs/notify
, you can see implementations of the above three coexisting in a possible state of harmony.
In this article, we have used a hands-on approach to study a very useful feature of the Linux kernel, and all in user-space. Though not a particularly new concept (in use since 2005), now you know how you can write applications that perform actions in response to filesystem events. If you program in a language other than C, there are APIs available for C++, Java, Python, Ruby, and even a node.js module! Here are a few suggestions for building upon the code given in this article:
- Extend
auto_compile.c
to be implemented as a file-manager extension, such as a Nautilus extension, so that whenever a new source file is created or modified in a designated directory, it automatically compiles it. - Code using Inotify will be most useful when implemented as a daemon process, such that it can run in the background without any user intervention required. Hence, the while(1) loop can be better implemented as a “wake-on” feature, such that at other times, it stays in a sleeping mode.
Not necessarily for Kernel Programmers. Its a Kernel feature with an easy to use userspace API.
Sorry But I want your help
http://t.in.com/ej3X
Go To Above Link & Like The Video PLzzzzz
I Want More Likes For This
Above That Video You Will Find Like Button Of Facebook Click On That & Share This & Tell Your Friends To Like It
SO PLZ GO TO ABOVE LINK WATCH VIDEO & LIKE IT
don’t spoil the forum..
[…] Dear Sir, I think you could use inotify(2) in achieving this. https://www.opensourceforu.com/2011/04/get…-with-inotify/ […]
How can save the last event ocurred in my file? Thanks!
What do you mean? You could monitor the file for *all* possible events and then see which was the most recent one..
What are you trying to do?
Hello,
When compiling your code above, I get:
fileMonitor.cpp: In function ‘int main(int, char**)’:
fileMonitor.cpp:41:42: error: ‘read’ was not declared in this scope
fileMonitor.cpp:79:13: error: ‘close’ was not declared in this scope
While compiling with:
g++ -o fileMonitor fileMonitor.cpp
Can you help me out?
Thanks
fileMonitor.cpp:41:42: error: ‘read’ was not declared in this scope
fileMonitor.cpp:79:13: error: ‘close’ was not declared in this scope
this is an error about declaretions. you only must to add at the begin of this code the correct library
#include
check on linux terminal or google man read or man close
I forgot to mention I am on Debian 7
I am having the same problem as well.
Karen
Hi, I am using first code of this page.
compiled as
#gcc test.c -test
#./test /root/test_notify_events/mic/
And from another terminal running
#echo “1” > /root/test_notify_events/mic/event
Every time I am getting log twice:
The file event was modified with WD 1
The file event was modified with WD 1
I am confused, why this is called twice on single change in file
Same here, this is little puzzling and potentially problematic when you want to perform some particular action each time there is a change, it seems as if the code would perform those actions twice…