How to use inotify in C? - c

I searched for questions related to inotify, and this one is somewhat different...
I use the following code to monitor change of one file (not directory). In testing, the read() does return when I save the target file which means it works. But event->mask is 32768 which is not IN_MODIFY and name is empty. Another issue: it cannot monitor continuously. When I change the file the second time, it has no response. Thank you for the help!
#include <sys/inotify.h>
#include <unistd.h>
#include <stdio.h>
#define EVENT_SIZE (sizeof (struct inotify_event))
#define BUF_LEN (16 * (EVENT_SIZE + 16))
int main()
{
int fd;
fd = inotify_init();
if (fd < 0)
perror("inotify_init()");
int wd;
wd = inotify_add_watch(fd, "target.txt", IN_MODIFY);
if (wd < 0)
perror("inotify_add_watch");
char buf[BUF_LEN];
int len;
start:
len = read(fd, buf, BUF_LEN);
if (len > 0)
{
int i = 0;
while (i < len)
{
struct inotify_event *event;
event = (struct inotify_event *) &buf[i];
printf("wd=%d mask=%x cookie=%u len=%u\n",
event->wd, event->mask,
event->cookie, event->len);
if (event->mask & IN_MODIFY)
printf("file modified %s", event->name);
if (event->len)
printf("name=%s\n", event->name);
i += EVENT_SIZE + event->len;
}
}
goto start;
return 0;
}

The 0x8000 corresponds to IN_IGNORED. Its presence in the mask indicates that the inotify watch had been removed because the file had been removed. Your editor probably removed the old file and put a new file in its place. Changing the file a second time had no effect because the watch had been removed.
The name is not being returned because you are not watching a directory.
From the inotify man page.
The name field is only present when an event is returned for a file inside a watched directory; it identifies the file pathname relative to the watched directory.
...
IN_IGNORED -- Watch was removed explicitly (inotify_rm_watch(2)) or automatically (file was deleted, or file system was unmounted).

event->mask 32768 is equivalent to 0x8000 that is IN_IGNORED
For more information : "/usr/include/linux/inotify.h"
if (event->mask & IN_IGNORED) {
/*Remove watch*/ inotify_rm_watch(fileDescriptor,watchDescriptor)
/*Add watch again*/ inotify_add_watch
}

Related

Filter repetitive inotify events about the same file in C

I'm developing a basic dropbox program. Very simple. I'm kinda stuck with the inotify thing. I need events that notifies about file creation, modification and deletion. I also need to filter out temporary files. I assume I need to be careful to not filter out truthful events, like if a user modifies the same file two or more times in a row.
If a modify a simple text file, I get three separate events: IN_CLOSE_WRITE test.c, IN_MOVED_TO test.c and IN_CLOSE_WRITE test.c. My expected result would be a single notify event about this file. Either one it's fine. All of them sends the same signal to server, to upload the file.
If I copy a file 'test.c': IN_CREATE test (copy).c and IN_CLOSE_WRITE test (copy).c. See the example above for the expected result.
Here's my code so far.
#include <stdio.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define EVENT_BUF_LEN 4096
#define EVENT_SIZE sizeof(struct inotify_event)
typedef struct
{
int length, fd, wd;
char buffer[EVENT_BUF_LEN] __attribute__((aligned(__alignof__(struct inotify_event))));
} notification;
notification inotify;
void getNotified(char *pathname)
{
char command[256];
notification inotify;
inotify.fd = inotify_init();
inotify.wd = inotify_add_watch(inotify.fd, pathname, IN_CLOSE_WRITE | IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM);
while (1)
{
inotify.length = read(inotify.fd, inotify.buffer, EVENT_BUF_LEN);
int i = 0;
while (i < inotify.length)
{
struct inotify_event *event = (struct inotify_event *)&inotify.buffer[i];
// Filtering out temporary files.
if (strstr(event->name, ".gout") != NULL)
{
i += (EVENT_SIZE + event->len);
continue;
}
if (event->mask & IN_CLOSE_WRITE)
strcpy(command, "IN_CLOSE_WRITE ");
else if (event->mask & IN_CREATE)
strcpy(command, "IN_CREATE ");
else if (event->mask & IN_MOVED_FROM)
strcpy(command, "IN_MOVED_FROM ");
else if (event->mask & IN_MOVED_TO)
strcpy(command, "IN_MOVED_TO ");
strcat(command, event->name);
printf("%s\n", command);
i += (EVENT_SIZE + event->len);
}
}
inotify_rm_watch(inotify.fd, inotify.wd);
close(inotify.fd);
}
int main(void)
{
getNotified(".");
return 0;
}

