source: psi/trunk/src/tools/dirwatch/dirwatch_unix.cpp

Last change on this file was 2, checked in by dmik, 19 years ago

Imported original Psi 0.10 sources from Affinix

File size: 6.6 KB
Line 
1/*
2 * dirwatch_unix.cpp - detect changes of directory content
3 * Copyright (C) 2003 Justin Karneges
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include"dirwatch.h"
22
23// A great many hints taken from KDE's KDirWatch system. Thank you KDE!
24
25#ifdef HAVE_DNOTIFY
26#include<qtimer.h>
27#include<qsocketnotifier.h>
28#include<qvaluelist.h>
29#include<qptrlist.h>
30#include<qfile.h>
31#include<unistd.h>
32#include<fcntl.h>
33#include<signal.h>
34#include<sys/utsname.h>
35
36#define DNOTIFY_SIGNAL (SIGRTMIN + 8)
37
38class DWEntry
39{
40public:
41 static DWEntry * open(const QString &s)
42 {
43 int fd = ::open(QFile::encodeName(s), O_RDONLY);
44 if(fd == -1)
45 return 0;
46
47 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT|DN_MODIFY|DN_ATTRIB;
48 if(fcntl(fd, F_SETSIG, DNOTIFY_SIGNAL) == -1 || fcntl(fd, F_NOTIFY, mask) == -1) {
49 close(fd);
50 return 0;
51 }
52 DWEntry *e = new DWEntry(s, fd);
53 return e;
54 }
55
56 ~DWEntry()
57 {
58 close(fd);
59 }
60
61 QString dir;
62 int fd;
63 QValueList<int> idList;
64 bool dirty;
65
66private:
67 DWEntry(const QString &_dir, int _fd)
68 {
69 dir = _dir;
70 fd = _fd;
71 dirty = false;
72 }
73};
74
75#endif
76
77class DirWatchPlatform::Private : public QObject
78{
79 Q_OBJECT
80public:
81#ifdef HAVE_DNOTIFY
82 QTimer t;
83 QSocketNotifier *sn;
84 int pipe_notify[2];
85 bool ok;
86 QPtrList<DWEntry> list;
87#endif
88 DirWatchPlatform *par;
89 static Private *self;
90
91 Private(DirWatchPlatform *parent)
92 {
93 par = parent;
94 self = this;
95
96#ifdef HAVE_DNOTIFY
97 list.setAutoDelete(true);
98 ok = false;
99
100 // from KDE
101 bool supports_dnotify = true; // not guilty until proven guilty
102 struct utsname uts;
103 int major, minor, patch;
104 if(uname(&uts) < 0)
105 supports_dnotify = false; // *shrug*
106 else if(sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
107 supports_dnotify = false; // *shrug*
108 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) // <2.4.19
109 supports_dnotify = false;
110
111 if(!supports_dnotify)
112 return;
113
114 if(pipe(pipe_notify) == -1)
115 return;
116
117 if(fcntl(pipe_notify[0], F_SETFL, O_NONBLOCK) == -1)
118 return;
119
120 connect(&t, SIGNAL(timeout()), this, SLOT(slotNotify()));
121
122 sn = new QSocketNotifier(pipe_notify[0], QSocketNotifier::Read);
123 connect(sn, SIGNAL(activated(int)), this, SLOT(slotActivated()));
124
125 struct sigaction act;
126 act.sa_sigaction = dnotify_handler;
127 sigemptyset(&act.sa_mask);
128 act.sa_flags = SA_SIGINFO;
129#ifdef SA_RESTART
130 act.sa_flags |= SA_RESTART;
131#endif
132 sigaction(DNOTIFY_SIGNAL, &act, NULL);
133
134 ok = true;
135#endif
136 }
137
138 ~Private()
139 {
140#ifdef HAVE_DNOTIFY
141 if(ok) {
142 delete sn;
143 close(pipe_notify[0]);
144 close(pipe_notify[1]);
145 }
146 list.clear();
147#endif
148
149 self = 0;
150 }
151
152#ifdef HAVE_DNOTIFY
153 static void dnotify_handler(int, siginfo_t *si, void *)
154 {
155 // bad news
156 if(!si)
157 return;
158
159 int fd = si->si_fd;
160 //printf("dnotify_handler: fd=%d\n", fd);
161
162 DWEntry *e = self->findEntryByFd(fd);
163 if(!e)
164 return;
165 e->dirty = true;
166
167 // write a byte into the pipe
168 char c = 1;
169 write(self->pipe_notify[1], &c, 1);
170 }
171#endif
172
173private slots:
174 void slotActivated()
175 {
176#ifdef HAVE_DNOTIFY
177 // clear out the pipe
178 while(1) {
179 char buf[64];
180 int ret = read(pipe_notify[0], buf, 64);
181 if(ret < 64)
182 break;
183 }
184
185 // use a timer to combine multiple dnotify events into one
186 if(!t.isActive())
187 t.start(200, true);
188#endif
189 }
190
191 void slotNotify()
192 {
193#ifdef HAVE_DNOTIFY
194 // see who is dirty
195 QPtrListIterator<DWEntry> it(list);
196 for(DWEntry *e; (e = it.current()); ++it) {
197 if(e->dirty) {
198 e->dirty = false;
199 for(QValueList<int>::ConstIterator idi = e->idList.begin(); idi != e->idList.end(); ++idi) {
200 par->triggerDirChanged(*idi);
201 }
202 }
203 }
204#endif
205 }
206
207public:
208#ifdef HAVE_DNOTIFY
209 int addItem(const QString &s)
210 {
211 //printf("setting up DNOTIFY for [%s]\n", s.latin1());
212
213 DWEntry *e = findEntryByDir(s);
214 if(!e) {
215 e = DWEntry::open(s);
216 if(!e)
217 return -1;
218 list.append(e);
219 }
220 int id = getUniqueId();
221 e->idList.append(id);
222
223 //printf("success (fd=%d)!\n", e->fd);
224 return id;
225 }
226
227 void removeItem(int id)
228 {
229 DWEntry *e = findEntryById(id);
230 if(!e)
231 return;
232 e->idList.remove(id);
233 if(e->idList.isEmpty())
234 list.removeRef(e);
235 }
236
237 int getUniqueId()
238 {
239 for(int n = 0;; ++n) {
240 QPtrListIterator<DWEntry> it(list);
241 bool found = false;
242 for(DWEntry *e; (e = it.current()); ++it) {
243 for(QValueList<int>::ConstIterator idi = e->idList.begin(); idi != e->idList.end(); ++idi) {
244 if(*idi == n) {
245 found = true;
246 break;
247 }
248 }
249 if(found)
250 break;
251 }
252 if(!found)
253 return n;
254 }
255 }
256
257 DWEntry * findEntryByDir(const QString &s)
258 {
259 QPtrListIterator<DWEntry> it(list);
260 for(DWEntry *e; (e = it.current()); ++it) {
261 if(e->dir == s)
262 return e;
263 }
264 return 0;
265 }
266
267 DWEntry * findEntryById(int id)
268 {
269 QPtrListIterator<DWEntry> it(list);
270 for(DWEntry *e; (e = it.current()); ++it) {
271 for(QValueList<int>::ConstIterator idi = e->idList.begin(); idi != e->idList.end(); ++idi) {
272 if(*idi == id)
273 return e;
274 }
275 }
276 return 0;
277 }
278
279 DWEntry * findEntryByFd(int fd)
280 {
281 QPtrListIterator<DWEntry> it(list);
282 for(DWEntry *e; (e = it.current()); ++it) {
283 if(e->fd == fd)
284 return e;
285 }
286 return 0;
287 }
288
289#endif
290};
291
292DirWatchPlatform::Private *DirWatchPlatform::Private::self;
293
294#ifdef HAVE_DNOTIFY
295
296DirWatchPlatform::DirWatchPlatform()
297:QObject(0)
298{
299 d = 0;
300}
301
302DirWatchPlatform::~DirWatchPlatform()
303{
304 delete d;
305}
306
307bool DirWatchPlatform::init()
308{
309 Private *p = new Private(this);
310 if(p->ok) {
311 d = p;
312 //printf("DirWatchPlatform::init() ok!\n");
313 return true;
314 }
315 else {
316 delete p;
317 //printf("DirWatchPlatform::init() fail!\n");
318 return false;
319 }
320}
321
322int DirWatchPlatform::addDir(const QString &s)
323{
324 return d->addItem(s);
325}
326
327void DirWatchPlatform::removeDir(int id)
328{
329 d->removeItem(id);
330}
331
332#else
333
334DirWatchPlatform::DirWatchPlatform():QObject(0) {}
335DirWatchPlatform::~DirWatchPlatform() {}
336bool DirWatchPlatform::init() { return false; }
337int DirWatchPlatform::addDir(const QString &) { return -1; }
338void DirWatchPlatform::removeDir(int) {}
339
340#endif
341
342#include"dirwatch_unix.moc"
Note: See TracBrowser for help on using the repository browser.