~ubuntu-branches/ubuntu/quantal/psi/quantal

« back to all changes in this revision

Viewing changes to src/tools/dirwatch/dirwatch_unix.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2006-01-20 00:20:36 UTC
  • mfrom: (1.2.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060120002036-7nw6yo6totip0ee5
Tags: 0.10-2
* Added upstream changelog (Closes: Bug#327748)
* Mention --no-gpg and --no-gpg-agent in manpage (Closes: Bug#204416)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
38
class DWEntry
 
39
{
 
40
public:
 
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
 
 
66
private:
 
67
        DWEntry(const QString &_dir, int _fd)
 
68
        {
 
69
                dir = _dir;
 
70
                fd = _fd;
 
71
                dirty = false;
 
72
        }
 
73
};
 
74
 
 
75
#endif
 
76
 
 
77
class DirWatchPlatform::Private : public QObject
 
78
{
 
79
        Q_OBJECT
 
80
public:
 
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
 
 
173
private 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
 
 
207
public:
 
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
 
 
292
DirWatchPlatform::Private *DirWatchPlatform::Private::self;
 
293
 
 
294
#ifdef HAVE_DNOTIFY
 
295
 
 
296
DirWatchPlatform::DirWatchPlatform()
 
297
:QObject(0)
 
298
{
 
299
        d = 0;
 
300
}
 
301
 
 
302
DirWatchPlatform::~DirWatchPlatform()
 
303
{
 
304
        delete d;
 
305
}
 
306
 
 
307
bool 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
 
 
322
int DirWatchPlatform::addDir(const QString &s)
 
323
{
 
324
        return d->addItem(s);
 
325
}
 
326
 
 
327
void DirWatchPlatform::removeDir(int id)
 
328
{
 
329
        d->removeItem(id);
 
330
}
 
331
 
 
332
#else
 
333
 
 
334
DirWatchPlatform::DirWatchPlatform():QObject(0) {}
 
335
DirWatchPlatform::~DirWatchPlatform() {}
 
336
bool DirWatchPlatform::init() { return false; }
 
337
int DirWatchPlatform::addDir(const QString &) { return -1; }
 
338
void DirWatchPlatform::removeDir(int) {}
 
339
 
 
340
#endif
 
341
 
 
342
#include"dirwatch_unix.moc"