Hashing program not returning identical values for the same file

This hashing function I've created (that scans for new files, and computes their hashes) seemingly functions, however once removing a file, for example test.c, and then replacing it with the exact same file it returns 2 different hash values. By this I mean that whilst the program is running the first calculation might return a hash of 1234, for example, and once deleting and placing the same file within the folder the it then returns 2345.
There seems to be no order, as 1234 could be the result 5 times in a row. I wondered whether there's any strikingly obvious reason in this code?
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/inotify.h>
#include <openssl/sha.h>
int main (int argc, char *argv[])
{
int fd;
unsigned char c[SHA512_DIGEST_LENGTH];
int i;
SHA512_CTX mdContext;
int bytes;
unsigned char data[1024];
const int event_size = sizeof(struct inotify_event);
const int buf_len = 1024 * (event_size + FILENAME_MAX);
char *directory = "/home/joe/Documents/";
char *hashDirectory = "/home/joe/Documents/_Hash/";
char hashInBuf[100];
char hashOutBuf[100];
fd = inotify_init();
if (fd < 0) {
perror("inotify_init");
}
while (1) {
char buff[buf_len];
int no_of_events, count = 0;
//SEARCH FOR NEW FILES WITHIN DIRECTORY
no_of_events = read (fd, buff, buf_len);
while (count < no_of_events) {
struct inotify_event *event = (struct inotify_event *)&buff[count];
if (event->len) {
if ((event->mask & IN_CREATE))
if(!(event->mask & IN_ISDIR)) {
printf("\n%s has been created\n", event->name);
//CONJOIN DIRECTORY AND FILENAME / EXTENSION
snprintf(hashInBuf, sizeof(hashInBuf), "%s/%s", directory, event->name);
snprintf(hashOutBuf, sizeof(hashOutBuf), "%s/%s.txt", hashDirectory, event->name);
FILE *ftest=fopen(hashInBuf, "rb");
FILE *ftest2=fopen(hashOutBuf, "wt");
//HASH FUNCTION
SHA512_Init (&mdContext);
while ((bytes = fread (data, 1, 1024, ftest)) != 0)
SHA512_Update (&mdContext, data, bytes);
SHA512_Final (c,&mdContext);
for(i = 0; i < SHA512_DIGEST_LENGTH; i++){
fprintf(ftest2, "%02x", c[i]);
printf("%02x", c[i]);
}
fclose (ftest);
fclose (ftest2);
fflush (stdout);
}
}
count += event_size + event->len;
}
}
return 0;
}
Thank you in advance!
In this line
if ((event->mask & IN_CREATE))
you wait for the event that a file is created. Then, your hashing function immediately starts running!
This may lead to the situation that the file is not fully written yet, so you only hashed a part of the file.
You should use the event IN_CLOSE_WRITE to make sure, that the file has already been completely written.
Another option is to not create the files in this directory, but creating them in a temporary directory and subsequently moving them into the target directory. The corresponding event is IN_MOVED_TO then.

How to watch the directory with multiple files changes using inotify in c

