Filter repetitive inotify events about the same file in C - 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;
}

Related

How to monitor the data in both source and destination folders?

I'm working on the file monitoring system project in which, first all the data will be copied to the destination folder then it should monitor the data in both source and destination folders. But unfortunately, it is just monitoring the data from the source folder.
I divided the inotify function into two parts because I used inotifyFunc1 to help me in copying the folders first. And once the data is copied then I used inotifyFunc2 to monitor the data in both folders. But as I said it is just monitoring the first source folder.
This code is big but I don't know how to make it understand in a short way.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#define STRING_LEN 200
#define MAX_EVENTS 1024
#define NAME_LEN 16
#define EVENT_SIZE (sizeof(struct inotify_event))
#define BUFFER_LEN (MAX_EVENTS * (EVENT_SIZE + NAME_LEN))
typedef struct{
int fd, wd, result, length;
uint32_t mask[2];
char path1[STRING_LEN], path2[STRING_LEN], cmd[STRING_LEN], option, buffer[BUFFER_LEN];
} monitoring;
monitoring monitor;
void sig_handler(int signal){
printf("\nThe program is closed\n");
inotify_rm_watch(monitor.fd, monitor.wd);
close(monitor.fd);
exit(0);
}
void inotifyFunc1(char *path1, uint32_t *maskPtr1){
monitor.fd = inotify_init();
if(fcntl(monitor.fd, F_SETFL, O_NONBLOCK)){
perror("inotify not initialized: ");
exit(0);
}
monitor.wd = inotify_add_watch(monitor.fd, path1, *maskPtr1);
if(monitor.wd < 0){
perror("Sorry");
exit(1);
}
}
void inotifyFunc2(char *path2, uint32_t *maskPtr2)
{
while(1)
{
int i = 0;
monitor.length = read(monitor.fd, monitor.buffer, BUFFER_LEN);
while(i<monitor.length){
struct inotify_event *event = (struct inotify_event *)&monitor.buffer[i];
if(event->len){
if(event->mask & *maskPtr2){
if(event->mask & IN_ISDIR){
printf("Directory is created\n");
break;
}
else{
printf("File is created\n");
break;
}
}
}
}
}
}
void monitoringSystem(char *pathname1, char *pathname2)
{
/* Closing inotify */
signal(SIGINT,sig_handler);
do
{
printf("Choose the source path: ");
scanf("%s", pathname1);
monitor.mask[0] = ENOENT;
inotifyFunc1(pathname1, &monitor.mask[0]);
printf("Choose the destination path: ");
scanf("%s", pathname2);
inotifyFunc1(pathname2, &monitor.mask[0]);
monitor.result = strcmp(pathname1, pathname2);
if(monitor.result == 0){
printf("Error: Both locations are the same\n");
exit(3);
}
else{
sprintf(monitor.cmd, "cp -r %s %s", pathname1, pathname2);
system(monitor.cmd);
printf("Data is copied from source to destination\n");
}
printf("\nBoth locations are being monitored\n");
monitor.mask[1] = IN_CREATE;
inotifyFunc1(pathname1, &monitor.mask[1]);
inotifyFunc2(pathname1, &monitor.mask[1]);
inotifyFunc1(pathname2, &monitor.mask[1]);
inotifyFunc2(pathname2, &monitor.mask[1]);
printf("Do you want to give location again? [y/n]: ");
scanf("%s", &monitor.option);
} while(monitor.option == 'y');
}
int main(int argc, char *argv[])
{
printf("DATA RECOVERY SYSTEM\n");
printf("WELCOME TO THE MAIN MENU\n\n");
monitoringSystem(monitor.path1, monitor.path2);
return 0;
}
void inotifyFunc1(char *path1, uint32_t *maskPtr1){
monitor.fd = inotify_init();
Your inotifyFunc1 function creates a brand new inotify instance and stores its handle in monitor.fd.
inotifyFunc1(pathname1, &monitor.mask[0]);
printf("Choose the destination path: ");
scanf("%s", pathname2);
inotifyFunc1(pathname2, &monitor.mask[0]);
But you call it twice. The second time, it again creates a new inotify instance and stores its handle in monitor.fd, leaking the old handle.
If you only want one inotify instance to watch more than one thing, you need to create only one inotify instance.
inotifyFunc1(pathname1, &monitor.mask[1]);
inotifyFunc2(pathname1, &monitor.mask[1]);
This also doesn't make sense. Why are you calling inotifyFunc1 again? If you thought it was already monitoring both locations, what do you think this accomplishes? If you didn't think it was already monitoring both locations, why is this after you say it is?
It's hard to explain what incorrect reasoning led you to call those functions because there are no comments in the code. But it makes no sense.
You want to:
Create only one inotify instance.
Add each location to it only once.

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 print name of newly created file(s) within a directory in C?

This code scans for newly created files within a directory, however where "%s" should contain the name of the new file(s) this does not occur.
I can imagine there are unnecessary pieces of code written here, however being quite unfamiliar with C I'm simply happy it compiles at this point (and actually recognizes new files) !
#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>
int main (int argc, char *argv[])
{
char target[FILENAME_MAX];
int result;
int fd;
int wd; /* watch descriptor */
const int event_size = sizeof(struct inotify_event);
const int buf_len = 1024 * (event_size + FILENAME_MAX);
fd = inotify_init();
if (fd < 0) {
perror("inotify_init");
}
wd = inotify_add_watch(fd, "/home/joe/Documents", IN_CREATE);
while (1) {
char buff[buf_len];
int no_of_events, count = 0;
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("The file %s has been created\n", target);
fflush(stdout);
}
}
count += event_size + event->len;
}
}
return 0;
}
You're printing out target when you get an event, however target is never modified.
The name of the created file is stored in event->name. That's what you want to print.
printf("The file %s has been created\n", event->name);

How to use inotify in 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
}

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