summaryrefslogtreecommitdiff
path: root/libopie2/opiecore/ofilenotify.cpp
Unidiff
Diffstat (limited to 'libopie2/opiecore/ofilenotify.cpp') (more/less context) (show whitespace changes)
-rw-r--r--libopie2/opiecore/ofilenotify.cpp264
1 files changed, 85 insertions, 179 deletions
diff --git a/libopie2/opiecore/ofilenotify.cpp b/libopie2/opiecore/ofilenotify.cpp
index 2a9bb8c..c221e58 100644
--- a/libopie2/opiecore/ofilenotify.cpp
+++ b/libopie2/opiecore/ofilenotify.cpp
@@ -1,17 +1,17 @@
1/* 1/*
2                This file is part of the Opie Project 2                This file is part of the Opie Project
3 =. Copyright (C) 2004 Michael 'Mickey' Lauer <mickey@Vanille.de> 3 =. Copyright (C) 2004-2005 Michael 'Mickey' Lauer <mickey@Vanille.de>
4 .=l. Copyright (C) The Opie Team <opie-devel@handhelds.org> 4 .=l. Copyright (C) The Opie Team <opie-devel@handhelds.org>
5          .>+-= 5          .>+-=
6_;:,     .>    :=|. This program is free software; you can 6_;:,     .>    :=|. This program is free software; you can
7.> <`_,   >  .   <= redistribute it and/or modify it under 7.> <`_,   >  .   <= redistribute it and/or modify it under
8:`=1 )Y*s>-.--   : the terms of the GNU Library General Public 8:`=1 )Y*s>-.--   : the terms of the GNU Library General Public
9.="- .-=="i,     .._ License as published by the Free Software 9.="- .-=="i,     .._ License as published by the Free Software
10- .   .-<_>     .<> Foundation; either version 2 of the License, 10- .   .-<_>     .<> Foundation; version 2 of the License.
11    ._= =}       : or (at your option) any later version. 11    ._= =}       :
12   .%`+i>       _;_. 12   .%`+i>       _;_.
13   .i_,=:_.      -<s. This program is distributed in the hope that 13   .i_,=:_.      -<s. This program is distributed in the hope that
14    +  .  -:.       = it will be useful, but WITHOUT ANY WARRANTY; 14    +  .  -:.       = it will be useful, but WITHOUT ANY WARRANTY;
15   : ..    .:,     . . . without even the implied warranty of 15   : ..    .:,     . . . without even the implied warranty of
16   =_        +     =;=|` MERCHANTABILITY or FITNESS FOR A 16   =_        +     =;=|` MERCHANTABILITY or FITNESS FOR A
17 _.=:.       :    :=>`: PARTICULAR PURPOSE. See the GNU 17 _.=:.       :    :=>`: PARTICULAR PURPOSE. See the GNU
@@ -30,138 +30,103 @@ _;:,     .>    :=|. This program is free software; you can
30using namespace Opie::Core; 30using 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 <qsocketnotifier.h>
36#include <qsignal.h> 37#include <qsignal.h>
37#include <qintdict.h> 38#include <qintdict.h>
38#include <qdir.h> 39#include <qdir.h>
39 40
40/* STD */ 41/* STD */
41#include <sys/types.h> 42#include <sys/types.h>
42#include <sys/stat.h> 43#include <sys/stat.h>
43#include <assert.h> 44#include <sys/ioctl.h>
44#include <fcntl.h> 45#include <fcntl.h>
46#include <assert.h>
45#include <string.h> 47#include <string.h>
46#include <errno.h> 48#include <errno.h>
47#include <unistd.h> 49#include <unistd.h>
48 50
49static QIntDict<OFileNotification> notification_list; 51static QIntDict<OFileNotification> notification_list;
50 52
53QSocketNotifier* OFileNotification::_sn;
54int OFileNotification::_fd = -1;
55
56#define INOTIFY_DEVICE "/dev/inotify"
57
51namespace Opie { 58namespace Opie {
52namespace Core { 59namespace Core {
53 60
54OFileNotification::OFileNotification( QObject* parent, const char* name ) 61OFileNotification::OFileNotification( QObject* parent, const char* name )
55 :QObject( parent, name ), _active( false ) 62 :QObject( parent, name ), _active( false ), _multi( true )
56{ 63{
57 qDebug( "OFileNotification::OFileNotification()" ); 64 qDebug( "OFileNotification::OFileNotification()" );
58} 65}
59 66
60 67
61OFileNotification::~OFileNotification() 68OFileNotification::~OFileNotification()
62{ 69{
70 stop();
63 qDebug( "OFileNotification::~OFileNotification()" ); 71 qDebug( "OFileNotification::~OFileNotification()" );
64} 72}
65 73
66 74
67bool OFileNotification::isActive() const 75bool OFileNotification::isActive() const
68{ 76{
69 return _active; 77 return _active;
70} 78}
71 79
72 80
73int OFileNotification::start( const QString& path, bool sshot, OFileNotificationType type ) 81int OFileNotification::watch( const QString& path, bool sshot, OFileNotificationType type )
74{
75 _path = QString::null;
76 _fd = 0;
77 if ( _active ) stop();
78 QString dirpath;
79
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 ( !(type & Create) && 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 { 82 {
92 int slashpos; 83 if ( QFile::exists( path ) )
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 );
109 if ( fd != -1 )
110 { 84 {
111 if ( notification_list.isEmpty() ) 85 if ( notification_list.isEmpty() )
112 { 86 {
113 OFileNotification::registerSignalHandler(); 87 OFileNotification::registerEventHandler();
114 } 88 }
115 89
116 result = ::fcntl( fd, F_SETSIG, SIGRTMIN ); 90 struct inotify_watch_request iwr;
117 if ( result == -1 ) 91 ::memset( &iwr, 0, sizeof iwr );
118 { 92 iwr.name = const_cast<char*>( (const char*) path );
119 qWarning( "OFileNotification::start(): Can't subscribe to '%s': %s.", (const char*) dirpath, strerror( errno ) ); 93 iwr.mask = type;
120 return -1; 94
121 } 95 _wd = ::ioctl( OFileNotification::_fd, INOTIFY_WATCH, &iwr );
122 if ( !sshot ) type = static_cast<OFileNotificationType>( (int) type | (int) Multi ); 96
123 result = ::fcntl( fd, F_NOTIFY, type ); 97 if ( _wd < 0 )
124 if ( result == -1 )
125 { 98 {
126 qWarning( "OFileNotification::start(): Can't subscribe to '%s': %s.", (const char*) dirpath, strerror( errno ) ); 99 qWarning( "OFileNotification::watch(): inotify can't watch '%s': %s.", (const char*) path, strerror( errno ) );
127 return -1; 100 return -1;
128 } 101 }
129 qDebug( "OFileNotification::start(): Subscribed for changes to %s (fd = %d, mask = 0x%0x)", (const char*) dirpath, fd, type ); 102
130 notification_list.insert( fd, this ); 103 notification_list.insert( _wd, this );
104 _multi = !sshot;
131 _type = type; 105 _type = type;
132 _fd = fd;
133 _active = true; 106 _active = true;
134 ::memset( &_stat, 0, sizeof _stat ); 107 qDebug( "OFileNotification::watch(): watching '%s' [wd=%d].", (const char*) path, _wd );
135 ::stat( _path, &_stat ); 108 return _wd;
136 return fd;
137 } 109 }
138 else 110 else
139 { 111 {
140 qWarning( "OFileNotification::start(): Error with path '%s': %s.", (const char*) dirpath, strerror( errno ) ); 112 qWarning( "OFileNotification::watch(): Can't watch '%s': %s.", (const char*) path, strerror( errno ) );
141 return -1; 113 return -1;
142 } 114 }
143} 115}
144 116
145 117
146void OFileNotification::stop() 118void OFileNotification::stop()
147{ 119{
148 if ( !_active ) return; 120 notification_list.remove( _wd );
149
150 int result = ::fcntl( _fd, F_NOTIFY, 0 );
151 if ( result == -1 )
152 {
153 qWarning( "OFileNotification::stop(): Can't remove subscription to '%s': %s.", (const char*) _path, strerror( errno ) );
154 }
155 else
156 {
157 ::close( _fd );
158 _type = Single;
159 _path = QString::null; 121 _path = QString::null;
160 _fd = 0; 122 _wd = 0;
161 _active = false; 123 _active = false;
124 if ( notification_list.isEmpty() )
125 {
126 OFileNotification::unregisterEventHandler();
162 } 127 }
163} 128}
164 129
165 130
166OFileNotificationType OFileNotification::type() const 131OFileNotificationType OFileNotification::type() const
167{ 132{
@@ -172,151 +137,92 @@ OFileNotificationType OFileNotification::type() const
172QString OFileNotification::path() const 137QString OFileNotification::path() const
173{ 138{
174 return _path; 139 return _path;
175} 140}
176 141
177 142
178int OFileNotification::fileno() const
179{
180 return _fd;
181}
182
183
184bool OFileNotification::activate() 143bool OFileNotification::activate()
185{ 144{
186 if ( hasChanged() )
187 {
188 emit triggered(); 145 emit triggered();
189 _signal.activate(); 146 _signal.activate();
147 if ( !_multi ) stop();
190 return true; 148 return true;
191 } 149 }
192 else
193 return false;
194}
195
196
197bool OFileNotification::hasChanged()
198{
199 bool c = false;
200
201 struct stat newstat;
202 ::memset( &newstat, 0, sizeof newstat );
203 int result = ::stat( _path, &newstat ); // may fail if file has been renamed or deleted. that doesn't matter :)
204
205 qDebug( "result of newstat call is %d (%s=%d)", result, result == -1 ? strerror( errno ) : "success", errno );
206 qDebug( "stat.atime = %0lx, newstat.atime = %0lx", (long)_stat.st_atime, (long)newstat.st_atime );
207 qDebug( "stat.mtime = %0lx, newstat.mtime = %0lx", (long)_stat.st_mtime, (long)newstat.st_mtime );
208 qDebug( "stat.ctime = %0lx, newstat.ctime = %0lx", (long)_stat.st_ctime, (long)newstat.st_ctime );
209
210 if ( !c && (_type & Create) &&
211 (long)_stat.st_atime == 0 && (long)_stat.st_mtime == 0 && (long)_stat.st_ctime == 0 &&
212 (long)newstat.st_atime > 0 && (long)newstat.st_mtime > 0 && (long)newstat.st_ctime > 0)
213 {
214 qDebug( "OFileNotification::hasChanged(): file has been created" );
215 c = true;
216 }
217 if ( !c && (_type & (Delete|Rename)) && (long)newstat.st_atime == 0 && (long)newstat.st_mtime == 0 && (long)newstat.st_ctime == 0)
218 {
219 qDebug( "OFileNotification::hasChanged(): file has been deleted or renamed" );
220 c = true;
221 }
222 if ( !c && (_type & Access) && (long)_stat.st_atime < (long)newstat.st_atime )
223 {
224 qDebug( "OFileNotification::hasChanged(): atime changed" );
225 c = true;
226 }
227 if ( !c && (_type & Modify) && (long)_stat.st_mtime < (long)newstat.st_mtime )
228 {
229 qDebug( "OFileNotification::hasChanged(): mtime changed" );
230 c = true;
231 }
232 if ( !c && (_type & Attrib) && (long)_stat.st_ctime < (long)newstat.st_ctime )
233 {
234 qDebug( "OFileNotification::hasChanged(): ctime changed" );
235 c = true;
236 }
237
238 return c;
239}
240 150
241 151
242void OFileNotification::singleShot( const QString& path, QObject* receiver, const char* member, OFileNotificationType type ) 152void OFileNotification::singleShot( const QString& path, QObject* receiver, const char* member, OFileNotificationType type )
243{ 153{
244 OFileNotification* ofn = new OFileNotification(); 154 OFileNotification* ofn = new OFileNotification();
245 ofn->_signal.connect( receiver, member ); 155 ofn->_signal.connect( receiver, member );
246 ofn->start( path, true, type ); 156 ofn->watch( path, true, type );
247} 157}
248 158
249 159
250void OFileNotification::__signalHandler( int sig, siginfo_t *si, void *data ) 160void OFileNotification::inotifyEventHandler()
251{
252 Q_UNUSED( sig )
253 Q_UNUSED( data )
254 qWarning( "OFileNotification::__signalHandler(): reached." );
255 int fd = si->si_fd;
256 OFileNotification* fn = notification_list[fd];
257 if ( fn )
258 { 161 {
259 // check if it really was the file (dnotify triggers on directory granularity, not file granularity) 162 qWarning( "OFileNotification::__eventHandler(): reached." );
260 if ( !fn->activate() ) 163
261 { 164 char buffer[16384];
262 qDebug( "OFileNotification::__signalHandler(): false alarm ;) Restarting the trigger (if it was single)..." ); 165 size_t buffer_i;
263 if ( !(fn->type() & Multi ) ) 166 struct inotify_event *pevent, *event;
264 { 167 ssize_t r;
265 int result = ::fcntl( fn->fileno(), F_NOTIFY, fn->type() ); 168 size_t event_size;
266 if ( result == -1 ) 169 int count = 0;
267 { 170
268 qWarning( "OFileNotification::__signalHandler(): Can't restart the trigger: %s.", strerror( errno ) ); 171 r = ::read(_fd, buffer, 16384);
269 } 172
270 } 173 if ( r <= 0 )
271 return; 174 return;
272 } 175
273 #if 1 176 buffer_i = 0;
274 if ( !(fn->type() & Multi) ) 177 while ( buffer_i < r )
275 {
276 qDebug( "OFileNotification::__signalHandler(): '%d' was singleShot. Removing from list.", fd );
277 notification_list.remove( fd );
278 if ( notification_list.isEmpty() )
279 { 178 {
280 OFileNotification::unregisterSignalHandler(); 179 /* Parse events and queue them ! */
281 } 180 pevent = (struct inotify_event *)&buffer[buffer_i];
282 } 181 event_size = sizeof(struct inotify_event) + pevent->len;
283 #endif 182 qDebug( "pevent->len = %d\n", pevent->len);
284 } 183
184 OFileNotification* fn = notification_list[ pevent->wd ];
185 if ( fn )
186 fn->activate();
285 else 187 else
286 { 188 assert( false );
287 qWarning( "OFileNotification::__signalHandler(): D'oh! Called without fd in notification_list. Race condition?" ); 189
190 //event = malloc(event_size);
191 //memmove(event, pevent, event_size);
192 //queue_enqueue(event, q);
193 buffer_i += event_size;
194 count++;
288 } 195 }
196
197 qDebug( "received %d events...", count );
198 return;
289} 199}
290 200
291 201
292bool OFileNotification::registerSignalHandler() 202bool OFileNotification::registerEventHandler()
293{ 203{
294 struct sigaction act; 204 OFileNotification::_fd = ::open( INOTIFY_DEVICE, O_RDONLY );
295 act.sa_sigaction = OFileNotification::__signalHandler; 205 if ( OFileNotification::_fd < 0 )
296 ::sigemptyset( &act.sa_mask );
297 act.sa_flags = SA_SIGINFO;
298 if ( ::sigaction( SIGRTMIN, &act, NULL ) == -1 )
299 { 206 {
300 qWarning( "OFileNotification::registerSignalHandler(): couldn't register signal handler: %s", strerror( errno ) ); 207 qWarning( "OFileNotification::registerEventHandler(): couldn't register event handler: %s", strerror( errno ) );
301 return false; 208 return false;
302 } 209 }
303 qDebug( "OFileNotification::registerSignalHandler(): done" ); 210
211 OFileNotification::_sn = new QSocketNotifier( _fd, QSocketNotifier::Read, this, "inotify event" );
212 connect( OFileNotification::_sn, SIGNAL( activated(int) ), this, SLOT( inotifyEventHandler() ) );
213
214 qDebug( "OFileNotification::registerEventHandler(): done" );
304 return true; 215 return true;
305} 216}
306 217
307 218
308void OFileNotification::unregisterSignalHandler() 219void OFileNotification::unregisterEventHandler()
309{ 220{
310 struct sigaction act; 221 if ( OFileNotification::_fd )
311 act.sa_sigaction = ( void (*)(int, siginfo_t*, void*) ) SIG_DFL; 222 ::close( OFileNotification::_fd );
312 ::sigemptyset( &act.sa_mask ); 223 qDebug( "OFileNotification::unregisterEventHandler(): done" );
313 if ( ::sigaction( SIGRTMIN, &act, NULL ) == -1 )
314 {
315 qWarning( "OFileNotification::unregisterSignalHandler(): couldn't deregister signal handler: %s", strerror( errno ) );
316 }
317 qDebug( "OFileNotification::unregisterSignalHandler(): done" );
318} 224}
319 225
320 226
321} 227}
322} 228}