Minor fixes for a couple 'uninitialized vars' that gcc here wasn't informing me of :)
[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 <time.h>           // for time()
7
8 #define _XOPEN_SOURCE 500
9 #define __USE_XOPEN_EXTENDED
10 #include <ftw.h> /* for nftw, tree walker */
11
12 #include "pnd_notify.h"
13 #include "pnd_pathiter.h"
14
15 typedef struct {
16   int fd;              // notify API file descriptor
17 } pnd_notify_t;
18
19 static int notify_handle;
20
21 //static void pnd_notify_hookup ( int fd );
22
23 #define PND_INOTIFY_MASK     IN_CREATE | IN_DELETE | IN_UNMOUNT \
24                              | IN_DELETE_SELF | IN_MOVE_SELF    \
25                              | IN_MOVED_FROM | IN_MOVED_TO
26
27 pnd_notify_handle pnd_notify_init ( void ) {
28   int fd;
29   pnd_notify_t *p;
30
31   fd = inotify_init();
32
33   if ( fd < 0 ) {
34     return ( NULL );
35   }
36
37   p = malloc ( sizeof(pnd_notify_t) );
38
39   if ( ! p ) {
40     close ( fd );
41     return ( NULL ); // uhh..
42   }
43
44   p -> fd = fd;
45
46   // setup some default watches
47   //pnd_notify_hookup ( fd );
48
49   return ( p );
50 }
51
52 void pnd_notify_shutdown ( pnd_notify_handle h ) {
53   pnd_notify_t *p = (pnd_notify_t*) h;
54
55   close ( p -> fd );
56
57   free ( p );
58
59   return;
60 }
61
62 static int pnd_notify_callback ( const char *fpath, const struct stat *sb,
63                                  int typeflag, struct FTW *ftwbuf )
64 {
65
66   // only include directories
67   if ( ! ( typeflag & FTW_D ) ) {
68     return ( 0 ); // continue the tree walk
69   }
70
71   //printf ( "Implicitly watching dir '%s'\n", fpath );
72
73   inotify_add_watch ( notify_handle, fpath, PND_INOTIFY_MASK );
74
75   return ( 0 ); // continue the tree walk
76 }
77
78 void pnd_notify_watch_path ( pnd_notify_handle h, char *fullpath, unsigned int flags ) {
79   pnd_notify_t *p = (pnd_notify_t*) h;
80
81 #if 1
82   inotify_add_watch ( p -> fd, fullpath, PND_INOTIFY_MASK );
83 #else
84   inotify_add_watch ( p -> fd, fullpath, IN_ALL_EVENTS );
85 #endif
86
87   if ( flags & PND_NOTIFY_RECURSE ) {
88
89     notify_handle = p -> fd;
90
91     nftw ( fullpath,             // path to descend
92            pnd_notify_callback,  // callback to do processing
93            10,                   // no more than X open fd's at once
94            FTW_PHYS );           // do not follow symlinks
95
96     notify_handle = -1;
97
98   } // recurse
99
100   return;
101 }
102
103 #if 0
104 static void pnd_notify_hookup ( int fd ) {
105
106   inotify_add_watch ( fd, "./testdata", IN_CREATE | IN_DELETE | IN_UNMOUNT );
107   inotify_add_watch ( fd, "/media", IN_CREATE | IN_DELETE | IN_UNMOUNT );
108
109   return;
110 }
111 #endif
112
113 unsigned char pnd_notify_rediscover_p ( pnd_notify_handle h ) {
114   pnd_notify_t *p = (pnd_notify_t*) h;
115
116   struct timeval t;
117   fd_set rfds;
118   int retcode;
119
120   // don't block for long..
121   //t.tv_sec = 1;
122   //t.tv_usec = 0; //5000;
123   t.tv_sec = 0;
124   t.tv_usec = 5000;
125
126   // only for our useful fd
127   FD_ZERO ( &rfds );
128   FD_SET ( (p->fd), &rfds );
129
130   // wait and test
131   retcode = select ( (p->fd) + 1, &rfds, NULL, NULL, &t );
132
133   if ( retcode < 0 ) {
134     return ( 0 ); // hmm.. need a better error code handler
135   } else if ( retcode == 0 ) {
136     return ( 0 ); // timeout
137   }
138
139   if ( ! FD_ISSET ( (p->fd), &rfds ) ) {
140     return ( 0 );
141   }
142
143   // by this point, something must have happened on our watch
144 #define BINBUFLEN ( 100 * ( sizeof(struct inotify_event) + 30 ) ) /* approx 100 events in a shot? */
145   unsigned char binbuf [ BINBUFLEN ];
146   int actuallen;
147
148   actuallen = read ( (p->fd), binbuf, BINBUFLEN );
149
150   if ( actuallen < 0 ) {
151     return ( 0 ); // error
152   } else if ( actuallen == 0 ) {
153     return ( 0 ); // nothing, or overflow, or .. whatever.
154   }
155
156   unsigned int i = 0;
157   struct inotify_event *e;
158
159   while ( i < actuallen ) {
160     e = (struct inotify_event *) &binbuf [ i ];
161
162     /* do it!
163      */
164
165     if ( e -> len ) {
166       //printf ( "Got event against '%s'\n", e -> name );
167     }
168
169     /* do it!
170      */
171
172     // next
173     i += ( sizeof(struct inotify_event) + e -> len );
174   } // while
175
176   return ( 1 );
177 }
178
179 #define _INOTIFY_TEST_PATH "/tmp/.notifytest"
180 #define _INOTIFY_TEST_FILE "/tmp/.notifytest/foopanda"
181 static unsigned char _inotify_test_run ( void ) {
182
183   // set up inotify
184   pnd_notify_t fdt;
185   int wd; // watch-descriptor
186
187   // set up inotify
188   fdt.fd = inotify_init();
189
190   if ( fdt.fd < 0 ) {
191     return ( 0 ); // failed to init at all
192   }
193
194   // make a temp dir
195   mkdir ( _INOTIFY_TEST_PATH, 0777 ); // if it fails we assume it exists, which is fine
196
197   // watch the dir
198   if ( ( wd = inotify_add_watch ( fdt.fd, _INOTIFY_TEST_PATH, IN_DELETE ) ) < 0 ) {
199     return ( 0 ); // couldn't watch dir
200   }
201
202   // sleep a sec, just to be safe; seems to dislike being called immediately sometimes
203   usleep ( 1000000 / 2 );
204
205   // create a temp file
206   FILE *f;
207   if ( ! ( f = fopen ( _INOTIFY_TEST_FILE, "w" ) ) ) {
208     close ( fdt.fd );
209     return ( 0 ); // couldn't create test file?!
210   }
211
212   fclose ( f );
213
214   // delete the temp file; this should trigger an event
215   if ( unlink ( _INOTIFY_TEST_FILE ) < 0 ) {
216     return ( 0 ); // couldn't rm a file? life is harsh.
217   }
218
219   // did we get anything?
220   unsigned char s;
221   s = pnd_notify_rediscover_p ( &fdt );
222
223   // ditch inotify
224   close ( fdt.fd );
225
226   // success
227   return ( s );
228 }
229
230 /* we've run into the issue where inotify returns that it is set up, but in
231  * fact is not doing anything; restarting the process repairs it.. so here
232  * we devise a wank that continually tests inotify until it responds, then
233  * returns knowing we're good
234  */
235 unsigned char pnd_notify_wait_until_ready ( unsigned int secs_timeout ) {
236   time_t start = time ( NULL );
237
238   while ( ( secs_timeout == 0 ) ||
239           ( time ( NULL ) - start < secs_timeout ) )
240   {
241     if ( _inotify_test_run() ) {
242       return ( 1 );
243     }
244     usleep ( 1000000 / 2 );
245   }
246
247   return ( 0 ); // fail
248 }