I have a some 50 files which is moved to a directory "/tmp" on some interval of time when its get modified. I am using inotify to watch this directory /tmp for these files moved to this directory so that I can merge these files to another file in another directory.
But the rate at which the files getting moved to this directory ("/tmp"), inotify is not able to give notifications for other files except one file.
How to watch the directory if multiple files with different names (unknown names) being created or moved to the directory using inotify.
I know I can create multiple watch discriptors for each file with its name .
But I dont know the file name which is getting created or moved to this directory. Dynamically the files gets created so I cannot create watch descriptors for each file.
Below is my code.
How can I check notifications for the multiple files gettign created in this directory.
Please help with the solution.
Your help is highly appreciated.
Thanks
int main( int argc, char **argv )
{
int length, i = 0;
int fd;
int wd;
char buffer[BUF_LEN];
fd = inotify_init();
if ( fd < 0 ) {
perror( "inotify_init" );
}
wd = inotify_add_watch( fd, "/tmp/", IN_MOVED_TO);
while (1){
struct inotify_event *event;
length = read( fd, buffer, BUF_LEN );
if ( length < 0 ) {
perror( "read" );
}
event = ( struct inotify_event * ) &buffer[ i ];
if ( event->len ) {
if ( event->mask & IN_CREATE || IN_MOVED_TO) {
printf( "The file %s was created.\n", event->name );
}
}
}
( void ) inotify_rm_watch( fd, wd );
( void ) close( fd );
exit( 0 );
}
To monitor a directory for file creation or deletion, you can create an inotify instance and monitor with the flags IN_CREATE | IN_DELETE. To monitor a file or directory, you first create the inotify instance with inotify_init which will return a file descriptor. You can then add files/directories to monitor with inotify_add_watch and supply the proper flags to look for the desired changes. You can then simply use read which will block until changes meeting you criteria are detected.
A simple example to monitor a directory (given as input as the first argument [./tmp default]) is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/inotify.h>
#define EVENT_SIZE ( sizeof (struct inotify_event) )
#define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
int dir_exists (char *d);
int main (int argc, char **argv)
{
int length, i = 0;
int fd;
int wd;
char buffer[EVENT_BUF_LEN];
char *path = argc > 1 ? argv[1] : "./tmp";
/* check directory to monitor exists */
if (!dir_exists (path)) {
fprintf (stderr, "error: directory does not exist '%s'.\n", path);
return 1;
}
/* create inotify instance & validate */
if ((fd = inotify_init ()) < 0) {
perror ("inotify_init");
}
/* add path to inotify watch list monitor for file create, delete.
add IN_MOVED_FROM | IN_MOVED_TO or move to/from directory */
wd = inotify_add_watch (fd, path, IN_CREATE | IN_DELETE);
/* monitor path for new file creation or deletion
(read blocks until the change event occurs) */
if ((length = read (fd, buffer, EVENT_BUF_LEN)) < 0) {
perror ("read");
}
/* report name of file created or deleted */
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 ("New directory %s created.\n", event->name);
} else {
printf ("New file %s created.\n", event->name);
}
} else if (event->mask & IN_DELETE) {
if (event->mask & IN_ISDIR) {
printf ("Directory %s deleted.\n", event->name);
} else {
printf ("File %s deleted.\n", event->name);
}
}
}
i += EVENT_SIZE + event->len;
}
/* remove monitoring of path from the watch list. */
inotify_rm_watch (fd, wd);
/* close the inotify instance */
close (fd);
return 0;
}
/** test that directory exists (>=1 success, 0 otherwise)
* NOTE: no directory is actually created. fail occurs instead.
*/
int dir_exists (char *d)
{
int flags = O_DIRECTORY | O_RDONLY;
int mode = S_IRUSR | S_IWUSR;
int fd = open (d, flags, mode);
if (fd < 0) /* directory does not exist */
return 0;
else if (fd) { /* directory exists, rtn fd */
close (fd);
}
return fd;
}
Compile
gcc -Wall -Wextra -o bin/inotify_watch inotify_watch.c
Example Use
$ ./bin/inotify_watch &
[1] 16916
$ touch tmp/file.txt
New file file.txt created.
[1]+ Done ./bin/inotify_watch
Use functions FindFirstFile and FindNextFile. For example, show this code:
#include <windows.h>
#include <stdio.h>
#include <sys/stat.h>
#include <io.h>
/*
HANDLE FindFirstFile
(
LPCTSTR lpFileName, // какой файл ищем, можно указывать маску *, ?
LPWIN32_FIND_DATA lpFindFileData // указатель на структуру с информацией
);*/
//В случае ошибке вернет INVALID_HANDLE_VALUE. Для продолжения поиска используется функция:
/*
BOOL FindNextFile
(
HANDLE hFindFile, // указатель на поиск
LPWIN32_FIND_DATA lpFindFileData // указатель на структуру с информацией
);*/
//Write this any address
#define DISK "C:\\"
void main()
{
WIN32_FIND_DATA FindFileData;
HANDLE hf;
struct stat st;
hf=FindFirstFile(DISK"*", &FindFileData);
if (hf!=INVALID_HANDLE_VALUE)
{
do
{
char s[MAX_PATH] = DISK;
int a;
strcat(s, FindFileData.cFileName);
stat(s, &st);
a = access(s, 04);
printf("%s\t\t%s\n", FindFileData.cFileName, st.st_mode & S_IFDIR ? "Directory" : (st.st_mode & S_IFREG ? "File" : "Other"));
}
while (FindNextFile(hf,&FindFileData)!=0);
FindClose(hf);
}
getchar();
}

