-rw-r--r-- | libopie2/opiecore/ofilenotify.cpp | 102 | ||||
-rw-r--r-- | libopie2/opiecore/ofilenotify.h | 4 |
2 files changed, 93 insertions, 13 deletions
diff --git a/libopie2/opiecore/ofilenotify.cpp b/libopie2/opiecore/ofilenotify.cpp index bcfb1aa..de4c63b 100644 --- a/libopie2/opiecore/ofilenotify.cpp +++ b/libopie2/opiecore/ofilenotify.cpp | |||
@@ -19,203 +19,281 @@ _;:, .> :=|. This program is free software; you can | |||
19 | ++= -. .` .: details. | 19 | ++= -. .` .: details. |
20 | : = ...= . :.=- | 20 | : = ...= . :.=- |
21 | -. .:....=;==+<; You should have received a copy of the GNU | 21 | -. .:....=;==+<; You should have received a copy of the GNU |
22 | -_. . . )=. = Library General Public License along with | 22 | -_. . . )=. = Library General Public License along with |
23 | -- :-=` this library; see the file COPYING.LIB. | 23 | -- :-=` this library; see the file COPYING.LIB. |
24 | If not, write to the Free Software Foundation, | 24 | If not, write to the Free Software Foundation, |
25 | Inc., 59 Temple Place - Suite 330, | 25 | Inc., 59 Temple Place - Suite 330, |
26 | Boston, MA 02111-1307, USA. | 26 | Boston, MA 02111-1307, USA. |
27 | */ | 27 | */ |
28 | 28 | ||
29 | #include "ofilenotify.h" | 29 | #include "ofilenotify.h" |
30 | using namespace Opie::Core; | 30 | using namespace Opie::Core; |
31 | 31 | ||
32 | /* OPIE */ | 32 | /* OPIE */ |
33 | 33 | ||
34 | /* QT */ | 34 | /* QT */ |
35 | #include <qobject.h> | 35 | #include <qobject.h> |
36 | #include <qsignal.h> | 36 | #include <qsignal.h> |
37 | #include <qintdict.h> | 37 | #include <qintdict.h> |
38 | #include <qdir.h> | 38 | #include <qdir.h> |
39 | 39 | ||
40 | /* STD */ | 40 | /* STD */ |
41 | #include <sys/types.h> | 41 | #include <sys/types.h> |
42 | #include <sys/stat.h> | 42 | #include <sys/stat.h> |
43 | #include <assert.h> | ||
43 | #include <fcntl.h> | 44 | #include <fcntl.h> |
44 | #include <string.h> | 45 | #include <string.h> |
45 | #include <errno.h> | 46 | #include <errno.h> |
46 | #include <unistd.h> | 47 | #include <unistd.h> |
47 | 48 | ||
48 | static QIntDict<OFileNotification> notification_list; | 49 | static QIntDict<OFileNotification> notification_list; |
49 | 50 | ||
50 | namespace Opie { | 51 | namespace Opie { |
51 | namespace Core { | 52 | namespace Core { |
52 | 53 | ||
53 | OFileNotification::OFileNotification( QObject* parent, const char* name ) | 54 | OFileNotification::OFileNotification( QObject* parent, const char* name ) |
54 | :QObject( parent, name ), _active( false ) | 55 | :QObject( parent, name ), _active( false ) |
55 | { | 56 | { |
56 | qDebug( "OFileNotification::OFileNotification()" ); | 57 | qDebug( "OFileNotification::OFileNotification()" ); |
57 | } | 58 | } |
58 | 59 | ||
59 | 60 | ||
60 | OFileNotification::~OFileNotification() | 61 | OFileNotification::~OFileNotification() |
61 | { | 62 | { |
62 | qDebug( "OFileNotification::~OFileNotification()" ); | 63 | qDebug( "OFileNotification::~OFileNotification()" ); |
63 | } | 64 | } |
64 | 65 | ||
65 | 66 | ||
66 | bool OFileNotification::isActive() const | 67 | bool OFileNotification::isActive() const |
67 | { | 68 | { |
68 | return _active; | 69 | return _active; |
69 | } | 70 | } |
70 | 71 | ||
71 | 72 | ||
72 | int OFileNotification::start( const QString& path, bool sshot, OFileNotificationType type ) | 73 | int OFileNotification::start( const QString& path, bool sshot, OFileNotificationType type ) |
73 | { | 74 | { |
74 | _path = QString::null; | 75 | _path = QString::null; |
75 | _fd = 0; | 76 | _fd = 0; |
76 | if ( _active ) stop(); | 77 | if ( _active ) stop(); |
78 | QString dirpath; | ||
77 | 79 | ||
78 | int fd = ::open( (const char*) path, O_RDONLY ); | 80 | // check if path exists and whether it is a file or a directory, if it exists at all |
81 | int result = ::stat( (const char*) path, &_stat ); | ||
82 | if ( result == -1 ) | ||
83 | { | ||
84 | qWarning( "OFileNotification::start(): Can't stat '%s': %s.", (const char*) path, strerror( errno ) ); | ||
85 | return -1; | ||
86 | } | ||
87 | |||
88 | // if it is not a directory, we need to find out in which directory the file is | ||
89 | bool isDirectory = S_ISDIR( _stat.st_mode ); | ||
90 | if ( !isDirectory ) | ||
91 | { | ||
92 | int slashpos; | ||
93 | slashpos = path.findRev( '/' ); | ||
94 | if ( slashpos > 0 ) | ||
95 | { | ||
96 | _path = path; | ||
97 | dirpath = path.left( slashpos ); | ||
98 | } | ||
99 | } | ||
100 | else /* isDirectory */ | ||
101 | { | ||
102 | qWarning( "FIXME FIXME FIXME = Directory Notification Not Yet Implemented!" ); | ||
103 | _path = path; | ||
104 | dirpath = path; | ||
105 | assert( 0 ); | ||
106 | } | ||
107 | |||
108 | int fd = ::open( (const char*) dirpath, O_RDONLY ); | ||
79 | if ( fd != -1 ) | 109 | if ( fd != -1 ) |
80 | { | 110 | { |
81 | if ( notification_list.isEmpty() ) | 111 | if ( notification_list.isEmpty() ) |
82 | { | 112 | { |
83 | OFileNotification::registerSignalHandler(); | 113 | OFileNotification::registerSignalHandler(); |
84 | } | 114 | } |
85 | int result = ::fcntl( fd, F_SETSIG, SIGRTMIN ); | 115 | |
116 | result = ::fcntl( fd, F_SETSIG, SIGRTMIN ); | ||
86 | if ( result == -1 ) | 117 | if ( result == -1 ) |
87 | { | 118 | { |
88 | qWarning( "OFileNotification::start(): Can't subscribe to '%s': %s.", (const char*) path, strerror( errno ) ); | 119 | qWarning( "OFileNotification::start(): Can't subscribe to '%s': %s.", (const char*) dirpath, strerror( errno ) ); |
89 | return -1; | 120 | return -1; |
90 | } | 121 | } |
91 | if ( !sshot ) (int) type |= (int) Multi; | 122 | if ( !sshot ) (int) type |= (int) Multi; |
92 | result = ::fcntl( fd, F_NOTIFY, type ); | 123 | result = ::fcntl( fd, F_NOTIFY, type ); |
93 | if ( result == -1 ) | 124 | if ( result == -1 ) |
94 | { | 125 | { |
95 | qWarning( "OFileNotification::start(): Can't subscribe to '%s': %s.", (const char*) path, strerror( errno ) ); | 126 | qWarning( "OFileNotification::start(): Can't subscribe to '%s': %s.", (const char*) dirpath, strerror( errno ) ); |
96 | return -1; | 127 | return -1; |
97 | } | 128 | } |
98 | qDebug( "OFileNotification::start(): Subscribed for changes to %s (fd = %d, mask = 0x%0x)", (const char*) path, fd, type ); | 129 | qDebug( "OFileNotification::start(): Subscribed for changes to %s (fd = %d, mask = 0x%0x)", (const char*) dirpath, fd, type ); |
99 | notification_list.insert( fd, this ); | 130 | notification_list.insert( fd, this ); |
100 | _type = type; | 131 | _type = type; |
101 | _path = path; | ||
102 | _fd = fd; | 132 | _fd = fd; |
133 | _active = true; | ||
134 | ::memset( &_stat, 0, sizeof _stat ); | ||
135 | ::stat( _path, &_stat ); | ||
103 | return fd; | 136 | return fd; |
104 | } | 137 | } |
105 | else | 138 | else |
106 | { | 139 | { |
107 | qWarning( "OFileNotification::start(): Error with path '%s': %s.", (const char*) path, strerror( errno ) ); | 140 | qWarning( "OFileNotification::start(): Error with path '%s': %s.", (const char*) dirpath, strerror( errno ) ); |
108 | return -1; | 141 | return -1; |
109 | } | 142 | } |
110 | } | 143 | } |
111 | 144 | ||
112 | 145 | ||
113 | void OFileNotification::stop() | 146 | void OFileNotification::stop() |
114 | { | 147 | { |
115 | if ( !_active ) return; | 148 | if ( !_active ) return; |
116 | 149 | ||
117 | int result = ::fcntl( _fd, F_NOTIFY, 0 ); | 150 | int result = ::fcntl( _fd, F_NOTIFY, 0 ); |
118 | if ( result == -1 ) | 151 | if ( result == -1 ) |
119 | { | 152 | { |
120 | qWarning( "OFileNotification::stop(): Can't remove subscription to '%s': %s.", (const char*) _path, strerror( errno ) ); | 153 | qWarning( "OFileNotification::stop(): Can't remove subscription to '%s': %s.", (const char*) _path, strerror( errno ) ); |
121 | } | 154 | } |
122 | else | 155 | else |
123 | { | 156 | { |
124 | ::close( _fd ); | 157 | ::close( _fd ); |
125 | _type = Single; | 158 | _type = Single; |
126 | _path = QString::null; | 159 | _path = QString::null; |
127 | _fd = 0; | 160 | _fd = 0; |
128 | _active = false; | 161 | _active = false; |
129 | } | 162 | } |
130 | } | 163 | } |
131 | 164 | ||
132 | 165 | ||
133 | OFileNotificationType OFileNotification::type() const | 166 | OFileNotificationType OFileNotification::type() const |
134 | { | 167 | { |
135 | return _type; | 168 | return _type; |
136 | } | 169 | } |
137 | 170 | ||
138 | 171 | ||
139 | QString OFileNotification::path() const | 172 | QString OFileNotification::path() const |
140 | { | 173 | { |
141 | return _path; | 174 | return _path; |
142 | } | 175 | } |
143 | 176 | ||
177 | |||
144 | int OFileNotification::fileno() const | 178 | int OFileNotification::fileno() const |
145 | { | 179 | { |
146 | return _fd; | 180 | return _fd; |
147 | } | 181 | } |
148 | 182 | ||
149 | void OFileNotification::activate() | 183 | |
184 | bool OFileNotification::activate() | ||
150 | { | 185 | { |
151 | emit triggered(); | 186 | if ( hasChanged() ) |
152 | _signal.activate(); | 187 | { |
188 | emit triggered(); | ||
189 | _signal.activate(); | ||
190 | return true; | ||
191 | } | ||
192 | else | ||
193 | return false; | ||
194 | } | ||
195 | |||
196 | |||
197 | bool OFileNotification::hasChanged() | ||
198 | { | ||
199 | bool c = false; | ||
200 | |||
201 | struct stat newstat; | ||
202 | ::memset( &newstat, 0, sizeof newstat ); | ||
203 | ::stat( _path, &newstat ); | ||
204 | |||
205 | qDebug( "stat.atime = %0lx, newstat.atime = %0lx", (long)_stat.st_atime, (long)newstat.st_atime ); | ||
206 | qDebug( "stat.mtime = %0lx, newstat.mtime = %0lx", (long)_stat.st_mtime, (long)newstat.st_mtime ); | ||
207 | if ( !c && (_type & Access) && (long)_stat.st_atime < (long)newstat.st_atime ) | ||
208 | { | ||
209 | qDebug( "OFileNotification::hasChanged(): atime changed" ); | ||
210 | c = true; | ||
211 | } | ||
212 | if ( !c && (_type & Modify) && (long)_stat.st_mtime < (long)newstat.st_mtime ) | ||
213 | { | ||
214 | qDebug( "OFileNotification::hasChanged(): mtime changed" ); | ||
215 | c = true; | ||
216 | } | ||
217 | |||
218 | return c; | ||
153 | } | 219 | } |
154 | 220 | ||
155 | 221 | ||
156 | void OFileNotification::singleShot( const QString& path, QObject* receiver, const char* member, OFileNotificationType type ) | 222 | void OFileNotification::singleShot( const QString& path, QObject* receiver, const char* member, OFileNotificationType type ) |
157 | { | 223 | { |
158 | OFileNotification* ofn = new OFileNotification(); | 224 | OFileNotification* ofn = new OFileNotification(); |
159 | ofn->_signal.connect( receiver, member ); | 225 | ofn->_signal.connect( receiver, member ); |
160 | ofn->start( path, true, type ); | 226 | ofn->start( path, true, type ); |
161 | } | 227 | } |
162 | 228 | ||
163 | 229 | ||
164 | void OFileNotification::__signalHandler( int sig, siginfo_t *si, void *data ) | 230 | void OFileNotification::__signalHandler( int sig, siginfo_t *si, void *data ) |
165 | { | 231 | { |
166 | qWarning( "OFileNotification::__signalHandler(): reached." ); | 232 | qWarning( "OFileNotification::__signalHandler(): reached." ); |
167 | int fd = si->si_fd; | 233 | int fd = si->si_fd; |
168 | OFileNotification* fn = notification_list[fd]; | 234 | OFileNotification* fn = notification_list[fd]; |
169 | if ( fn ) | 235 | if ( fn ) |
170 | { | 236 | { |
171 | fn->activate(); | 237 | // check if it really was the file (dnotify triggers on directory granularity, not file granularity) |
238 | if ( !fn->activate() ) | ||
239 | { | ||
240 | qDebug( "OFileNotification::__signalHandler(): false alarm ;) Restarting the trigger (if it was single)..." ); | ||
241 | if ( !(fn->type() & Multi ) ) | ||
242 | { | ||
243 | int result = ::fcntl( fn->fileno(), F_NOTIFY, fn->type() ); | ||
244 | if ( result == -1 ) | ||
245 | { | ||
246 | qWarning( "OFileNotification::__signalHandler(): Can't restart the trigger: %s.", strerror( errno ) ); | ||
247 | } | ||
248 | } | ||
249 | return; | ||
250 | } | ||
172 | #if 1 | 251 | #if 1 |
173 | if ( !(fn->type() & Multi) ) | 252 | if ( !(fn->type() & Multi) ) |
174 | { | 253 | { |
175 | qDebug( "OFileNotification::__signalHandler(): '%d' was singleShot. Removing from list.", fd ); | 254 | qDebug( "OFileNotification::__signalHandler(): '%d' was singleShot. Removing from list.", fd ); |
176 | notification_list.remove( fd ); | 255 | notification_list.remove( fd ); |
177 | if ( notification_list.isEmpty() ) | 256 | if ( notification_list.isEmpty() ) |
178 | { | 257 | { |
179 | OFileNotification::unregisterSignalHandler(); | 258 | OFileNotification::unregisterSignalHandler(); |
180 | } | 259 | } |
181 | } | 260 | } |
182 | #endif | 261 | #endif |
183 | } | 262 | } |
184 | else | 263 | else |
185 | { | 264 | { |
186 | qWarning( "OFileNotification::__signalHandler(): D'oh! Called without fd in notification_list. Race condition?" ); | 265 | qWarning( "OFileNotification::__signalHandler(): D'oh! Called without fd in notification_list. Race condition?" ); |
187 | } | 266 | } |
188 | } | 267 | } |
189 | 268 | ||
190 | 269 | ||
191 | bool OFileNotification::registerSignalHandler() | 270 | bool OFileNotification::registerSignalHandler() |
192 | { | 271 | { |
193 | struct sigaction act; | 272 | struct sigaction act; |
194 | act.sa_sigaction = OFileNotification::__signalHandler; | 273 | act.sa_sigaction = OFileNotification::__signalHandler; |
195 | ::sigemptyset( &act.sa_mask ); | 274 | ::sigemptyset( &act.sa_mask ); |
196 | act.sa_flags = SA_SIGINFO; | 275 | act.sa_flags = SA_SIGINFO; |
197 | if ( ::sigaction( SIGRTMIN, &act, NULL ) == -1 ) | 276 | if ( ::sigaction( SIGRTMIN, &act, NULL ) == -1 ) |
198 | { | 277 | { |
199 | qWarning( "OFileNotification::registerSignalHandler(): couldn't register signal handler: %s", strerror( errno ) ); | 278 | qWarning( "OFileNotification::registerSignalHandler(): couldn't register signal handler: %s", strerror( errno ) ); |
200 | return false; | 279 | return false; |
201 | } | 280 | } |
202 | qDebug( "OFileNotification::registerSignalHandler(): done" ); | 281 | qDebug( "OFileNotification::registerSignalHandler(): done" ); |
203 | } | 282 | } |
204 | 283 | ||
205 | 284 | ||
206 | void OFileNotification::unregisterSignalHandler() | 285 | void OFileNotification::unregisterSignalHandler() |
207 | { | 286 | { |
208 | struct sigaction act; | 287 | struct sigaction act; |
209 | act.sa_sigaction = ( void (*)(int, siginfo_t*, void*) ) SIG_DFL; | 288 | act.sa_sigaction = ( void (*)(int, siginfo_t*, void*) ) SIG_DFL; |
210 | ::sigemptyset( &act.sa_mask ); | 289 | ::sigemptyset( &act.sa_mask ); |
211 | if ( ::sigaction( SIGRTMIN, &act, NULL ) == -1 ) | 290 | if ( ::sigaction( SIGRTMIN, &act, NULL ) == -1 ) |
212 | if ( ::sigaction( SIGRTMIN, &act, NULL ) == -1 ) | ||
213 | { | 291 | { |
214 | qWarning( "OFileNotification::unregisterSignalHandler(): couldn't deregister signal handler: %s", strerror( errno ) ); | 292 | qWarning( "OFileNotification::unregisterSignalHandler(): couldn't deregister signal handler: %s", strerror( errno ) ); |
215 | } | 293 | } |
216 | qDebug( "OFileNotification::unregisterSignalHandler(): done" ); | 294 | qDebug( "OFileNotification::unregisterSignalHandler(): done" ); |
217 | } | 295 | } |
218 | 296 | ||
219 | 297 | ||
220 | } | 298 | } |
221 | } | 299 | } |
diff --git a/libopie2/opiecore/ofilenotify.h b/libopie2/opiecore/ofilenotify.h index 3bc141d..5315896 100644 --- a/libopie2/opiecore/ofilenotify.h +++ b/libopie2/opiecore/ofilenotify.h | |||
@@ -54,42 +54,44 @@ enum OFileNotificationType { Single = 0x0000000, | |||
54 | Attrib = DN_ATTRIB }; | 54 | Attrib = DN_ATTRIB }; |
55 | 55 | ||
56 | class OFileNotification : public QObject | 56 | class OFileNotification : public QObject |
57 | { | 57 | { |
58 | Q_OBJECT | 58 | Q_OBJECT |
59 | 59 | ||
60 | public: | 60 | public: |
61 | OFileNotification( QObject* parent = 0, const char* name = 0 ); | 61 | OFileNotification( QObject* parent = 0, const char* name = 0 ); |
62 | ~OFileNotification(); | 62 | ~OFileNotification(); |
63 | 63 | ||
64 | static void singleShot( const QString& path, QObject* receiver, const char* member, OFileNotificationType type = Modify ); | 64 | static void singleShot( const QString& path, QObject* receiver, const char* member, OFileNotificationType type = Modify ); |
65 | 65 | ||
66 | int start( const QString& path, bool sshot = false, OFileNotificationType type = Modify ); | 66 | int start( const QString& path, bool sshot = false, OFileNotificationType type = Modify ); |
67 | void stop(); | 67 | void stop(); |
68 | 68 | ||
69 | OFileNotificationType type() const; | 69 | OFileNotificationType type() const; |
70 | QString path() const; | 70 | QString path() const; |
71 | int fileno() const; | 71 | int fileno() const; |
72 | bool isActive() const; | 72 | bool isActive() const; |
73 | 73 | ||
74 | signals: | 74 | signals: |
75 | void triggered(); | 75 | void triggered(); |
76 | 76 | ||
77 | protected: | 77 | protected: |
78 | void activate(); | 78 | bool activate(); |
79 | bool hasChanged(); | ||
79 | static bool registerSignalHandler(); | 80 | static bool registerSignalHandler(); |
80 | static void unregisterSignalHandler(); | 81 | static void unregisterSignalHandler(); |
81 | static void __signalHandler( int sig, siginfo_t *si, void *data ); | 82 | static void __signalHandler( int sig, siginfo_t *si, void *data ); |
82 | 83 | ||
83 | private: | 84 | private: |
84 | QString _path; | 85 | QString _path; |
85 | OFileNotificationType _type; | 86 | OFileNotificationType _type; |
86 | QSignal _signal; | 87 | QSignal _signal; |
87 | int _fd; | 88 | int _fd; |
88 | bool _active; | 89 | bool _active; |
90 | struct stat _stat; | ||
89 | }; | 91 | }; |
90 | 92 | ||
91 | } | 93 | } |
92 | } | 94 | } |
93 | 95 | ||
94 | #endif | 96 | #endif |
95 | 97 | ||