2 #include <sys/inotify.h> // for INOTIFY duh
3 #include <stdio.h> // for stdio, NULL
4 #include <stdlib.h> // for malloc, etc
5 #include <unistd.h> // for close
6 #include <time.h> // for time()
8 #define _XOPEN_SOURCE 500
9 #define __USE_XOPEN_EXTENDED
10 #include <ftw.h> /* for nftw, tree walker */
12 #include "pnd_notify.h"
13 #include "pnd_pathiter.h"
14 #include "pnd_logger.h"
17 int fd; // notify API file descriptor
20 static int notify_handle;
22 //static void pnd_notify_hookup ( int fd );
25 #define PND_INOTIFY_MASK IN_CREATE | IN_DELETE | IN_UNMOUNT \
26 | IN_DELETE_SELF | IN_MOVE_SELF \
27 | IN_MOVED_FROM | IN_MOVED_TO | IN_CLOSE_WRITE
29 #define PND_INOTIFY_MASK IN_ALL_EVENTS
32 pnd_notify_handle pnd_notify_init ( void ) {
42 p = malloc ( sizeof(pnd_notify_t) );
46 return ( NULL ); // uhh..
51 // setup some default watches
52 //pnd_notify_hookup ( fd );
57 void pnd_notify_shutdown ( pnd_notify_handle h ) {
58 pnd_notify_t *p = (pnd_notify_t*) h;
67 static int pnd_notify_callback ( const char *fpath, const struct stat *sb,
68 int typeflag, struct FTW *ftwbuf )
71 // only include directories
72 if ( ! ( typeflag & FTW_D ) ) {
73 return ( 0 ); // continue the tree walk
76 //printf ( "Implicitly watching dir '%s'\n", fpath );
78 inotify_add_watch ( notify_handle, fpath, PND_INOTIFY_MASK );
80 if ( pnd_log_do_buried_logging() ) {
81 pnd_log ( PND_LOG_DEFAULT, "notify callback: added watch on %s\n", fpath );
84 return ( 0 ); // continue the tree walk
87 void pnd_notify_watch_path ( pnd_notify_handle h, char *fullpath, unsigned int flags ) {
88 pnd_notify_t *p = (pnd_notify_t*) h;
90 inotify_add_watch ( p -> fd, fullpath, PND_INOTIFY_MASK );
92 if ( flags & PND_NOTIFY_RECURSE ) {
94 notify_handle = p -> fd;
96 nftw ( fullpath, // path to descend
97 pnd_notify_callback, // callback to do processing
98 10, // no more than X open fd's at once
99 FTW_PHYS ); // do not follow symlinks
109 static void pnd_notify_hookup ( int fd ) {
111 inotify_add_watch ( fd, "./testdata", IN_CREATE | IN_DELETE | IN_UNMOUNT );
112 inotify_add_watch ( fd, "/media", IN_CREATE | IN_DELETE | IN_UNMOUNT );
118 unsigned char pnd_notify_rediscover_p ( pnd_notify_handle h ) {
119 pnd_notify_t *p = (pnd_notify_t*) h;
125 // don't block for long..
127 //t.tv_usec = 0; //5000;
131 // only for our useful fd
133 FD_SET ( (p->fd), &rfds );
136 retcode = select ( (p->fd) + 1, &rfds, NULL, NULL, &t );
139 return ( 0 ); // hmm.. need a better error code handler
140 } else if ( retcode == 0 ) {
141 return ( 0 ); // timeout
144 if ( ! FD_ISSET ( (p->fd), &rfds ) ) {
148 // by this point, something must have happened on our watch
149 #define BINBUFLEN ( 100 * ( sizeof(struct inotify_event) + 30 ) ) /* approx 100 events in a shot? */
150 unsigned char binbuf [ BINBUFLEN ];
153 actuallen = read ( (p->fd), binbuf, BINBUFLEN );
155 if ( actuallen < 0 ) {
156 return ( 0 ); // error
157 } else if ( actuallen == 0 ) {
158 return ( 0 ); // nothing, or overflow, or .. whatever.
162 struct inotify_event *e;
164 while ( i < actuallen ) {
165 e = (struct inotify_event *) &binbuf [ i ];
170 if ( pnd_log_do_buried_logging() ) {
171 pnd_log ( PND_LOG_DEFAULT, "notify: Got event against '%s' [%u %x]\n", e -> name, e -> mask, e -> mask );
178 i += ( sizeof(struct inotify_event) + e -> len );
184 #define _INOTIFY_TEST_PATH "/tmp/.notifytest"
185 #define _INOTIFY_TEST_FILE "/tmp/.notifytest/foopanda"
186 static unsigned char _inotify_test_run ( void ) {
190 int wd; // watch-descriptor
193 fdt.fd = inotify_init();
196 return ( 0 ); // failed to init at all
200 mkdir ( _INOTIFY_TEST_PATH, 0777 ); // if it fails we assume it exists, which is fine
203 if ( ( wd = inotify_add_watch ( fdt.fd, _INOTIFY_TEST_PATH, IN_DELETE ) ) < 0 ) {
204 return ( 0 ); // couldn't watch dir
207 // sleep a sec, just to be safe; seems to dislike being called immediately sometimes
208 usleep ( 1000000 / 2 );
210 // create a temp file
212 if ( ! ( f = fopen ( _INOTIFY_TEST_FILE, "w" ) ) ) {
214 return ( 0 ); // couldn't create test file?!
219 // delete the temp file; this should trigger an event
220 if ( unlink ( _INOTIFY_TEST_FILE ) < 0 ) {
221 return ( 0 ); // couldn't rm a file? life is harsh.
224 // did we get anything?
226 s = pnd_notify_rediscover_p ( &fdt );
235 /* we've run into the issue where inotify returns that it is set up, but in
236 * fact is not doing anything; restarting the process repairs it.. so here
237 * we devise a wank that continually tests inotify until it responds, then
238 * returns knowing we're good
240 unsigned char pnd_notify_wait_until_ready ( unsigned int secs_timeout ) {
241 time_t start = time ( NULL );
243 while ( ( secs_timeout == 0 ) ||
244 ( time ( NULL ) - start < secs_timeout ) )
246 if ( _inotify_test_run() ) {
249 usleep ( 1000000 / 2 );
252 return ( 0 ); // fail