KNemo – network interfaces monitor for systray
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

499 lines
16 KiB

  1. /* This file is part of KNemo
  2. Copyright (C) 2004, 2006 Percy Leonhardt <percy@eris23.de>
  3. KNemo is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU Library General Public License as
  5. published by the Free Software Foundation; either version 2 of
  6. the License, or (at your option) any later version.
  7. KNemo is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Library General Public License for more details.
  11. You should have received a copy of the GNU Library General Public License
  12. along with this library; see the file COPYING.LIB. If not, write to
  13. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  14. Boston, MA 02110-1301, USA.
  15. */
  16. #include <tqmap.h>
  17. #include <tqregexp.h>
  18. #include <tqstringlist.h>
  19. #include <kdebug.h>
  20. #include <kprocess.h>
  21. #include <kio/global.h>
  22. #include "nettoolsbackend.h"
  23. #include "config.h"
  24. NetToolsBackend::NetToolsBackend( TQDict<Interface>& interfaces )
  25. : TQObject(),
  26. BackendBase( interfaces ),
  27. mRouteProcess(0L),
  28. mIfconfigProcess(0L),
  29. mIwconfigProcess(0L)
  30. {
  31. }
  32. NetToolsBackend::~NetToolsBackend()
  33. {
  34. if ( mRouteProcess )
  35. {
  36. mRouteProcess->kill();
  37. delete mRouteProcess;
  38. }
  39. if ( mIfconfigProcess )
  40. {
  41. mIfconfigProcess->kill();
  42. delete mIfconfigProcess;
  43. }
  44. if ( mIwconfigProcess )
  45. {
  46. mIwconfigProcess->kill();
  47. delete mIwconfigProcess;
  48. }
  49. }
  50. BackendBase* NetToolsBackend::createInstance( TQDict<Interface>& interfaces )
  51. {
  52. return new NetToolsBackend( interfaces );
  53. }
  54. void NetToolsBackend::update()
  55. {
  56. if ( !mIfconfigProcess )
  57. {
  58. mIfconfigStdout = TQString();
  59. mIfconfigProcess = new KProcess();
  60. mIfconfigProcess->setEnvironment( "LANG", "C" );
  61. mIfconfigProcess->setEnvironment( "LC_ALL", "C" );
  62. *mIfconfigProcess << PATH_IFCONFIG << "-a";
  63. connect( mIfconfigProcess, TQT_SIGNAL( receivedStdout( KProcess*, char*, int ) ),
  64. this, TQT_SLOT( ifconfigProcessStdout( KProcess*, char*, int ) ) );
  65. connect( mIfconfigProcess, TQT_SIGNAL( processExited( KProcess* ) ),
  66. this, TQT_SLOT( ifconfigProcessExited( KProcess* ) ) );
  67. if ( !mIfconfigProcess->start( KProcess::NotifyOnExit, KProcess::Stdout ) )
  68. {
  69. delete mIfconfigProcess;
  70. mIfconfigProcess = 0L;
  71. }
  72. }
  73. #ifdef PATH_IWCONFIG
  74. if ( !mIwconfigProcess )
  75. {
  76. mIwconfigStdout = TQString();
  77. mIwconfigProcess = new KProcess();
  78. mIwconfigProcess->setEnvironment( "LANG", "C" );
  79. mIwconfigProcess->setEnvironment( "LC_ALL", "C" );
  80. *mIwconfigProcess << PATH_IWCONFIG;
  81. connect( mIwconfigProcess, TQT_SIGNAL( receivedStdout( KProcess*, char*, int ) ),
  82. this, TQT_SLOT( iwconfigProcessStdout( KProcess*, char*, int ) ) );
  83. connect( mIwconfigProcess, TQT_SIGNAL( receivedStderr( KProcess*, char*, int ) ),
  84. this, TQT_SLOT( iwconfigProcessStdout( KProcess*, char*, int ) ) );
  85. connect( mIwconfigProcess, TQT_SIGNAL( processExited( KProcess* ) ),
  86. this, TQT_SLOT( iwconfigProcessExited( KProcess* ) ) );
  87. if ( !mIwconfigProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput ) )
  88. {
  89. delete mIwconfigProcess;
  90. mIwconfigProcess = 0L;
  91. }
  92. }
  93. #endif
  94. #ifdef PATH_ROUTE
  95. if ( !mRouteProcess )
  96. {
  97. mRouteStdout = TQString();
  98. mRouteProcess = new KProcess();
  99. mRouteProcess->setEnvironment( "LANG", "C" );
  100. mRouteProcess->setEnvironment( "LC_ALL", "C" );
  101. *mRouteProcess << PATH_ROUTE << "-n";
  102. connect( mRouteProcess, TQT_SIGNAL( receivedStdout( KProcess*, char*, int ) ),
  103. this, TQT_SLOT( routeProcessStdout( KProcess*, char*, int ) ) );
  104. connect( mRouteProcess, TQT_SIGNAL( receivedStderr( KProcess*, char*, int ) ),
  105. this, TQT_SLOT( routeProcessStdout( KProcess*, char*, int ) ) );
  106. connect( mRouteProcess, TQT_SIGNAL( processExited( KProcess* ) ),
  107. this, TQT_SLOT( routeProcessExited( KProcess* ) ) );
  108. if ( !mRouteProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput ) )
  109. {
  110. delete mRouteProcess;
  111. mRouteProcess = 0L;
  112. }
  113. }
  114. #endif
  115. }
  116. void NetToolsBackend::routeProcessExited( KProcess* process )
  117. {
  118. if ( process == mRouteProcess )
  119. {
  120. mRouteProcess->deleteLater(); // we're in a slot connected to mRouteProcess
  121. mRouteProcess = 0L;
  122. parseRouteOutput();
  123. }
  124. }
  125. void NetToolsBackend::routeProcessStdout( KProcess*, char* buffer, int buflen )
  126. {
  127. mRouteStdout += TQString::fromLatin1( buffer, buflen );
  128. }
  129. void NetToolsBackend::ifconfigProcessExited( KProcess* process )
  130. {
  131. if ( process == mIfconfigProcess )
  132. {
  133. delete mIfconfigProcess;
  134. mIfconfigProcess = 0L;
  135. parseIfconfigOutput();
  136. }
  137. }
  138. void NetToolsBackend::ifconfigProcessStdout( KProcess*, char* buffer, int buflen )
  139. {
  140. mIfconfigStdout += TQString::fromLatin1( buffer, buflen );
  141. }
  142. void NetToolsBackend::iwconfigProcessExited( KProcess* process )
  143. {
  144. if ( process == mIwconfigProcess )
  145. {
  146. delete mIwconfigProcess;
  147. mIwconfigProcess = 0L;
  148. parseIwconfigOutput();
  149. }
  150. }
  151. void NetToolsBackend::iwconfigProcessStdout( KProcess*, char* buffer, int buflen )
  152. {
  153. mIwconfigStdout += TQString::fromLatin1( buffer, buflen );
  154. }
  155. void NetToolsBackend::parseIfconfigOutput()
  156. {
  157. /* mIfconfigStdout contains the complete output of 'ifconfig' which we
  158. * are going to parse here.
  159. */
  160. TQMap<TQString, TQString> configs;
  161. TQStringList ifList = TQStringList::split( "\n\n", mIfconfigStdout );
  162. TQStringList::Iterator it;
  163. for ( it = ifList.begin(); it != ifList.end(); ++it )
  164. {
  165. int index = ( *it ).find( ' ' );
  166. if ( index == -1 )
  167. continue;
  168. TQString key = ( *it ).left( index );
  169. configs[key] = ( *it ).mid( index );
  170. }
  171. /* We loop over the interfaces the user wishs to monitor.
  172. * If we find the interface in the output of 'ifconfig'
  173. * we update its data, otherwise we mark it as
  174. * 'not existing'.
  175. */
  176. TQDictIterator<Interface> ifIt( mInterfaces );
  177. for ( ; ifIt.current(); ++ifIt )
  178. {
  179. TQString key = ifIt.currentKey();
  180. Interface* interface = ifIt.current();
  181. if ( configs.find( key ) == configs.end() )
  182. {
  183. // The interface does not exist. Meaning the driver
  184. // isn't loaded and/or the interface has not been created.
  185. interface->getData().existing = false;
  186. interface->getData().available = false;
  187. }
  188. // JJ 2005-07-18: use RUNNING instead of UP to detect whether interface is connected
  189. else if ( !configs[key].contains( "inet " ) ||
  190. !configs[key].contains( "RUNNING" ) )
  191. {
  192. // The interface is up or has an IP assigned but not both
  193. interface->getData().existing = true;
  194. interface->getData().available = false;
  195. }
  196. else
  197. {
  198. // ...determine the type of the interface
  199. if ( configs[key].contains( "Ethernet" ) )
  200. interface->setType( Interface::ETHERNET );
  201. else
  202. interface->setType( Interface::PPP );
  203. // Update the interface.
  204. interface->getData().existing = true;
  205. interface->getData().available = true;
  206. updateInterfaceData( configs[key], interface->getData(), interface->getType() );
  207. }
  208. }
  209. updateComplete();
  210. }
  211. void NetToolsBackend::updateInterfaceData( TQString& config, InterfaceData& data, int type )
  212. {
  213. TQRegExp regExp( ".*RX.*:(\\d+).*:\\d+.*:\\d+.*:\\d+" );
  214. if ( regExp.search( config ) > -1 )
  215. data.rxPackets = regExp.cap( 1 ).toULong();
  216. regExp.setPattern( ".*TX.*:(\\d+).*:\\d+.*:\\d+.*:\\d+" );
  217. if ( regExp.search( config ) > -1 )
  218. data.txPackets = regExp.cap( 1 ).toULong();
  219. regExp.setPattern( "RX bytes:(\\d+)\\s*\\(\\d+\\.\\d+\\s*\\w+\\)" );
  220. if ( regExp.search( config ) > -1 )
  221. {
  222. // We count the traffic on ourself to avoid an overflow after
  223. // 4GB of traffic.
  224. unsigned long currentRxBytes = regExp.cap( 1 ).toULong();
  225. if ( currentRxBytes < data.prevRxBytes )
  226. {
  227. // there was an overflow
  228. if ( type == Interface::ETHERNET )
  229. {
  230. // This makes data counting more accurate but will not work
  231. // for interfaces that reset the transfered data to zero
  232. // when deactivated like ppp does.
  233. data.rxBytes += 0xFFFFFFFF - data.prevRxBytes;
  234. }
  235. data.prevRxBytes = 0L;
  236. }
  237. if ( data.rxBytes == 0L )
  238. {
  239. // on startup set to currently received bytes
  240. data.rxBytes = currentRxBytes;
  241. // this is new: KNemo only counts the traffic transfered
  242. // while it is running. Important to not falsify statistics!
  243. data.prevRxBytes = currentRxBytes;
  244. }
  245. else
  246. // afterwards only add difference to previous number of bytes
  247. data.rxBytes += currentRxBytes - data.prevRxBytes;
  248. data.incomingBytes = currentRxBytes - data.prevRxBytes;
  249. data.prevRxBytes = currentRxBytes;
  250. data.rxString = KIO::convertSize( data.rxBytes );
  251. }
  252. regExp.setPattern( "TX bytes:(\\d+)\\s*\\(\\d+\\.\\d+\\s*\\w+\\)" );
  253. if ( regExp.search( config ) > -1 )
  254. {
  255. // We count the traffic on ourself to avoid an overflow after
  256. // 4GB of traffic.
  257. unsigned long currentTxBytes = regExp.cap( 1 ).toULong();
  258. if ( currentTxBytes < data.prevTxBytes )
  259. {
  260. // there was an overflow
  261. if ( type == Interface::ETHERNET )
  262. {
  263. // This makes data counting more accurate but will not work
  264. // for interfaces that reset the transfered data to zero
  265. // when deactivated like ppp does.
  266. data.txBytes += 0xFFFFFFFF - data.prevTxBytes;
  267. }
  268. data.prevTxBytes = 0L;
  269. }
  270. if ( data.txBytes == 0L )
  271. {
  272. // on startup set to currently transmitted bytes
  273. data.txBytes = currentTxBytes;
  274. // this is new: KNemo only counts the traffic transfered
  275. // while it is running. Important to not falsify statistics!
  276. data.prevTxBytes = currentTxBytes;
  277. }
  278. else
  279. // afterwards only add difference to previous number of bytes
  280. data.txBytes += currentTxBytes - data.prevTxBytes;
  281. data.outgoingBytes = currentTxBytes - data.prevTxBytes;
  282. data.prevTxBytes = currentTxBytes;
  283. data.txString = KIO::convertSize( data.txBytes );
  284. }
  285. regExp.setPattern( "inet\\s+\\w+:(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})" );
  286. if ( regExp.search( config ) > -1 )
  287. data.ipAddress = regExp.cap( 1 );
  288. 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})" );
  289. if ( regExp.search( config ) > -1 )
  290. {
  291. data.broadcastAddress = regExp.cap( 2 );
  292. data.subnetMask = regExp.cap( 3 );
  293. }
  294. if ( type == Interface::ETHERNET )
  295. {
  296. regExp.setPattern( "(.{2}:.{2}:.{2}:.{2}:.{2}:.{2})" );
  297. if ( regExp.search( config ) > -1 )
  298. data.hwAddress = regExp.cap( 1 );
  299. }
  300. else if ( type == Interface::PPP )
  301. {
  302. 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})" );
  303. if ( regExp.search( config ) > -1 )
  304. data.ptpAddress = regExp.cap( 2 );
  305. }
  306. }
  307. void NetToolsBackend::parseIwconfigOutput()
  308. {
  309. /* mIwconfigStdout contains the complete output of 'iwconfig' which we
  310. * are going to parse here.
  311. */
  312. TQMap<TQString, TQString> configs;
  313. TQStringList ifList = TQStringList::split( "\n\n", mIwconfigStdout );
  314. TQStringList::Iterator it;
  315. for ( it = ifList.begin(); it != ifList.end(); ++it )
  316. {
  317. int index = ( *it ).find( ' ' );
  318. if ( index == -1 )
  319. continue;
  320. TQString key = ( *it ).left( index );
  321. configs[key] = ( *it ).mid( index );
  322. }
  323. /* We loop over the interfaces the user wishs to monitor.
  324. * If we find the interface in the output of 'iwconfig'
  325. * we update its data.
  326. */
  327. TQDictIterator<Interface> ifIt( mInterfaces );
  328. for ( ; ifIt.current(); ++ifIt )
  329. {
  330. TQString key = ifIt.currentKey();
  331. Interface* interface = ifIt.current();
  332. if ( configs.find( key ) == configs.end() )
  333. {
  334. // The interface was not found.
  335. continue;
  336. }
  337. else if ( configs[key].contains( "no wireless extensions" ) )
  338. {
  339. // The interface isn't a wireless device.
  340. interface->getData().wirelessDevice = false;
  341. }
  342. else
  343. {
  344. // Update the wireless data of the interface.
  345. interface->getData().wirelessDevice = true;
  346. updateWirelessData( configs[key], interface->getWirelessData() );
  347. }
  348. }
  349. }
  350. void NetToolsBackend::updateWirelessData( TQString& config, WirelessData& data )
  351. {
  352. TQRegExp regExp( "ESSID:([^\"][\\S]*)" );
  353. if ( regExp.search( config ) > -1 )
  354. data.essid = regExp.cap( 1 );
  355. else
  356. {
  357. regExp.setPattern( "ESSID:\"([^\"]*)" );
  358. if ( regExp.search( config ) > -1 )
  359. data.essid = regExp.cap( 1 );
  360. else
  361. data.essid = TQString();
  362. }
  363. regExp.setPattern( "Mode:(\\w*)" );
  364. if ( regExp.search( config ) > -1 )
  365. data.mode = regExp.cap( 1 );
  366. regExp.setPattern( "Frequency:([\\w|\\.]*\\s*\\w*)" );
  367. if ( regExp.search( config ) > -1 )
  368. {
  369. data.frequency = regExp.cap( 1 );
  370. data.channel = "-";
  371. }
  372. else
  373. {
  374. data.frequency = "-";
  375. regExp.setPattern( "Channel:(\\d*)" );
  376. if ( regExp.search( config ) > -1 )
  377. data.channel = regExp.cap( 1 );
  378. else
  379. data.channel = "-";
  380. }
  381. regExp.setPattern( "Bit Rate[=:](\\d*\\s*[\\w/]*)" );
  382. if ( regExp.search( config ) > -1 )
  383. data.bitRate = regExp.cap( 1 );
  384. regExp.setPattern( "(.{2}:.{2}:.{2}:.{2}:.{2}:.{2})" );
  385. if ( regExp.search( config ) > -1 )
  386. data.accessPoint = regExp.cap( 1 );
  387. regExp.setPattern( "Nickname:\"(\\w*)\"" );
  388. if ( regExp.search( config ) > -1 )
  389. data.nickName = regExp.cap( 1 );
  390. regExp.setPattern( "Link Quality[=:]([\\d]*)" );
  391. if ( regExp.search( config ) > -1 )
  392. data.linkQuality = regExp.cap( 1 );
  393. regExp.setPattern( "Encryption key:" );
  394. if ( regExp.search( config ) > -1 )
  395. {
  396. regExp.setPattern( "Encryption key:off" );
  397. if ( regExp.search( config ) > -1 )
  398. {
  399. data.encryption = false;
  400. }
  401. else
  402. {
  403. data.encryption = true;
  404. }
  405. }
  406. else
  407. {
  408. data.encryption = false;
  409. }
  410. }
  411. void NetToolsBackend::parseRouteOutput()
  412. {
  413. /* mRouteStdout contains the complete output of 'route' which we
  414. * are going to parse here.
  415. */
  416. TQMap<TQString, TQStringList> configs;
  417. TQStringList routeList = TQStringList::split( "\n", mRouteStdout );
  418. TQStringList::Iterator it;
  419. for ( it = routeList.begin(); it != routeList.end(); ++it )
  420. {
  421. TQStringList routeParameter = TQStringList::split( " ", *it );
  422. if ( routeParameter.count() < 8 ) // no routing entry
  423. continue;
  424. if ( routeParameter[0] != "0.0.0.0" ) // no default route
  425. continue;
  426. configs[routeParameter[7]] = routeParameter;
  427. }
  428. /* We loop over the interfaces the user wishs to monitor.
  429. * If we find the interface in the output of 'route' we update
  430. * the data of the interface.
  431. */
  432. TQDictIterator<Interface> ifIt( mInterfaces );
  433. for ( ; ifIt.current(); ++ifIt )
  434. {
  435. TQString key = ifIt.currentKey();
  436. Interface* interface = ifIt.current();
  437. if ( configs.find( key ) != configs.end() )
  438. {
  439. // Update the default gateway.
  440. TQStringList routeParameter = configs[key];
  441. interface->getData().defaultGateway = routeParameter[1];
  442. }
  443. else
  444. {
  445. // Reset the default gateway.
  446. interface->getData().defaultGateway = TQString();
  447. }
  448. }
  449. }