~ubuntu-branches/ubuntu/oneiric/psi/oneiric

« back to all changes in this revision

Viewing changes to iris/src/irisnet/corelib/netinterface.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2009-09-25 17:49:51 UTC
  • mfrom: (6.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20090925174951-lvm7kdap82o8xhn3
Tags: 0.13-1
* Updated to upstream version 0.13
* Set Standards-Version to 3.8.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2006  Justin Karneges
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Lesser General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2.1 of the License, or (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Lesser General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Lesser General Public
 
15
 * License along with this library; if not, write to the Free Software
 
16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 
17
 * 02110-1301  USA
 
18
 *
 
19
 */
 
20
 
 
21
#include "netinterface.h"
 
22
 
 
23
#include "irisnetplugin.h"
 
24
#include "irisnetglobal_p.h"
 
25
 
 
26
#include <QWaitCondition>
 
27
#include <QPointer>
 
28
#include <QDebug>
 
29
 
 
30
namespace XMPP {
 
31
 
 
32
//----------------------------------------------------------------------------
 
33
// NetTracker
 
34
//----------------------------------------------------------------------------
 
35
class NetTracker : public QObject {
 
36
        Q_OBJECT
 
37
public:
 
38
        QList<NetInterfaceProvider::Info> getInterfaces() {
 
39
                QMutexLocker locker(&m);
 
40
 
 
41
                return info;
 
42
        }
 
43
 
 
44
        NetTracker() {
 
45
                QList<IrisNetProvider*> list = irisNetProviders();
 
46
 
 
47
                c = 0;
 
48
                foreach(IrisNetProvider* p, list) {
 
49
                        c = p->createNetInterfaceProvider();
 
50
                        if(c) break;
 
51
                }
 
52
                Q_ASSERT(c); // we have built-in support, so this should never fail
 
53
                connect(c, SIGNAL(updated()), SLOT(c_updated()));
 
54
 
 
55
                c->start();
 
56
                info = filterList(c->interfaces());
 
57
        }
 
58
 
 
59
        ~NetTracker() {
 
60
                QMutexLocker locker(&m);
 
61
                delete c;
 
62
        }
 
63
 
 
64
 
 
65
signals:
 
66
        void updated();
 
67
private:
 
68
 
 
69
 
 
70
        static QList<NetInterfaceProvider::Info> filterList(const QList<NetInterfaceProvider::Info> &in) {
 
71
                QList<NetInterfaceProvider::Info> out;
 
72
                for(int n = 0; n < in.count(); ++n)
 
73
                {
 
74
                        if(!in[n].isLoopback) out += in[n];
 
75
                }
 
76
                return out;
 
77
        }
 
78
 
 
79
private slots:
 
80
        void c_updated() {
 
81
                {
 
82
                        QMutexLocker locker(&m);
 
83
                        info = filterList(c->interfaces());
 
84
                }
 
85
                emit updated();
 
86
        }
 
87
 
 
88
 
 
89
private:
 
90
        // this are all protected by m
 
91
        NetInterfaceProvider *c;
 
92
        QMutex m;
 
93
        QList<NetInterfaceProvider::Info> info;
 
94
 
 
95
};
 
96
 
 
97
 
 
98
// Global because static getRef needs this too.
 
99
Q_GLOBAL_STATIC(QMutex, nettracker_mutex)
 
100
 
 
101
class NetTrackerThread : public QThread {
 
102
        Q_OBJECT
 
103
public:
 
104
        /** Get a reference to the NetTracker singleton.
 
105
            Calls to getInterfaces will immediately give valid results
 
106
         */
 
107
        static NetTrackerThread* getRef() {
 
108
                QMutexLocker locker(nettracker_mutex());
 
109
 
 
110
                if (!self) {
 
111
                        self = new NetTrackerThread();
 
112
                }
 
113
                self->refs++;
 
114
                return self;
 
115
        }
 
116
 
 
117
        /** Release reference.
 
118
         */
 
119
        void releaseRef() {
 
120
                QMutexLocker locker(nettracker_mutex());
 
121
 
 
122
                Q_ASSERT(refs > 0);
 
123
                refs--;
 
124
                if (refs <= 0) {
 
125
                        exit(0);
 
126
                        wait();
 
127
                        delete this;
 
128
                        self = 0;
 
129
                }
 
130
        }
 
131
 
 
132
        QList<NetInterfaceProvider::Info> getInterfaces() {
 
133
                return nettracker->getInterfaces();
 
134
        }
 
135
 
 
136
 
 
137
        ~NetTrackerThread() {
 
138
                // locked from caller
 
139
        }
 
140
 
 
141
 
 
142
signals:
 
143
        void updated();
 
144
private:
 
145
        NetTrackerThread() {
 
146
                // locked from caller
 
147
                refs = 0;
 
148
                moveToThread(QCoreApplication::instance()->thread());
 
149
                startMutex = new QMutex();
 
150
                {
 
151
                        QMutexLocker startLocker(startMutex);
 
152
                        start();
 
153
                        startCond.wait(startMutex); // wait for thread startup finished
 
154
                }
 
155
                delete startMutex;
 
156
                startMutex = 0;
 
157
        }
 
158
 
 
159
        void run() {
 
160
                {
 
161
                        QMutexLocker locker(startMutex);
 
162
 
 
163
                        nettracker = new NetTracker();
 
164
                        connect(nettracker, SIGNAL(updated()), SIGNAL(updated()), Qt::DirectConnection);
 
165
 
 
166
                        startCond.wakeOne(); // we're ready to serve.
 
167
                }
 
168
                exec();
 
169
                delete nettracker;
 
170
                nettracker = 0;
 
171
        }
 
172
 
 
173
private:
 
174
        QWaitCondition startCond;
 
175
        QMutex *startMutex;
 
176
        // these are all protected by global nettracker_mutex.
 
177
        int refs;
 
178
        static NetTrackerThread *self;
 
179
        NetTracker *nettracker;
 
180
};
 
181
 
 
182
NetTrackerThread *NetTrackerThread::self = 0;
 
183
 
 
184
 
 
185
//----------------------------------------------------------------------------
 
186
// NetInterface
 
187
//----------------------------------------------------------------------------
 
188
class NetInterfacePrivate : public QObject
 
189
{
 
190
        Q_OBJECT
 
191
public:
 
192
        friend class NetInterfaceManagerPrivate;
 
193
 
 
194
        NetInterface *q;
 
195
 
 
196
        QPointer<NetInterfaceManager> man;
 
197
        bool valid;
 
198
        QString id, name;
 
199
        QList<QHostAddress> addrs;
 
200
        QHostAddress gw;
 
201
 
 
202
        NetInterfacePrivate(NetInterface *_q) : QObject(_q), q(_q)
 
203
        {
 
204
                valid = false;
 
205
        }
 
206
 
 
207
        void doUnavailable()
 
208
        {
 
209
                if (!valid) return;
 
210
                valid = false;
 
211
                if (man.isNull()) return;
 
212
                man->unreg(q);
 
213
                emit q->unavailable();
 
214
        }
 
215
};
 
216
 
 
217
NetInterface::NetInterface(const QString &id, NetInterfaceManager *manager)
 
218
                                : QObject(manager)
 
219
{
 
220
        d = new NetInterfacePrivate(this);
 
221
        d->man = manager;
 
222
 
 
223
        NetInterfaceProvider::Info *info = (NetInterfaceProvider::Info *)d->man->reg(id, this);
 
224
        if (info) {
 
225
                d->valid = true;
 
226
                d->id = info->id;
 
227
                d->name = info->name;
 
228
                d->addrs = info->addresses;
 
229
                d->gw = info->gateway;
 
230
                delete info;
 
231
        }
 
232
}
 
233
 
 
234
NetInterface::~NetInterface()
 
235
{
 
236
        if (d->valid && !d->man.isNull()) d->man->unreg(this);
 
237
        delete d;
 
238
}
 
239
 
 
240
bool NetInterface::isValid() const
 
241
{
 
242
        return d->valid && !d->man.isNull();
 
243
}
 
244
 
 
245
QString NetInterface::id() const
 
246
{
 
247
        return d->id;
 
248
}
 
249
 
 
250
QString NetInterface::name() const
 
251
{
 
252
        return d->name;
 
253
}
 
254
 
 
255
QList<QHostAddress> NetInterface::addresses() const
 
256
{
 
257
        return d->addrs;
 
258
}
 
259
 
 
260
QHostAddress NetInterface::gateway() const
 
261
{
 
262
        return d->gw;
 
263
}
 
264
 
 
265
//----------------------------------------------------------------------------
 
266
// NetInterfaceManager
 
267
//----------------------------------------------------------------------------
 
268
class NetInterfaceManagerPrivate : public QObject
 
269
{
 
270
        Q_OBJECT
 
271
public:
 
272
        NetInterfaceManager *q;
 
273
 
 
274
        QList<NetInterfaceProvider::Info> info;
 
275
        QList<NetInterface*> listeners;
 
276
        NetTrackerThread *tracker;
 
277
 
 
278
        bool pending;
 
279
 
 
280
        NetInterfaceManagerPrivate(NetInterfaceManager *_q) : QObject(_q), q(_q)
 
281
        {
 
282
                tracker = NetTrackerThread::getRef();
 
283
                pending = false;
 
284
                connect(tracker, SIGNAL(updated()), SLOT(tracker_updated()));
 
285
        }
 
286
 
 
287
        ~NetInterfaceManagerPrivate() {
 
288
                tracker->releaseRef();
 
289
                tracker = 0;
 
290
        }
 
291
 
 
292
        static int lookup(const QList<NetInterfaceProvider::Info> &list, const QString &id)
 
293
        {
 
294
                for(int n = 0; n < list.count(); ++n) {
 
295
                        if(list[n].id == id) return n;
 
296
                }
 
297
                return -1;
 
298
        }
 
299
 
 
300
        static bool sameContent(const NetInterfaceProvider::Info &a, const NetInterfaceProvider::Info &b)
 
301
        {
 
302
                // assume ids are the same already
 
303
                return (a.name == b.name && a.isLoopback == b.isLoopback && a.addresses == b.addresses && a.gateway == b.gateway);
 
304
        }
 
305
 
 
306
        void do_update()
 
307
        {
 
308
                // grab the latest info
 
309
                QList<NetInterfaceProvider::Info> newinfo = tracker->getInterfaces();
 
310
 
 
311
                QStringList here_ids, gone_ids;
 
312
 
 
313
                // removed / changed
 
314
                for(int n = 0; n < info.count(); ++n)
 
315
                {
 
316
                        int i = lookup(newinfo, info[n].id);
 
317
                        // id is still here
 
318
                        if(i != -1) {
 
319
                                // content changed?
 
320
                                if(!sameContent(info[n], newinfo[i])) {
 
321
                                        gone_ids += info[n].id;
 
322
                                        here_ids += info[n].id;
 
323
                                }
 
324
                        } else { // id is gone
 
325
                                gone_ids += info[n].id;
 
326
                        }
 
327
                }
 
328
 
 
329
                // added
 
330
                for(int n = 0; n < newinfo.count(); ++n) {
 
331
                        int i = lookup(info, newinfo[n].id);
 
332
                        if(i == -1)
 
333
                                here_ids += newinfo[n].id;
 
334
                }
 
335
                info = newinfo;
 
336
 
 
337
                // announce gone
 
338
                for(int n = 0; n < gone_ids.count(); ++n) {
 
339
                        // work on a copy, just in case the list changes.
 
340
                        //   it is important to make the copy here, and not
 
341
                        //   outside the outer loop, in case the items
 
342
                        //   get deleted
 
343
                        QList<NetInterface*> list = listeners;
 
344
                        for(int i = 0; i < list.count(); ++i) {
 
345
                                if(list[i]->d->id == gone_ids[n]) {
 
346
                                        list[i]->d->doUnavailable();
 
347
                                }
 
348
                        }
 
349
                }
 
350
 
 
351
                // announce here
 
352
                for(int n = 0; n < here_ids.count(); ++n)
 
353
                        emit q->interfaceAvailable(here_ids[n]);
 
354
        }
 
355
 
 
356
public slots:
 
357
        void tracker_updated()
 
358
        {
 
359
                // collapse multiple updates by queuing up an update if there isn't any queued yet.
 
360
                if(!pending) {
 
361
                        QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
 
362
                        pending = true;
 
363
                }
 
364
        }
 
365
 
 
366
        void update()
 
367
        {
 
368
                pending = false;
 
369
                do_update();
 
370
        }
 
371
};
 
372
 
 
373
NetInterfaceManager::NetInterfaceManager(QObject *parent)
 
374
                                :QObject(parent)
 
375
{
 
376
        d = new NetInterfaceManagerPrivate(this);
 
377
}
 
378
 
 
379
NetInterfaceManager::~NetInterfaceManager()
 
380
{
 
381
        delete d;
 
382
}
 
383
 
 
384
QStringList NetInterfaceManager::interfaces() const
 
385
{
 
386
        d->info = d->tracker->getInterfaces();
 
387
        QStringList out;
 
388
        for(int n = 0; n < d->info.count(); ++n) {
 
389
                out += d->info[n].id;
 
390
        }
 
391
        return out;
 
392
}
 
393
 
 
394
QString NetInterfaceManager::interfaceForAddress(const QHostAddress &a)
 
395
{
 
396
        NetInterfaceManager netman;
 
397
        QStringList list = netman.interfaces();
 
398
        for(int n = 0; n < list.count(); ++n) {
 
399
                NetInterface iface(list[n], &netman);
 
400
                if(iface.addresses().contains(a)) return list[n];
 
401
        }
 
402
        return QString();
 
403
}
 
404
 
 
405
void *NetInterfaceManager::reg(const QString &id, NetInterface *i)
 
406
{
 
407
        for(int n = 0; n < d->info.count(); ++n) {
 
408
                if(d->info[n].id == id) {
 
409
                        d->listeners += i;
 
410
                        return new NetInterfaceProvider::Info(d->info[n]);
 
411
                }
 
412
        }
 
413
        return 0;
 
414
}
 
415
 
 
416
void NetInterfaceManager::unreg(NetInterface *i)
 
417
{
 
418
        d->listeners.removeAll(i);
 
419
}
 
420
 
 
421
}
 
422
 
 
423
#include "netinterface.moc"