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 <errno.h> // for errno
7 #include <time.h> // for time()
9 #define _XOPEN_SOURCE 500
10 #define __USE_XOPEN_EXTENDED
11 #include <ftw.h> /* for nftw, tree walker */
13 #include "pnd_notify.h"
14 #include "pnd_pathiter.h"
15 #include "pnd_logger.h"
18 int fd; // notify API file descriptor
21 static int notify_handle;
23 //static void pnd_notify_hookup ( int fd );
26 #define PND_INOTIFY_MASK IN_DELETE | IN_UNMOUNT \
27 | IN_DELETE_SELF | IN_MOVE_SELF \
28 | IN_MOVED_FROM | IN_MOVED_TO | IN_CLOSE_WRITE
30 #define PND_INOTIFY_MASK IN_ALL_EVENTS
33 pnd_notify_handle pnd_notify_init ( void ) {
43 p = malloc ( sizeof(pnd_notify_t) );
47 return ( NULL ); // uhh..
52 // setup some default watches
53 //pnd_notify_hookup ( fd );
58 void pnd_notify_shutdown ( pnd_notify_handle h ) {
59 pnd_notify_t *p = (pnd_notify_t*) h;
68 static int pnd_notify_callback ( const char *fpath, const struct stat *sb,
69 int typeflag, struct FTW *ftwbuf )
72 // only include directories
73 if ( ! ( typeflag & FTW_D ) ) {
74 return ( 0 ); // continue the tree walk
77 //printf ( "Implicitly watching dir '%s'\n", fpath );
79 inotify_add_watch ( notify_handle, fpath, PND_INOTIFY_MASK );
81 if ( pnd_log_do_buried_logging() ) {
82 pnd_log ( PND_LOG_DEFAULT, "notify callback: added watch on %s\n", fpath );
85 return ( 0 ); // continue the tree walk
88 void pnd_notify_watch_path ( pnd_notify_handle h, char *fullpath, unsigned int flags ) {
89 pnd_notify_t *p = (pnd_notify_t*) h;
91 inotify_add_watch ( p -> fd, fullpath, PND_INOTIFY_MASK );
93 if ( flags & PND_NOTIFY_RECURSE ) {
95 notify_handle = p -> fd;
97 nftw ( fullpath, // path to descend
98 pnd_notify_callback, // callback to do processing
99 10, // no more than X open fd's at once
100 FTW_PHYS ); // do not follow symlinks
110 static void pnd_notify_hookup ( int fd ) {
112 inotify_add_watch ( fd, "./testdata", IN_CREATE | IN_DELETE | IN_UNMOUNT );
113 inotify_add_watch ( fd, "/media", IN_CREATE | IN_DELETE | IN_UNMOUNT );
119 unsigned char pnd_notify_rediscover_p ( pnd_notify_handle h ) {
120 pnd_notify_t *p = (pnd_notify_t*) h;
126 // don't block for long..
128 //t.tv_usec = 0; //5000;
132 // only for our useful fd
134 FD_SET ( (p->fd), &rfds );
137 retcode = select ( (p->fd) + 1, &rfds, NULL, NULL, &t );
140 pnd_log ( 3, "ERROR: notify: select failed: %d\n", errno );
141 return ( 0 ); // hmm.. need a better error code handler
142 } else if ( retcode == 0 ) {
143 return ( 0 ); // timeout
146 if ( ! FD_ISSET ( (p->fd), &rfds ) ) {
150 // by this point, something must have happened on our watch
151 #define BINBUFLEN ( 100 * ( sizeof(struct inotify_event) + 30 ) ) /* approx 100 events in a shot? */
152 unsigned char binbuf [ BINBUFLEN ];
155 actuallen = read ( (p->fd), binbuf, BINBUFLEN );
157 if ( actuallen < 0 ) {
158 pnd_log ( 3, "ERROR: notify: read failed: %d\n", errno );
159 return ( 0 ); // error
160 } else if ( actuallen == 0 ) {
161 return ( 0 ); // nothing, or overflow, or .. whatever.
165 struct inotify_event *e;
167 while ( i < actuallen ) {
168 e = (struct inotify_event *) &binbuf [ i ];
173 if ( pnd_log_do_buried_logging() ) {
174 pnd_log ( PND_LOG_DEFAULT, "notify: Got event against '%s' [%u %x]\n", e -> name, e -> mask, e -> mask );
181 i += ( sizeof(struct inotify_event) + e -> len );
187 #define _INOTIFY_TEST_PATH "/tmp/.notifytest"
188 #define _INOTIFY_TEST_FILE "/tmp/.notifytest/foopanda"
189 static unsigned char _inotify_test_run ( void ) {
193 int wd; // watch-descriptor
196 fdt.fd = inotify_init();
199 return ( 0 ); // failed to init at all
203 mkdir ( _INOTIFY_TEST_PATH, 0777 ); // if it fails we assume it exists, which is fine
206 if ( ( wd = inotify_add_watch ( fdt.fd, _INOTIFY_TEST_PATH, IN_DELETE ) ) < 0 ) {
207 return ( 0 ); // couldn't watch dir
210 // sleep a sec, just to be safe; seems to dislike being called immediately sometimes
211 usleep ( 1000000 / 2 );
213 // create a temp file
215 if ( ! ( f = fopen ( _INOTIFY_TEST_FILE, "w" ) ) ) {
217 return ( 0 ); // couldn't create test file?!
222 // delete the temp file; this should trigger an event
223 if ( unlink ( _INOTIFY_TEST_FILE ) < 0 ) {
224 return ( 0 ); // couldn't rm a file? life is harsh.
227 // did we get anything?
229 s = pnd_notify_rediscover_p ( &fdt );
238 /* we've run into the issue where inotify returns that it is set up, but in
239 * fact is not doing anything; restarting the process repairs it.. so here
240 * we devise a wank that continually tests inotify until it responds, then
241 * returns knowing we're good
243 unsigned char pnd_notify_wait_until_ready ( unsigned int secs_timeout ) {
244 time_t start = time ( NULL );
246 while ( ( secs_timeout == 0 ) ||
247 ( time ( NULL ) - start < secs_timeout ) )
249 if ( _inotify_test_run() ) {
252 usleep ( 1000000 / 2 );
255 return ( 0 ); // fail
258 int pnd_notify_fd ( pnd_notify_handle h ) {
259 pnd_notify_t *p = (pnd_notify_t*) h;