/* This file is part of KNemo
Copyright (C) 2004, 2006 Percy Leonhardt <percy@eris23.de>
KNemo is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
KNemo is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifdef __FreeBSD__
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <net/ethernet.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_mib.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include <qmap.h>
#include <qregexp.h>
#include <qstringlist.h>
#include <kdebug.h>
#include <kprocess.h>
#include <kio/global.h>
#include "interfaceupdater.h"
#include "config.h"
InterfaceUpdater::InterfaceUpdater( QDict<Interface>& interfaceDict )
: QObject(),
mRouteProcess(0L),
mIfconfigProcess(0L),
mIwconfigProcess(0L),
mInterfaceDict( interfaceDict )
{
}
InterfaceUpdater::~InterfaceUpdater()
{
#ifndef Q_OS_FREEBSD
if ( mRouteProcess )
{
mRouteProcess->kill();
delete mRouteProcess;
}
if ( mIfconfigProcess )
{
mIfconfigProcess->kill();
delete mIfconfigProcess;
}
if ( mIwconfigProcess )
{
mIwconfigProcess->kill();
delete mIwconfigProcess;
}
#endif
}
void InterfaceUpdater::checkConfig()
{
#ifndef Q_OS_FREEBSD
if ( !mIfconfigProcess )
{
mIfconfigStdout = QString::null;
mIfconfigProcess = new KProcess();
mIfconfigProcess->setEnvironment( "LANG", "C" );
mIfconfigProcess->setEnvironment( "LC_ALL", "C" );
*mIfconfigProcess << PATH_IFCONFIG << "-a";
connect( mIfconfigProcess, SIGNAL( receivedStdout( KProcess*, char*, int ) ),
this, SLOT( ifconfigProcessStdout( KProcess*, char*, int ) ) );
connect( mIfconfigProcess, SIGNAL( processExited( KProcess* ) ),
this, SLOT( ifconfigProcessExited( KProcess* ) ) );
if ( !mIfconfigProcess->start( KProcess::NotifyOnExit, KProcess::Stdout ) )
{
delete mIfconfigProcess;
mIfconfigProcess = 0L;
}
}
#ifdef PATH_IWCONFIG
if ( !mIwconfigProcess )
{
mIwconfigStdout = QString::null;
mIwconfigProcess = new KProcess();
mIwconfigProcess->setEnvironment( "LANG", "C" );
mIwconfigProcess->setEnvironment( "LC_ALL", "C" );
*mIwconfigProcess << PATH_IWCONFIG;
connect( mIwconfigProcess, SIGNAL( receivedStdout( KProcess*, char*, int ) ),
this, SLOT( iwconfigProcessStdout( KProcess*, char*, int ) ) );
connect( mIwconfigProcess, SIGNAL( receivedStderr( KProcess*, char*, int ) ),
this, SLOT( iwconfigProcessStdout( KProcess*, char*, int ) ) );
connect( mIwconfigProcess, SIGNAL( processExited( KProcess* ) ),
this, SLOT( iwconfigProcessExited( KProcess* ) ) );
if ( !mIwconfigProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput ) )
{
delete mIwconfigProcess;
mIwconfigProcess = 0L;
}
}
#endif
#endif // Q_OS_FREEBSD
#ifdef PATH_ROUTE
if ( !mRouteProcess )
{
mRouteStdout = QString::null;
mRouteProcess = new KProcess();
mRouteProcess->setEnvironment( "LANG", "C" );
mRouteProcess->setEnvironment( "LC_ALL", "C" );
#ifdef Q_OS_FREEBSD
*mRouteProcess << PATH_ROUTE << "-n" << "get" << "default";
#else
*mRouteProcess << PATH_ROUTE << "-n";
#endif
connect( mRouteProcess, SIGNAL( receivedStdout( KProcess*, char*, int ) ),
this, SLOT( routeProcessStdout( KProcess*, char*, int ) ) );
connect( mRouteProcess, SIGNAL( receivedStderr( KProcess*, char*, int ) ),
this, SLOT( routeProcessStdout( KProcess*, char*, int ) ) );
connect( mRouteProcess, SIGNAL( processExited( KProcess* ) ),
this, SLOT( routeProcessExited( KProcess* ) ) );
if ( !mRouteProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput ) )
{
delete mRouteProcess;
mRouteProcess = 0L;
}
}
#endif
#ifdef Q_OS_FREEBSD
// Check interfaces and update accordingly
Interface* interface = 0;
InterfaceData* data = 0;
struct if_data* ifd;
struct ifaddrs *ifap;
char buf[NI_MAXHOST];
// Reset all devices first
QDictIterator<Interface> ifIt( mInterfaceDict );
for ( ; ifIt.current(); ++ifIt )
{
interface = ifIt.current();
interface->getData().existing = false;
interface->getData().available = false;
interface->getData().addrData.clear();
}
// Get IP address and related information
if ( getifaddrs(&ifap) == 0 ) {
for ( ifaddrs *ifa = ifap; ifa; ifa = ifa->ifa_next ) {
switch ( ifa->ifa_addr->sa_family ) {
case AF_INET6:
case AF_INET:
interface = mInterfaceDict[QString::fromLatin1(ifa->ifa_name)];
if ( interface ) {
data = &interface->getData();
bzero(buf, NI_MAXHOST);
getnameinfo(ifa->ifa_addr, ifa->ifa_addr->sa_len, buf, sizeof(buf), 0, 0, NI_NUMERICHOST);
AddrData *addrData = new AddrData;
data->addrData.insert( QString::fromLatin1(buf), addrData);
if ( ifa->ifa_netmask != NULL ) {
if (ifa->ifa_addr->sa_family == AF_INET6 ) {
bzero(buf, NI_MAXHOST);
getnameinfo(ifa->ifa_netmask, ifa->ifa_netmask->sa_len, buf, sizeof(buf), 0, 0, NI_NUMERICHOST);
addrData->subnetMask = QString::fromLatin1( buf );
} else {
struct sockaddr_in *sin = (struct sockaddr_in *)ifa->ifa_netmask;
addrData->subnetMask = QString::fromLatin1( inet_ntoa(sin->sin_addr) );
}
}
if ( ifa->ifa_broadaddr != NULL ) {
bzero(buf, NI_MAXHOST);
getnameinfo(ifa->ifa_broadaddr, ifa->ifa_broadaddr->sa_len, buf, sizeof(buf), 0, 0, NI_NUMERICHOST);
addrData->broadcastAddress = QString::fromLatin1(buf);
}
if ( ifa->ifa_dstaddr != NULL ) {
bzero(buf, NI_MAXHOST);
getnameinfo(ifa->ifa_dstaddr, ifa->ifa_dstaddr->sa_len, buf, sizeof(buf), 0, 0, NI_NUMERICHOST);
data->ptpAddress = QString::fromLatin1(buf);
}
data->existing = true;
interface->setType((ifa->ifa_flags & IFF_POINTOPOINT ) ? Interface::PPP : Interface::ETHERNET );
// Get media status
int s;
if ((s = socket(ifa->ifa_addr->sa_family, SOCK_DGRAM, 0)) >= 0) {
struct ifmediareq ifmr;
(void) memset(&ifmr, 0, sizeof(ifmr));
(void) strncpy(ifmr.ifm_name, ifa->ifa_name, sizeof(ifmr.ifm_name));
if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
if (ifmr.ifm_status & IFM_AVALID)
/*
* The interface is marked available, if the interface is up
* and if it is has carrier (LAN) or is associated (WLAN)
*/
data->available = (ifa->ifa_flags & IFF_UP) && (ifmr.ifm_status & IFM_ACTIVE);
}
else
data->available = (ifa->ifa_flags & IFF_UP);
close(s);
}
}
break;
case AF_LINK:
interface = mInterfaceDict[QString::fromLatin1(ifa->ifa_name)];
if ( interface ) {
data = &interface->getData();
// get MAC address
struct sockaddr_dl* sdl = (struct sockaddr_dl*)ifa->ifa_addr;
if (sdl->sdl_type == IFT_ETHER && sdl->sdl_alen == ETHER_ADDR_LEN)
data->hwAddress = QString::fromLatin1(ether_ntoa((struct ether_addr*)LLADDR(sdl)));
else
data->hwAddress = "";
// Get traffic statistics
if (ifa->ifa_data != NULL) {
ifd = (if_data *)ifa->ifa_data;
data->rxPackets = ifd->ifi_ipackets;
data->txPackets = ifd->ifi_opackets;
if ( data->prevRxBytes == 0L )
data->prevRxBytes = ifd->ifi_ibytes;
else
data->prevRxBytes = data->rxBytes;
data->rxBytes = ifd->ifi_ibytes;
data->incomingBytes = data->rxBytes - data->prevRxBytes;
data->rxString = KIO::convertSize( data->rxBytes );
if ( data->prevTxBytes == 0L )
data->prevTxBytes = ifd->ifi_obytes;
else
data->prevTxBytes = data->txBytes;
data->txBytes = ifd->ifi_obytes;
data->outgoingBytes = data->txBytes - data->prevTxBytes;
data->txString = KIO::convertSize( data->txBytes );
}
}
break;
}
}
freeifaddrs(ifap);
}
// Update the display
for ( ifIt.toFirst(); ifIt.current(); ++ifIt )
ifIt.current()->activateMonitor();
#endif // Q_OS_FREEBSD
}
void InterfaceUpdater::routeProcessExited( KProcess* process )
{
if ( process == mRouteProcess )
{
mRouteProcess->deleteLater(); // we're in a slot connected to mRouteProcess
mRouteProcess = 0L;
parseRouteOutput();
}
}
void InterfaceUpdater::routeProcessStdout( KProcess*, char* buffer, int buflen )
{
mRouteStdout += QString::fromLatin1( buffer, buflen );
}
void InterfaceUpdater::ifconfigProcessExited( KProcess* process )
{
if ( process == mIfconfigProcess )
{
delete mIfconfigProcess;
mIfconfigProcess = 0L;
parseIfconfigOutput();
}
}
void InterfaceUpdater::ifconfigProcessStdout( KProcess*, char* buffer, int buflen )
{
mIfconfigStdout += QString::fromLatin1( buffer, buflen );
}
void InterfaceUpdater::iwconfigProcessExited( KProcess* process )
{
if ( process == mIwconfigProcess )
{
delete mIwconfigProcess;
mIwconfigProcess = 0L;
parseIwconfigOutput();
}
}
void InterfaceUpdater::iwconfigProcessStdout( KProcess*, char* buffer, int buflen )
{
mIwconfigStdout += QString::fromLatin1( buffer, buflen );
}
void InterfaceUpdater::parseIfconfigOutput()
{
#ifndef Q_OS_FREEBSD
/* mIfconfigStdout contains the complete output of 'ifconfig' which we
* are going to parse here.
*/
QMap<QString, QString> configs;
QStringList ifList = QStringList::split( "\n\n", mIfconfigStdout );
QStringList::Iterator it;
for ( it = ifList.begin(); it != ifList.end(); ++it )
{
int index = ( *it ).find( ' ' );
if ( index == -1 )
continue;
QString key = ( *it ).left( index );
configs[key] = ( *it ).mid( index );
}
/* We loop over the interfaces the user wishs to monitor.
* If we find the interface in the output of 'ifconfig'
* we update its data, otherwise we mark it as
* 'not existing'.
*/
QDictIterator<Interface> ifIt( mInterfaceDict );
for ( ; ifIt.current(); ++ifIt )
{
QString key = ifIt.currentKey();
Interface* interface = ifIt.current();
if ( configs.find( key ) == configs.end() )
{
// The interface does not exist. Meaning the driver
// isn't loaded and/or the interface has not been created.
interface->getData().existing = false;
interface->getData().available = false;
}
// JJ 2005-07-18: use RUNNING instead of UP to detect whether interface is connected
else if ( !configs[key].contains( "inet " ) ||
!configs[key].contains( "RUNNING" ) )
{
// The interface is up or has an IP assigned but not both
interface->getData().existing = true;
interface->getData().available = false;
}
else
{
// ...determine the type of the interface
if ( configs[key].contains( "Ethernet" ) )
interface->setType( Interface::ETHERNET );
else
interface->setType( Interface::PPP );
// Update the interface.
interface->getData().existing = true;
interface->getData().available = true;
updateInterfaceData( configs[key], interface->getData(), interface->getType() );
}
interface->activateMonitor();
}
#endif
}
void InterfaceUpdater::updateInterfaceData( QString& config, InterfaceData& data, int type )
{
#ifndef Q_OS_FREEBSD
QRegExp regExp( ".*RX.*:(\\d+).*:\\d+.*:\\d+.*:\\d+" );
if ( regExp.search( config ) > -1 )
data.rxPackets = regExp.cap( 1 ).toULong();
regExp.setPattern( ".*TX.*:(\\d+).*:\\d+.*:\\d+.*:\\d+" );
if ( regExp.search( config ) > -1 )
data.txPackets = regExp.cap( 1 ).toULong();
regExp.setPattern( "RX bytes:(\\d+)\\s*\\(\\d+\\.\\d+\\s*\\w+\\)" );
if ( regExp.search( config ) > -1 )
{
// We count the traffic on ourself to avoid an overflow after
// 4GB of traffic.
unsigned long currentRxBytes = regExp.cap( 1 ).toULong();
if ( currentRxBytes < data.prevRxBytes )
{
// there was an overflow
data.rxBytes += 0x7FFFFFFF - data.prevRxBytes;
data.prevRxBytes = 0L;
}
if ( data.rxBytes == 0L )
{
// on startup set to currently received bytes
data.rxBytes = currentRxBytes;
// this is new: KNemo only counts the traffic transfered
// while it is running. Important to not falsify statistics!
data.prevRxBytes = currentRxBytes;
}
else
// afterwards only add difference to previous number of bytes
data.rxBytes += currentRxBytes - data.prevRxBytes;
data.incomingBytes = currentRxBytes - data.prevRxBytes;
data.prevRxBytes = currentRxBytes;
data.rxString = KIO::convertSize( data.rxBytes );
}
regExp.setPattern( "TX bytes:(\\d+)\\s*\\(\\d+\\.\\d+\\s*\\w+\\)" );
if ( regExp.search( config ) > -1 )
{
// We count the traffic on ourself to avoid an overflow after
// 4GB of traffic.
unsigned long currentTxBytes = regExp.cap( 1 ).toULong();
if ( currentTxBytes < data.prevTxBytes )
{
// there was an overflow
data.txBytes += 0x7FFFFFFF - data.prevTxBytes;
data.prevTxBytes = 0L;
}
if ( data.txBytes == 0L )
{
// on startup set to currently transmitted bytes
data.txBytes = currentTxBytes;
// this is new: KNemo only counts the traffic transfered
// while it is running. Important to not falsify statistics!
data.prevTxBytes = currentTxBytes;
}
else
// afterwards only add difference to previous number of bytes
data.txBytes += currentTxBytes - data.prevTxBytes;
data.outgoingBytes = currentTxBytes - data.prevTxBytes;
data.prevTxBytes = currentTxBytes;
data.txString = KIO::convertSize( data.txBytes );
}
regExp.setPattern( "inet\\s+\\w+:(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})" );
if ( regExp.search( config ) > -1 )
data.ipAddress = regExp.cap( 1 );
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})" );
if ( regExp.search( config ) > -1 )
{
data.broadcastAddress = regExp.cap( 2 );
data.subnetMask = regExp.cap( 3 );
}
if ( type == Interface::ETHERNET )
{
regExp.setPattern( "(.{2}:.{2}:.{2}:.{2}:.{2}:.{2})" );
if ( regExp.search( config ) > -1 )
data.hwAddress = regExp.cap( 1 );
}
else if ( type == Interface::PPP )
{
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})" );
if ( regExp.search( config ) > -1 )
data.ptpAddress = regExp.cap( 2 );
}
#endif
}
void InterfaceUpdater::parseIwconfigOutput()
{
#ifndef Q_OS_FREEBSD
/* mIwconfigStdout contains the complete output of 'iwconfig' which we
* are going to parse here.
*/
QMap<QString, QString> configs;
QStringList ifList = QStringList::split( "\n\n", mIwconfigStdout );
QStringList::Iterator it;
for ( it = ifList.begin(); it != ifList.end(); ++it )
{
int index = ( *it ).find( ' ' );
if ( index == -1 )
continue;
QString key = ( *it ).left( index );
configs[key] = ( *it ).mid( index );
}
/* We loop over the interfaces the user wishs to monitor.
* If we find the interface in the output of 'iwconfig'
* we update its data.
*/
QDictIterator<Interface> ifIt( mInterfaceDict );
for ( ; ifIt.current(); ++ifIt )
{
QString key = ifIt.currentKey();
Interface* interface = ifIt.current();
if ( configs.find( key ) == configs.end() )
{
// The interface was not found.
continue;
}
else if ( configs[key].contains( "no wireless extensions" ) )
{
// The interface isn't a wireless device.
interface->getData().wirelessDevice = false;
}
else
{
// Update the wireless data of the interface.
interface->getData().wirelessDevice = true;
updateWirelessData( configs[key], interface->getWirelessData() );
}
}
#endif
}
void InterfaceUpdater::updateWirelessData( QString& config, WirelessData& data )
{
#ifndef Q_OS_FREEBSD
QRegExp regExp( "ESSID:\"?([^\"]*)\"?" );
if ( regExp.search( config ) > -1 )
data.essid = regExp.cap( 1 );
regExp.setPattern( "Mode:(\\w*)" );
if ( regExp.search( config ) > -1 )
data.mode = regExp.cap( 1 );
regExp.setPattern( "Frequency:([\\w|\\.]*)" );
if ( regExp.search( config ) > -1 )
data.frequency = regExp.cap( 1 );
else
{
regExp.setPattern( "Channel:(\\d*)" );
if ( regExp.search( config ) > -1 )
data.channel = regExp.cap( 1 );
}
regExp.setPattern( "Bit Rate[=:]([\\w/]*)" );
if ( regExp.search( config ) > -1 )
data.bitRate = regExp.cap( 1 );
regExp.setPattern( "Signal level.(-?\\d+\\s*\\w+)" );
if ( regExp.search( config ) > -1 )
data.signal = regExp.cap( 1 );
regExp.setPattern( "Noise level.(-?\\d+\\s*\\w+)" );
if ( regExp.search( config ) > -1 )
data.noise = regExp.cap( 1 );
regExp.setPattern( "Link Quality[=:]([\\d/]*)" );
if ( regExp.search( config ) > -1 )
data.linkQuality = regExp.cap( 1 );
#endif
}
void InterfaceUpdater::parseRouteOutput()
{
/* mRouteStdout contains the complete output of 'route' which we
* are going to parse here.
*/
QMap<QString, QStringList> configs;
QStringList gateway, routeList = QStringList::split( "\n", mRouteStdout );
QStringList::Iterator it;
for ( it = routeList.begin(); it != routeList.end(); ++it )
{
QStringList routeParameter = QStringList::split( " ", *it );
#ifdef Q_OS_FREEBSD
if ( routeParameter.count() != 2 )
continue;
if ( routeParameter[0] == "gateway:" )
gateway = routeParameter;
if ( routeParameter[0] == "interface:" )
configs[routeParameter[1]] = gateway;
#else
if ( routeParameter.count() < 8 ) // no routing entry
continue;
if ( routeParameter[0] != "0.0.0.0" ) // no default route
continue;
configs[routeParameter[7]] = routeParameter;
#endif
}
/* We loop over the interfaces the user wishs to monitor.
* If we find the interface in the output of 'route' we update
* the data of the interface.
*/
QDictIterator<Interface> ifIt( mInterfaceDict );
for ( ; ifIt.current(); ++ifIt )
{
QString key = ifIt.currentKey();
Interface* interface = ifIt.current();
if ( configs.find( key ) != configs.end() )
{
// Update the default gateway.
QStringList routeParameter = configs[key];
interface->getData().defaultGateway = routeParameter[1];
}
else
{
// Reset the default gateway.
interface->getData().defaultGateway = QString::null;
}
}
}
#include "interfaceupdater.moc"
syntax highlighted by Code2HTML, v. 0.9.1