8

i need to print events on a folder with multiple subfolders. how to do it recursivly? Please print a c++ code. I am stucked!! Every time the evet is poped i need to open the subfolder, take the file and copy it into another directory. I don't want to list all the subfolders in every 2 seconds and find the files if there are any. Is not efficient. I need to use a monitor folder. Please help

The director that i want to monitor has multiple subfolders. Each subfolder has another subfolder that could contain in a moment of time a file. MainFolder->Subfolders->each subfolder-> subfolder -> file.

Here is the code I have for he moment:

/*


*/
  #include <pthread.h>
    #include <unistd.h>

#include <iostream>
#include <sys/inotify.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <vector>
#include <string>
    #include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
using namespace std;
 vector<string> SS;



void *print_message_function( void *ptr );


int main(int argc, char **argv ){

  pthread_t t1;
    int fd,fd1,wd,wd1,i=0,i1=0,len=0,len1=0;
      int length;
    char pathname[100],buf[1024],buf1[1024];
   int data;
    struct inotify_event *event;
     char *message1 = "Thread 1";



   FILE *fr;
   // fd=inotify_init1(IN_NONBLOCK);//--rewrite
    fd = inotify_init();


    /* watch /test directory for any activity and report it back to me */
    wd=inotify_add_watch(fd,"/home/MainFoder/",IN_ALL_EVENTS);

  //  int flag=0;
   // char*ev="";
//wd=inotifytools_watch_recursively_with_exclude("/home/MainFolder/",IN_ALL_EVENTS);
 while(1)
{
//sleep(30);
        //read 1024  bytes of events from fd into buf

i=0;
        len=read(fd,buf,1024);
        while(i<len){

            event=(struct inotify_event *) &buf[i];


    /* watch /test directory for any activity and report it back to me */


            /* check for changes */
              {
            if((event->mask & IN_OPEN) ||(event->mask & IN_CREATE))

             {  


                 printf("\n %s :was opened\n",event->name);
                SS.push_back(event->name);



             }

       }
            /* update index to start of next event */
            i+=sizeof(struct inotify_event)+event->len;
        }

         vector<string>::const_iterator cii;
for(cii=SS.begin(); cii!=SS.end(); cii++)
       {


wd1 = watch_from_filename(*ci);
}
/*
vector<string>::const_iterator cii;
       for(cii=SS.begin(); cii!=SS.end(); cii++)
       {
          cout <<"HERE:"<< *cii << endl;
       }
*/
int  iret1, iret2;

    /* Create independent threads each of which will execute function */

     iret1 = pthread_create( &t1, NULL, print_message_function, (void*) message1);

}

}
void *print_message_function( void *ptr )
{
    vector<string>::const_iterator cii;
       for(cii=SS.begin(); cii!=SS.end(); cii++)
       {

          cout <<"HERE:"<< *cii << endl;
          std::string path=exec

       }
}
user1165435
  • 231
  • 2
  • 4
  • 11

5 Answers5

5

This working sample on Github does what you're looking for: inotify-example.cpp

On CREATE events, the current wd (watch descriptor), plus the inotify_event wd and name components, are added to a Watch object (see sample). The class includes methods to lookup wd and names in several ways.

This snippet shows how CREATE/DELETE events are handled:

            if ( event->mask & IN_CREATE ) {
                current_dir = watch.get(event->wd);
                if ( event->mask & IN_ISDIR ) {
                    new_dir = current_dir + "/" + event->name;
                    wd = inotify_add_watch( fd, new_dir.c_str(), WATCH_FLAGS );
                    watch.insert( event->wd, event->name, wd );
                    total_dir_events++;
                    printf( "New directory %s created.\n", new_dir.c_str() );
                } else {
                    total_file_events++;
                    printf( "New file %s/%s created.\n", current_dir.c_str(), event->name );
                }
            } else if ( event->mask & IN_DELETE ) {
                if ( event->mask & IN_ISDIR ) {
                    new_dir = watch.erase( event->wd, event->name, &wd );
                    inotify_rm_watch( fd, wd );
                    total_dir_events--;
                    printf( "Directory %s deleted.\n", new_dir.c_str() );
                } else {
                    current_dir = watch.get(event->wd);
                    total_file_events--;
                    printf( "File %s/%s deleted.\n", current_dir.c_str(), event->name );
                }
            }
Peter Krnjevic
  • 1,070
  • 15
  • 20
  • 2
    I think there is still a hole here. If watching A and then A/B and A/B/C are quickly added, by the time you process the notification for A/B (and added B to the watch) C has already been created and you missed its create event since you were not yet listening on B. Then you miss any events that come under C. – ribram Oct 17 '13 at 22:50
  • Agreed. It is impossible to add directory watches quickly enough to guarantee subsequent file events won't be lost. – Peter Krnjevic Oct 18 '13 at 02:33
  • So far this is the best approach I've seen using inotify. Please share if you have something better. There are more robust ways to track file changes via Journals on Windows and OS X (and I believe *nix too), but OP specifically asked about inotify. – Peter Krnjevic Oct 18 '13 at 02:41
  • 1
    Agreed Peter that this approach is best for inotify. The only solution I know of is that when the consumer of the watcher receive a directory create event, the consumer must scan that dir recursively and submit any child subdirs it finds back to the watcher to ensure that the watcher has a handle open for them. An unfortunate side effect for not being able to specify a recursive watch atomically, but as you said this is an unfortunate artifact of the inotify implementation. – ribram Oct 18 '13 at 15:24
  • You're quite correct that subsequently scanning newly created folders does increase reliability, but also adds overhead (and is not included in my sample). If the OP needed that level of reliability and hadn't specifically mentioned inotify, I would have recommended fanotify as it is robust and does not lose creation events. – Peter Krnjevic Oct 18 '13 at 18:43
  • I believe that fanotify does not report delete and rename events however (is that correct?) in case those events are important for your application that is yet another consideration :-) – ribram Oct 18 '13 at 22:03
  • It's been a while, but I think delete and rename show up as modification events on the containing directory. For details you have to scan the directory. Not ideal, but still robust. Personally, I'd love to see something like OSX's super-easy FSEvents on Linux. Perhaps you have some free time, ribram? ;-) – Peter Krnjevic Oct 18 '13 at 22:33