inotify api stops working after reporting once or twice

I wanted to test inotify, so took a couple of examples from internet, modified it to learn various aspects, but failed as it didn't work like i wanted to. First i tried to watch over a directory where it worked pretty nicely.
So i extended that example for file with some modification, but it works for one time only and gets blocked on read function
#include <sys/inotify.h>
#include <unistd.h>
#include <stdio.h>
#define EVENT_SIZE (sizeof (struct inotify_event))
#define BUF_LEN (16 * (EVENT_SIZE + 16))
int main()
{
int fd;
fd = inotify_init();
if (fd < 0)
perror("inotify_init()");
int wd;
wd = inotify_add_watch(fd, "target", IN_CLOSE_WRITE);
if (wd < 0)
perror("inotify_add_watch");
char buf[BUF_LEN];
int len;
while(1) {
len = read(fd, buf, BUF_LEN);
printf("after read\n");
if (len > 0)
{
int i = 0;
while (i < len)
{
struct inotify_event *event;
event = (struct inotify_event *) &buf[i];
printf("wd=%d mask=%x cookie=%u len=%u\n",
event->wd, event->mask,
event->cookie, event->len);
if (event->mask & IN_MODIFY)
printf("file modified %s", event->name);
if (event->len)
printf("name=%s\n", event->name);
i += EVENT_SIZE + event->len;
}
}
}
return 0;
}
So, i shifted to select(), but here also, it works once,reports twice then stop reporting the changes.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/inotify.h>
#define EVENT_SIZE ( sizeof (struct inotify_event) )
#define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
int main( int argc, char **argv )
{
int length, i = 0;
int fd;
int wd;
char buffer[BUF_LEN];
struct timeval timeout;
fd = inotify_init();
if ( fd < 0 ) {
perror( "inotify_init" );
}
wd = inotify_add_watch( fd, "target",
IN_CLOSE_WRITE );
fd_set rfds,rfdss;
int ret;
/* zero-out the fd_set */
FD_ZERO (&rfds);
FD_ZERO (&rfdss);
FD_SET (fd, &rfds);
timeout.tv_sec = 5;
timeout.tv_usec = 0;
while(1){
printf("Before select\n");
//rfds = rfdss;
ret = select (fd + 1, &rfds, NULL, NULL, NULL);
printf("After Select\n");
timeout.tv_sec = 5;
timeout.tv_usec = 0;
if (ret < 0)
perror ("select");
else if (!ret){
}
/* timed out! */
else if (FD_ISSET (fd, &rfds)){
printf("file changed============\n");
length = read( fd, buffer, BUF_LEN );
}
}
( void ) inotify_rm_watch( fd, wd );
( void ) close( fd );
exit( 0 );
}
A bit of research showed that that the popular editors save it in different way.
Instead of overwriting that file directly, they actually make a temporary file and then replace original file with new temporary one. So what actually happens is the file which you were actually watching no longer exists and therefore any changes that would be making won't be reflected back.
Editors that actually follow this method are(many more may exist)
Gedit, Geany, vi
Editors that directly overwrite the file is(many more may exist)
nano
Hence even though code was correct, anomalous behavior of editor can be problematic
For the blocking read call, see: https://stackoverflow.com/a/914520/149111
You may also want to switch from IN_CLOSE_WRITE to IN_ALL_EVENTS to make sure you're not missing something; likely a delete:
It is a best practice to watch the dir that contains the file(s) of interest rather than the individual files, as this will consume fewer resources in the kernel. It will also allow you to observe "atomic" file replacement operations where the writer creates a temporary file on the file system (perhaps in the same dir), write to the temporary file and then at the end rename(2) it over the top of the original file.
This style of replacement ensures that observers of the file will only ever notice the complete content of the file and not a half-written version of it.

