Bringing in patches from cpasjuste
[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
7 #define _XOPEN_SOURCE 500
8 #define __USE_XOPEN_EXTENDED
9 #include <ftw.h> /* for nftw, tree walker */
10
11 #include "pnd_notify.h"
12 #include "pnd_pathiter.h"
13
14 typedef struct {
15   int fd;              // notify API file descriptor
16 } pnd_notify_t;
17
18 static int notify_handle;
19
20 static void pnd_notify_hookup ( int fd );
21
22 #define PND_INOTIFY_MASK     IN_CREATE | IN_DELETE | IN_UNMOUNT \
23                              | IN_DELETE_SELF | IN_MOVE_SELF    \
24                              | IN_MOVED_FROM | IN_MOVED_TO
25
26 pnd_notify_handle pnd_notify_init ( void ) {
27   int fd;
28   pnd_notify_t *p;
29
30   fd = inotify_init();
31
32   if ( fd < 0 ) {
33     return ( NULL );
34   }
35
36   p = malloc ( sizeof(pnd_notify_t) );
37
38   if ( ! p ) {
39     close ( fd );
40     return ( NULL ); // uhh..
41   }
42
43   p -> fd = fd;
44
45   // setup some default watches
46   pnd_notify_hookup ( fd );
47
48   return ( p );
49 }
50
51 void pnd_notify_shutdown ( pnd_notify_handle h ) {
52   pnd_notify_t *p = (pnd_notify_t*) h;
53
54   close ( p -> fd );
55
56   free ( p );
57
58   return;
59 }
60
61 static int pnd_notify_callback ( const char *fpath, const struct stat *sb,
62                                  int typeflag, struct FTW *ftwbuf )
63 {
64
65   // only include directories
66   if ( ! ( typeflag & FTW_D ) ) {
67     return ( 0 ); // continue the tree walk
68   }
69
70   //printf ( "Implicitly watching dir '%s'\n", fpath );
71
72   inotify_add_watch ( notify_handle, fpath, PND_INOTIFY_MASK );
73
74   return ( 0 ); // continue the tree walk
75 }
76
77 void pnd_notify_watch_path ( pnd_notify_handle h, char *fullpath, unsigned int flags ) {
78   pnd_notify_t *p = (pnd_notify_t*) h;
79
80 #if 1
81   inotify_add_watch ( p -> fd, fullpath, PND_INOTIFY_MASK );
82 #else
83   inotify_add_watch ( p -> fd, fullpath, IN_ALL_EVENTS );
84 #endif
85
86   if ( flags & PND_NOTIFY_RECURSE ) {
87
88     notify_handle = p -> fd;
89
90     nftw ( fullpath,             // path to descend
91            pnd_notify_callback,  // callback to do processing
92            10,                   // no more than X open fd's at once
93            FTW_PHYS );           // do not follow symlinks
94
95     notify_handle = -1;
96
97   } // recurse
98
99   return;
100 }
101
102 static void pnd_notify_hookup ( int fd ) {
103
104   inotify_add_watch ( fd, "./testdata", IN_CREATE | IN_DELETE | IN_UNMOUNT );
105   inotify_add_watch ( fd, "/media", IN_CREATE | IN_DELETE | IN_UNMOUNT );
106
107   return;
108 }
109
110 unsigned char pnd_notify_rediscover_p ( pnd_notify_handle h ) {
111   pnd_notify_t *p = (pnd_notify_t*) h;
112
113   struct timeval t;
114   fd_set rfds;
115   int retcode;
116
117   // don't block for long..
118   //t.tv_sec = 1;
119   //t.tv_usec = 0; //5000;
120   t.tv_sec = 0;
121   t.tv_usec = 5000;
122
123   // only for our useful fd
124   FD_ZERO ( &rfds );
125   FD_SET ( (p->fd), &rfds );
126
127   // wait and test
128   retcode = select ( (p->fd) + 1, &rfds, NULL, NULL, &t );
129
130   if ( retcode < 0 ) {
131     return ( 0 ); // hmm.. need a better error code handler
132   } else if ( retcode == 0 ) {
133     return ( 0 ); // timeout
134   }
135
136   if ( ! FD_ISSET ( (p->fd), &rfds ) ) {
137     return ( 0 );
138   }
139
140   // by this point, something must have happened on our watch
141 #define BINBUFLEN ( 100 * ( sizeof(struct inotify_event) + 30 ) ) /* approx 100 events in a shot? */
142   unsigned char binbuf [ BINBUFLEN ];
143   int actuallen;
144
145   actuallen = read ( (p->fd), binbuf, BINBUFLEN );
146
147   if ( actuallen < 0 ) {
148     return ( 0 ); // error
149   } else if ( actuallen == 0 ) {
150     return ( 0 ); // nothing, or overflow, or .. whatever.
151   }
152
153   unsigned int i;
154   struct inotify_event *e;
155
156   while ( i < actuallen ) {
157     e = (struct inotify_event *) &binbuf [ i ];
158
159     /* do it!
160      */
161
162     if ( e -> len ) {
163       //printf ( "Got event against '%s'\n", e -> name );
164     }
165
166     /* do it!
167      */
168
169     // next
170     i += ( sizeof(struct inotify_event) + e -> len );
171   } // while
172
173   return ( 1 );
174 }