1

I have written the code for you. Now, you have to do only one change in this code. Just give path of your directory in main function.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <limits.h>
#include<sys/stat.h>
#include<dirent.h>
#include<time.h>
#include<string.h>
#include<unistd.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*/

void monitor(char *);
int evnt_mon(char *); 




void main()
{
    if(fork()==0)
    evnt_mon("./usssb");// give path of your directory which you want to monitor
    monitor("./usssb");// give path of your directory which you want to monitor
    while(1);
}

void monitor(char * rt_dir)
{

    struct stat st;
    DIR *dirp; 
    struct dirent *dp;
    char str[100][100]={ };
    char temp[100];
    char str1[500]=" ";
    int i=0,j=0,src_ret=9,src_ret1=9;
    strcpy(str1,rt_dir);
    dirp=opendir(str1);
    if(dirp==NULL)
    {
        perror("opendir");
        return;
    }

    while(1)
    {
        dp=readdir(dirp);
        if(dp==NULL)
        break;
        if((strcmp(dp->d_name,".\0")==0) || (strcmp(dp->d_name,"..")==0))
        continue;   

        if((dp->d_type==DT_DIR)&&((strcmp(dp->d_name,".")!=0)&&(strcmp(dp->d_name,"..")!=0)))
        {   
            strcat(str[i],str1);
            strcat(str[i],"/");
            strcat(str[i],dp->d_name);
            if(fork()==0)   
            {
                evnt_mon(str[i]);
            }
            i++;
        }

    }

    closedir(dirp);
    if(i>0)
    {
        for(j=0;j<i;j++)
        {
            monitor(str[j]);    
        }
    }

}




int evnt_mon(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, IN_CREATE | IN_MODIFY | IN_DELETE); 

    if (wd == -1)
    {
        printf("Couldn't add watch to %s\n",argv);
    }
    else
    {
        printf("Watching:: %s\n",argv);
    }

    /* 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 in %s.\n", event->name,argv );    


                        if(fork()==0)
                        {
                            char p[100]=" ";
                            strcpy(p,argv);
                            strcat(p,"/");
                            strcat(p,event->name);
                            evnt_mon(p);
                        }

                    }                       
                    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 from %s.\n", event->name,argv );       
                            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;
}
Pang
  • 9,564
  • 146
  • 81
  • 122
Akatsuki
  • 282
  • 1
  • 12
  • it is good but there are some bug, like: if file was shift+delete then detected but if only delete which move's file into trash was not indecated, and if we remove that file from trash then no message indication. – Rupesh Yadav. Jan 13 '15 at 09:18
  • @Rupesh Yadav,you are absolutely right but this code is written for beaglebone in which we use rm command to delete it.But I will update it & try to resolve this.Thanks for notify. – Akatsuki Jan 13 '15 at 09:30
  • 1
    The `while(1);` ending `main` is a poor idea. Replace it with [pause(2)](http://man7.org/linux/man-pages/man2/pause.2.html) – Basile Starynkevitch Apr 09 '18 at 05:15
1

You might use the fanotify API. It allows you to monitor a complete mount. The only drawback is that you need to be root.

Xypron
  • 2,215
  • 1
  • 12
  • 24
1

You can do it in two steps:

  1. Detect all the changes you're interested in on the root directory, plus (if not already included) creations (IN_CREATE).
  2. If the creation is a directory, do the whole algorithm on it.
Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
  • 1
    i've post a code. please tell me how to change it? If te first subfolder is created i need notify for subfiles – user1165435 Feb 05 '12 at 08:11
  • @user1165435, first fix your formatting. As is, it's hard to read. Use consistent spacing and brace style. Beyond that, try to implement the steps above and tell us if you get stuck. – Matthew Flaschen Feb 05 '12 at 08:18
  • @MatthewFlaschen Thanks for the tip, I was actually looking for the same thing and your suggestion is quite easy and totally makes sense. When a folder is created just watch() that folder too... – Logan Jul 09 '12 at 21:23
0

To address the problem stated by ribram (the 'hole':)). one possible solution i can think of is that we can do a combination of 'polling the directory' and 'using inotify'... i.e. Each time a directory is detected (directory only, don't do it for files):

  • add a watchpoint for the newly detected directory to inotify
  • 'poll' (or 'scan') the newly detected directory (man readdir()) to see if there're already items (files, directories) created. Those are possibly the ones that are missing.

Note that to build an 'air-tight' case, the above steps' order is important. you need to add the watchpoint first than scan ... This will guarantee that an item is picked up by either 'scan' or inotify or both. In that case you may also need to aware of the dups. i.e. the same item can be both yielded by the scan and the inotify