inotify file in C

I am trying to run an example of inotify in C..but it's not working.
I want to monitor modifications to a file (the file is tmp.cfg), but it doesn't work..I don't know if i'm running it correctly, because I understand how to monitor directories, but not a single file
Here´s the example:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <unistd.h>
#define EVENT_SIZE ( sizeof (struct inotify_event) )
#define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
int main( int argc, char **argv )
{
int length, i = 0;
int fd;
int wd;
char buffer[BUF_LEN];
fd = inotify_init();
if ( fd < 0 ) {
perror( "inotify_init" );
}
wd = inotify_add_watch( fd, "/home/name/tmp.cfg",
IN_MODIFY | IN_CREATE | IN_DELETE );
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->mask & IN_CREATE ) {
printf( "The file %s was created.\n", event->name );
}
else if ( event->mask & IN_DELETE ) {
printf( "The file %s was deleted.\n", event->name );
}
else if ( event->mask & IN_MODIFY ) {
printf( "The file %s was modified.\n", event->name );
}
i += EVENT_SIZE + event->len;
}
( void ) inotify_rm_watch( fd, wd );
( void ) close( fd );
return 0;
}
Once i run it, if i write something on the file and then save it, nothing happens.
i've tryed debugging it..the problem seems to be the if ( event->mask & IN_MODIFY ), as it doesn't recognize it as a modification
You have 2 issues. First, as far as I can tell, inotify does not really work on files - it needs directory name to watch.
Second, you missed if (event->len) { inside while loop.
This code works for me for creating, deleting and modifying files in current directory:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <unistd.h>
#define EVENT_SIZE (sizeof(struct inotify_event))
#define BUF_LEN (1024 * (EVENT_SIZE + 16))
int main(int argc, char **argv) {
int length, i = 0;
int fd;
int wd;
char buffer[BUF_LEN];
fd = inotify_init();
if (fd < 0) {
perror("inotify_init");
}
wd = inotify_add_watch(fd, ".",
IN_MODIFY | IN_CREATE | IN_DELETE);
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) {
printf("The file %s was created.\n", event->name);
} else if (event->mask & IN_DELETE) {
printf("The file %s was deleted.\n", event->name);
} else if (event->mask & IN_MODIFY) {
printf("The file %s was modified.\n", event->name);
}
}
i += EVENT_SIZE + event->len;
}
(void) inotify_rm_watch(fd, wd);
(void) close(fd);
return 0;
}
It doesn't work on a single file because, when we use a editor to modify file, the editor opens a copy of the file and when we save the edited version from the text editor, the existing file is deleted and a new file of the same name is created with the modifications.
When the old file is deleted, the watch created on that file becomes invalid and it is deleted automatically.
You can see the old file being replaced by the new file if you monitor the parent directory.
There are two ways to solve it, monitor the parent directory and print the message when modifications is done to the particular that you want to watch.
Else create a new watch on the file whenever modifications are made. When the old file is deleted, IN_DELETE_SELF event is triggered.
event->name will be non-empty only when you watch a directory, as it will contain the name of the file on which the event has occurred in the watched directory.
I think you're not using your user name, which is your home directory, and you're not checking the return of inotify_add_watch which probably fails:
"/home/name/tmp.cfg"
Edit: okay second problem, you shouldn't print name because
The name field is only present when an event is returned for a file
inside a watched directory;
Edit2: third problem, the file must exist before you run the program because you add a watch on the file, I suggest you check the error from inotify_add_watch
In watching a file, if the file is manipulated by an editor which you might do to edit it and create a change, it is likely to be doing some operations that results in the original file you asked to watch being deleted. Hence the notifications will stop if you only watch the one file.

Resources