pndnotifyd: ignore IN_CREATE
[pandora-libraries.git] / lib / pnd_notify.c
1
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()
8
9 #define _XOPEN_SOURCE 500
10 #define __USE_XOPEN_EXTENDED
11 #include <ftw.h> /* for nftw, tree walker */
12
13 #include "pnd_notify.h"
14 #include "pnd_pathiter.h"
15 #include "pnd_logger.h"
16
17 typedef struct {
18   int fd;              // notify API file descriptor
19 } pnd_notify_t;
20
21 static int notify_handle;
22
23 //static void pnd_notify_hookup ( int fd );
24
25 #if 1
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
29 #else
30 #define PND_INOTIFY_MASK     IN_ALL_EVENTS
31 #endif
32
33 pnd_notify_handle pnd_notify_init ( void ) {
34   int fd;
35   pnd_notify_t *p;
36
37   fd = inotify_init();
38
39   if ( fd < 0 ) {
40     return ( NULL );
41   }
42
43   p = malloc ( sizeof(pnd_notify_t) );
44
45   if ( ! p ) {
46     close ( fd );
47     return ( NULL ); // uhh..
48   }
49
50   p -> fd = fd;
51
52   // setup some default watches
53   //pnd_notify_hookup ( fd );
54
55   return ( p );
56 }
57
58 void pnd_notify_shutdown ( pnd_notify_handle h ) {
59   pnd_notify_t *p = (pnd_notify_t*) h;
60
61   close ( p -> fd );
62
63   free ( p );
64
65   return;
66 }
67
68 static int pnd_notify_callback ( const char *fpath, const struct stat *sb,
69                                  int typeflag, struct FTW *ftwbuf )
70 {
71
72   // only include directories
73   if ( ! ( typeflag & FTW_D ) ) {
74     return ( 0 ); // continue the tree walk
75   }
76
77   //printf ( "Implicitly watching dir '%s'\n", fpath );
78
79   inotify_add_watch ( notify_handle, fpath, PND_INOTIFY_MASK );
80
81   if ( pnd_log_do_buried_logging() ) {
82     pnd_log ( PND_LOG_DEFAULT, "notify callback: added watch on %s\n", fpath );
83   }
84
85   return ( 0 ); // continue the tree walk
86 }
87
88 void pnd_notify_watch_path ( pnd_notify_handle h, char *fullpath, unsigned int flags ) {
89   pnd_notify_t *p = (pnd_notify_t*) h;
90
91   inotify_add_watch ( p -> fd, fullpath, PND_INOTIFY_MASK );
92
93   if ( flags & PND_NOTIFY_RECURSE ) {
94
95     notify_handle = p -> fd;
96
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
101
102     notify_handle = -1;
103
104   } // recurse
105
106   return;
107 }
108
109 #if 0
110 static void pnd_notify_hookup ( int fd ) {
111
112   inotify_add_watch ( fd, "./testdata", IN_CREATE | IN_DELETE | IN_UNMOUNT );
113   inotify_add_watch ( fd, "/media", IN_CREATE | IN_DELETE | IN_UNMOUNT );
114
115   return;
116 }
117 #endif
118
119 unsigned char pnd_notify_rediscover_p ( pnd_notify_handle h ) {
120   pnd_notify_t *p = (pnd_notify_t*) h;
121
122   struct timeval t;
123   fd_set rfds;
124   int retcode;
125
126   // don't block for long..
127   //t.tv_sec = 1;
128   //t.tv_usec = 0; //5000;
129   t.tv_sec = 0;
130   t.tv_usec = 5000;
131
132   // only for our useful fd
133   FD_ZERO ( &rfds );
134   FD_SET ( (p->fd), &rfds );
135
136   // wait and test
137   retcode = select ( (p->fd) + 1, &rfds, NULL, NULL, &t );
138
139   if ( retcode < 0 ) {
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
144   }
145
146   if ( ! FD_ISSET ( (p->fd), &rfds ) ) {
147     return ( 0 );
148   }
149
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 ];
153   int actuallen;
154
155   actuallen = read ( (p->fd), binbuf, BINBUFLEN );
156
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.
162   }
163
164   unsigned int i = 0;
165   struct inotify_event *e;
166
167   while ( i < actuallen ) {
168     e = (struct inotify_event *) &binbuf [ i ];
169
170     /* do it!
171      */
172
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 );
175     }
176
177     /* do it!
178      */
179
180     // next
181     i += ( sizeof(struct inotify_event) + e -> len );
182   } // while
183
184   return ( 1 );
185 }
186
187 #define _INOTIFY_TEST_PATH "/tmp/.notifytest"
188 #define _INOTIFY_TEST_FILE "/tmp/.notifytest/foopanda"
189 static unsigned char _inotify_test_run ( void ) {
190
191   // set up inotify
192   pnd_notify_t fdt;
193   int wd; // watch-descriptor
194
195   // set up inotify
196   fdt.fd = inotify_init();
197
198   if ( fdt.fd < 0 ) {
199     return ( 0 ); // failed to init at all
200   }
201
202   // make a temp dir
203   mkdir ( _INOTIFY_TEST_PATH, 0777 ); // if it fails we assume it exists, which is fine
204
205   // watch the dir
206   if ( ( wd = inotify_add_watch ( fdt.fd, _INOTIFY_TEST_PATH, IN_DELETE ) ) < 0 ) {
207     return ( 0 ); // couldn't watch dir
208   }
209
210   // sleep a sec, just to be safe; seems to dislike being called immediately sometimes
211   usleep ( 1000000 / 2 );
212
213   // create a temp file
214   FILE *f;
215   if ( ! ( f = fopen ( _INOTIFY_TEST_FILE, "w" ) ) ) {
216     close ( fdt.fd );
217     return ( 0 ); // couldn't create test file?!
218   }
219
220   fclose ( f );
221
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.
225   }
226
227   // did we get anything?
228   unsigned char s;
229   s = pnd_notify_rediscover_p ( &fdt );
230
231   // ditch inotify
232   close ( fdt.fd );
233
234   // success
235   return ( s );
236 }
237
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
242  */
243 unsigned char pnd_notify_wait_until_ready ( unsigned int secs_timeout ) {
244   time_t start = time ( NULL );
245
246   while ( ( secs_timeout == 0 ) ||
247           ( time ( NULL ) - start < secs_timeout ) )
248   {
249     if ( _inotify_test_run() ) {
250       return ( 1 );
251     }
252     usleep ( 1000000 / 2 );
253   }
254
255   return ( 0 ); // fail
256 }
257
258 int pnd_notify_fd ( pnd_notify_handle h ) {
259   pnd_notify_t *p = (pnd_notify_t*) h;
260   int r = -1;
261
262   if ( p )
263     r = p->fd;
264
265   return r;
266 }