1
/* This file is part of KNemo
2
Copyright (C) 2004, 2006 Percy Leonhardt <percy@eris23.de>
3
Copyright (C) 2009 John Stamp <jstamp@users.sourceforge.net>
5
KNemo is free software; you can redistribute it and/or modify
6
it under the terms of the GNU Library General Public License as
7
published by the Free Software Foundation; either version 2 of
8
the License, or (at your option) any later version.
10
KNemo 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
13
GNU Library General Public License for more details.
15
You should have received a copy of the GNU Library General Public License
16
along with this library; see the file COPYING.LIB. If not, write to
17
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
Boston, MA 02110-1301, USA.
22
#include <kio/global.h>
25
#include "nettoolsbackend.h"
27
NetToolsBackend::NetToolsBackend( QHash<QString, Interface *>& interfaces )
29
BackendBase( interfaces ),
36
NetToolsBackend::~NetToolsBackend()
40
mRouteProcess->kill();
43
if ( mIfconfigProcess )
45
mIfconfigProcess->kill();
46
delete mIfconfigProcess;
48
if ( mIwconfigProcess )
50
mIwconfigProcess->kill();
51
delete mIwconfigProcess;
55
BackendBase* NetToolsBackend::createInstance( QHash<QString, Interface *>& interfaces )
57
return new NetToolsBackend( interfaces );
60
void NetToolsBackend::update()
62
if ( !mIfconfigProcess )
64
mIfconfigStdout = QString::null;
65
mIfconfigProcess = new KProcess();
66
mIfconfigProcess->setEnv( "LANG", "C" );
67
mIfconfigProcess->setEnv( "LC_ALL", "C" );
68
mIfconfigProcess->setOutputChannelMode( KProcess::OnlyStdoutChannel );
69
*mIfconfigProcess << PATH_IFCONFIG << "-a";
70
connect( mIfconfigProcess, SIGNAL( readyReadStandardOutput() ) ,
71
this, SLOT( ifconfigProcessStdout() ) );
72
connect( mIfconfigProcess, SIGNAL( finished( int, QProcess::ExitStatus ) ),
73
this, SLOT( ifconfigProcessExited( int, QProcess::ExitStatus ) ) );
75
mIfconfigProcess->start();
79
if ( !mIwconfigProcess )
81
mIwconfigStdout = QString::null;
82
mIwconfigProcess = new KProcess();
83
mIwconfigProcess->setOutputChannelMode( KProcess::MergedChannels );
84
mIwconfigProcess->setEnv( "LANG", "C" );
85
mIwconfigProcess->setEnv( "LC_ALL", "C" );
86
*mIwconfigProcess << PATH_IWCONFIG;
87
connect( mIwconfigProcess, SIGNAL( readyReadStandardOutput() ),
88
this, SLOT( iwconfigProcessStdout() ) );
89
connect( mIwconfigProcess, SIGNAL( finished( int, QProcess::ExitStatus ) ),
90
this, SLOT( iwconfigProcessExited( int, QProcess::ExitStatus ) ) );
92
mIwconfigProcess->start();
99
mRouteStdout = QString::null;
100
mRouteProcess = new KProcess();
101
mRouteProcess->setOutputChannelMode( KProcess::MergedChannels );
102
mRouteProcess->setEnv( "LANG", "C" );
103
mRouteProcess->setEnv( "LC_ALL", "C" );
104
*mRouteProcess << PATH_ROUTE << "-n";
105
connect( mRouteProcess, SIGNAL( readyReadStandardOutput() ),
106
this, SLOT( routeProcessStdout() ) );
107
connect( mRouteProcess, SIGNAL( finished( int, QProcess::ExitStatus ) ),
108
this, SLOT( routeProcessExited( int, QProcess::ExitStatus ) ) );
110
mRouteProcess->start();
115
QString NetToolsBackend::getDefaultRouteIface()
120
droute.setOutputChannelMode( KProcess::MergedChannels );
121
droute.setEnv( "LANG", "C" );
122
droute.setEnv( "LC_ALL", "C" );
123
droute << PATH_ROUTE << "-n";
125
QString routeStdout = droute.readAllStandardOutput();
127
QStringList routeList = routeStdout.split( "\n" );
128
QStringListIterator it( routeList );
129
while ( it.hasNext() )
131
QStringList routeParameter = it.next().split( " ", QString::SkipEmptyParts );
132
if ( routeParameter.count() < 8 ) // no routing entry
134
if ( routeParameter[0] != "0.0.0.0" ) // no default route
136
if ( routeParameter[7] == "lo" )
138
iface = routeParameter[7]; // default route interface
145
void NetToolsBackend::routeProcessExited( int, QProcess::ExitStatus )
147
mRouteProcess->deleteLater(); // we're in a slot connected to mRouteProcess
152
void NetToolsBackend::routeProcessStdout()
154
mRouteStdout += mRouteProcess->readAllStandardOutput();
157
void NetToolsBackend::ifconfigProcessExited( int, QProcess::ExitStatus )
159
mIfconfigProcess->deleteLater();
160
mIfconfigProcess = 0L;
161
parseIfconfigOutput();
164
void NetToolsBackend::ifconfigProcessStdout()
166
mIfconfigStdout += mIfconfigProcess->readAllStandardOutput();
169
void NetToolsBackend::iwconfigProcessExited( int, QProcess::ExitStatus )
171
mIwconfigProcess->deleteLater();
172
mIwconfigProcess = 0L;
173
parseIwconfigOutput();
176
void NetToolsBackend::iwconfigProcessStdout()
178
mIwconfigStdout += mIwconfigProcess->readAllStandardOutput();
181
void NetToolsBackend::parseIfconfigOutput()
183
/* mIfconfigStdout contains the complete output of 'ifconfig' which we
184
* are going to parse here.
186
QMap<QString, QString> configs;
187
QStringList ifList = mIfconfigStdout.split( "\n\n" );
188
QStringList::Iterator it;
189
for ( it = ifList.begin(); it != ifList.end(); ++it )
191
int index = ( *it ).indexOf( ' ' );
194
QString key = ( *it ).left( index );
195
configs[key] = ( *it ).mid( index );
198
/* We loop over the interfaces the user wishs to monitor.
199
* If we find the interface in the output of 'ifconfig'
200
* we update its data, otherwise we mark it as
203
foreach ( QString key, mInterfaces.keys() )
205
Interface *interface = mInterfaces.value( key );
206
if ( configs.find( key ) == configs.end() )
208
// The interface does not exist. Meaning the driver
209
// isn't loaded and/or the interface has not been created.
210
interface->getData().existing = false;
211
interface->getData().available = false;
213
// JJ 2005-07-18: use RUNNING instead of UP to detect whether interface is connected
214
else if ( !configs[key].contains( "inet " ) ||
215
!configs[key].contains( "RUNNING" ) )
217
// The interface is up or has an IP assigned but not both
218
interface->getData().existing = true;
219
interface->getData().available = false;
223
// ...determine the type of the interface
224
if ( configs[key].contains( "Ethernet" ) )
225
interface->setType( Interface::ETHERNET );
227
interface->setType( Interface::PPP );
229
// Update the interface.
230
interface->getData().existing = true;
231
interface->getData().available = true;
232
updateInterfaceData( configs[key], interface->getData(), interface->getType() );
238
void NetToolsBackend::updateInterfaceData( QString& config, InterfaceData& data, int type )
240
QRegExp regExp( ".*RX.*:(\\d+).*:\\d+.*:\\d+.*:\\d+" );
241
if ( regExp.indexIn( config ) > -1 )
242
data.rxPackets = regExp.cap( 1 ).toULong();
244
regExp.setPattern( ".*TX.*:(\\d+).*:\\d+.*:\\d+.*:\\d+" );
245
if ( regExp.indexIn( config ) > -1 )
246
data.txPackets = regExp.cap( 1 ).toULong();
248
regExp.setPattern( "RX bytes:(\\d+)\\s*\\(\\d+\\.\\d+\\s*\\w+\\)" );
249
if ( regExp.indexIn( config ) > -1 )
251
// We count the traffic on ourself to avoid an overflow after
253
unsigned long currentRxBytes = regExp.cap( 1 ).toULong();
254
if ( currentRxBytes < data.prevRxBytes )
256
// there was an overflow
257
if ( type == Interface::ETHERNET )
259
// This makes data counting more accurate but will not work
260
// for interfaces that reset the transfered data to zero
261
// when deactivated like ppp does.
262
data.rxBytes += 0xFFFFFFFF - data.prevRxBytes;
264
data.prevRxBytes = 0L;
266
if ( data.rxBytes == 0L )
268
// on startup set to currently received bytes
269
data.rxBytes = currentRxBytes;
270
// this is new: KNemo only counts the traffic transfered
271
// while it is running. Important to not falsify statistics!
272
data.prevRxBytes = currentRxBytes;
275
// afterwards only add difference to previous number of bytes
276
data.rxBytes += currentRxBytes - data.prevRxBytes;
278
data.incomingBytes = currentRxBytes - data.prevRxBytes;
279
data.prevRxBytes = currentRxBytes;
280
data.rxString = KIO::convertSize( data.rxBytes );
283
regExp.setPattern( "TX bytes:(\\d+)\\s*\\(\\d+\\.\\d+\\s*\\w+\\)" );
284
if ( regExp.indexIn( config ) > -1 )
286
// We count the traffic on ourself to avoid an overflow after
288
unsigned long currentTxBytes = regExp.cap( 1 ).toULong();
289
if ( currentTxBytes < data.prevTxBytes )
291
// there was an overflow
292
if ( type == Interface::ETHERNET )
294
// This makes data counting more accurate but will not work
295
// for interfaces that reset the transfered data to zero
296
// when deactivated like ppp does.
297
data.txBytes += 0xFFFFFFFF - data.prevTxBytes;
299
data.prevTxBytes = 0L;
301
if ( data.txBytes == 0L )
303
// on startup set to currently transmitted bytes
304
data.txBytes = currentTxBytes;
305
// this is new: KNemo only counts the traffic transfered
306
// while it is running. Important to not falsify statistics!
307
data.prevTxBytes = currentTxBytes;
310
// afterwards only add difference to previous number of bytes
311
data.txBytes += currentTxBytes - data.prevTxBytes;
313
data.outgoingBytes = currentTxBytes - data.prevTxBytes;
314
data.prevTxBytes = currentTxBytes;
315
data.txString = KIO::convertSize( data.txBytes );
318
regExp.setPattern( "inet\\s+\\w+:(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})" );
319
if ( regExp.indexIn( config ) > -1 )
320
data.ipAddress = regExp.cap( 1 );
322
regExp.setPattern( "(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}).*(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}).*(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})" );
323
if ( regExp.indexIn( config ) > -1 )
325
data.broadcastAddress = regExp.cap( 2 );
326
data.subnetMask = regExp.cap( 3 );
329
if ( type == Interface::ETHERNET )
331
regExp.setPattern( "(.{2}:.{2}:.{2}:.{2}:.{2}:.{2})" );
332
if ( regExp.indexIn( config ) > -1 )
333
data.hwAddress = regExp.cap( 1 );
335
else if ( type == Interface::PPP )
337
regExp.setPattern( "(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}).*(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}).*(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})" );
338
if ( regExp.indexIn( config ) > -1 )
339
data.ptpAddress = regExp.cap( 2 );
343
void NetToolsBackend::parseIwconfigOutput()
345
/* mIwconfigStdout contains the complete output of 'iwconfig' which we
346
* are going to parse here.
348
QMap<QString, QString> configs;
349
QStringList ifList = mIwconfigStdout.split( "\n\n" );
350
QStringList::Iterator it;
351
for ( it = ifList.begin(); it != ifList.end(); ++it )
353
int index = ( *it ).indexOf( ' ' );
356
QString key = ( *it ).left( index );
357
configs[key] = ( *it ).mid( index );
360
/* We loop over the interfaces the user wishs to monitor.
361
* If we find the interface in the output of 'iwconfig'
362
* we update its data.
364
foreach ( QString key, mInterfaces.keys() )
366
Interface* interface = mInterfaces.value( key );
368
if ( configs.find( key ) == configs.end() )
370
// The interface was not found.
373
else if ( configs[key].contains( "no wireless extensions" ) )
375
// The interface isn't a wireless device.
376
interface->getData().wirelessDevice = false;
380
// Update the wireless data of the interface.
381
interface->getData().wirelessDevice = true;
382
updateWirelessData( key, configs[key], interface->getWirelessData() );
387
void NetToolsBackend::updateWirelessData( QString& ifaceName, QString& config, WirelessData& data )
389
QRegExp regExp( "ESSID:([^\"][\\S]*)" );
390
if ( regExp.indexIn( config ) > -1 )
391
data.essid = regExp.cap( 1 );
394
regExp.setPattern( "ESSID:\"([^\"]*)" );
395
if ( regExp.indexIn( config ) > -1 )
396
data.essid = regExp.cap( 1 );
398
data.essid = QString::null;
401
regExp.setPattern( "Mode:(\\w*)" );
402
if ( regExp.indexIn( config ) > -1 )
403
data.mode = regExp.cap( 1 );
405
regExp.setPattern( "Frequency:([\\w|\\.]*\\s*\\w*)" );
406
if ( regExp.indexIn( config ) > -1 )
408
data.frequency = regExp.cap( 1 );
413
data.frequency = "-";
414
regExp.setPattern( "Channel:(\\d*)" );
415
if ( regExp.indexIn( config ) > -1 )
416
data.channel = regExp.cap( 1 );
421
regExp.setPattern( "Bit Rate[=:](\\d*\\s*[\\w/]*)" );
422
if ( regExp.indexIn( config ) > -1 )
423
data.bitRate = regExp.cap( 1 );
425
regExp.setPattern( "(.{2}:.{2}:.{2}:.{2}:.{2}:.{2})" );
426
if ( regExp.indexIn( config ) > -1 )
427
data.accessPoint = regExp.cap( 1 );
429
data.accessPoint.clear();
431
regExp.setPattern( "Nickname:\"(\\w*)\"" );
432
if ( regExp.indexIn( config ) > -1 )
433
data.nickName = regExp.cap( 1 );
435
regExp.setPattern( "Link Quality[=:]([\\d]*)(\\/([\\d]+))?" );
436
if ( regExp.indexIn( config ) > -1 )
438
if ( regExp.numCaptures() == 3 && !regExp.cap( 3 ).isEmpty() )
440
int maxQual = regExp.cap( 3 ).toInt();
442
data.linkQuality = QString( "%1%" ).arg( 100 * regExp.cap( 1 ).toInt() / maxQual );
444
data.linkQuality = "0";
447
data.linkQuality = regExp.cap( 1 );
451
if ( data.accessPoint != data.prevAccessPoint )
453
/* Reset encryption status for new access point */
454
data.encryption = false;
455
data.prevAccessPoint = data.accessPoint;
457
/* We only use left-over wireless scans to prevent doing a new scan every
458
* polling period. This requires that we run iwlist once per wireless
459
* device. If our current access point disappears from the results then
460
* parseWirelessEncryption will use the last encryption status until the
461
* results are updated again. */
462
KProcess iwlistProcess;
463
iwlistProcess.setOutputChannelMode( KProcess::MergedChannels );
464
iwlistProcess.setEnv( "LANG", "C" );
465
iwlistProcess.setEnv( "LC_ALL", "C" );
466
iwlistProcess << PATH_IWLIST << ifaceName << "scan" << "last";
468
iwlistProcess.execute();
469
QString iwlistOutput = iwlistProcess.readAllStandardOutput();
470
parseWirelessEncryption( iwlistOutput, data );
474
void NetToolsBackend::parseWirelessEncryption( QString& config, WirelessData& data )
476
QStringList apList = config.split( "Cell [0-9]{2} - ", QString::SkipEmptyParts );
477
foreach( QString ap, apList )
479
QRegExp regExp( "Address: (.{2}:.{2}:.{2}:.{2}:.{2}:.{2})" );
480
if ( regExp.indexIn( ap ) > -1 && regExp.cap( 1 ) == data.accessPoint )
482
regExp.setPattern( "Encryption key:" );
483
if ( regExp.indexIn( ap ) > -1 )
485
regExp.setPattern( "Encryption key:off" );
486
if ( regExp.indexIn( ap ) > -1 )
487
data.encryption = false;
489
data.encryption = true;
496
void NetToolsBackend::parseRouteOutput()
498
/* mRouteStdout contains the complete output of 'route' which we
499
* are going to parse here.
501
QHash<QString, QStringList> configs;
502
QStringList routeList = mRouteStdout.split( "\n" );
503
foreach ( QString it, routeList )
505
QStringList routeParameter = it.split( " ", QString::SkipEmptyParts );
506
if ( routeParameter.count() < 8 ) // no routing entry
508
if ( routeParameter[0] != "0.0.0.0" ) // no default route
510
configs.insert( routeParameter[7], routeParameter );
513
/* We loop over the interfaces the user wishs to monitor.
514
* If we find the interface in the output of 'route' we update
515
* the data of the interface.
517
foreach ( QString key, mInterfaces.keys() )
519
Interface* interface = mInterfaces.value( key );
521
if ( configs.contains( key ) )
523
// Update the default gateway.
524
QStringList routeParameter = configs[key];
525
interface->getData().defaultGateway = routeParameter[1];
529
// Reset the default gateway.
530
interface->getData().defaultGateway = QString::null;