#include "networkdiscovery.h"
#include "../common/fbgui.h"
NetworkDiscovery::NetworkDiscovery(QObject *parent) {
_server = new QLocalServer(this);
}
NetworkDiscovery::~NetworkDiscovery() {
}
void NetworkDiscovery::initAndRun(QString serverIp, bool userChoice,QString serverPath, QString pathToExe,
QStringList* args) {
_serverIp = serverIp;
_userChoice = userChoice;
_blocked = false;
_pathToDhcpcdExe = pathToExe;
_numberOfProcesses = 0;
if (serverPath != DEFAULT_QTSOCKETADDRESS) {
_dhcpcdArguments.append("-q");
_dhcpcdArguments.append(serverPath);
}
/* delete the file at serverPath. this is necessary since in case the application crashes, the file still
* exists which leads to an error.
*/
if(QFile::exists(serverPath)) {
QFile::remove(serverPath);
}
if (!_server->listen(serverPath)) {
/*
QMessageBox::critical(this, tr("NetworkDiscovery"), tr(
"Unable to start the server: %1.") .arg(server->errorString()));
close();
*/
// emit signal to the gui that a critial error occoured
QString errorInfo("Unable to start server: ");
qDebug() << "--- \t [NetworkDiscovery::initAndRun] " + errorInfo
<< _server->errorString();
emit
abortBoot(errorInfo + _server->errorString());
return;
}
connect(_server, SIGNAL(newConnection()), this, SLOT(handleNewConnection()));
// check if the path to the customdhcpcd file is correct
QFileInfo fInfo(_pathToDhcpcdExe);
if (!fInfo.exists()) {
qDebug()
<< "could not find customdhcpcd exe. Please check the path to this file.";
emit abortBoot(
"could not find customdhcpcd exe. Please check the path to this file.");
return;
}
if (args != NULL && !args->isEmpty()) {
qDebug() << "--- \t [NetworkDiscovery::initAndRun] added additional args";
_dhcpcdArguments.append(*args);
}
// start the main work:
QList<QString> list = getListOfNetworkInterfaces();
if (list.size() > 0) {
//list = checkCarrierState(list);
//dhcpcdArguments.append("-d");
_numberOfProcesses = list.size();
runDHCPCD(list);
} else {
qDebug() << "list is empty. Have not found usable interface.";
emit abortBoot("Haven not found usable interface");
return;
}
}
int NetworkDiscovery::replaceDefaultRoute(QString &ifName, QString &gateway, int af, int mss) {
_networkManager.replaceDefaultRoute(ifName, gateway, mss, AF_INET);
}
int NetworkDiscovery::ip4_replaceDefaultRoute(QString ifName, QString gateway, int mss) {
_networkManager.replaceDefaultRoute(ifName, gateway, mss, AF_INET);
}
int NetworkDiscovery::ip4_setManualConfiguration(QVariantMap result) {
QList<QString> dns;
dns.append(result["dns"].toString());
_networkManager.ip4_setManualConfiguration(result["ifname"].toString(),
result["ipaddr"].toString(),
result["netmask"].toString(),
result["broadcast"].toString(),
result["gateway"].toString(),
0,
AF_INET,
"/etc/",
dns);
qDebug() << "set man conf. test connectivity";
if (!checkConnectivityViaTcp(_serverIp)) {
qDebug() << "no connectivity. reset conf.";
interfaceconfiguration * ifc = _ifcMap.value(result["ifname"].toString(), NULL);
if(ifc != NULL) {
dns.clear();
dns = ifc->getDnsservers().trimmed().split(" ");
_networkManager.ip4_setManualConfiguration(result["ifname"].toString(),
ifc->getIpAddress(),
ifc->getNetmask(),
ifc->getBroadcast(),
ifc->getGateway(),
0,
AF_INET,
"/etc/",
dns);
}
return 0;
}
emit continueBoot(result["ifname"].toString(), 0);
return 0;
}
QString NetworkDiscovery::getGatewayForInterface(QString ifName) {
interfaceconfiguration * ifConf = _ifcMap.value(ifName);
return ifConf->getGateway();
}
QList<QString> NetworkDiscovery::getListOfNetworkInterfaces() {
QList<QNetworkInterface> nIList = QNetworkInterface::allInterfaces();
QList<QString> result;
if (nIList.size() > 0) {
foreach(QNetworkInterface nI, nIList)
{
if (((!(nI.flags() & QNetworkInterface::CanBroadcast)
|| nI.flags() & QNetworkInterface::IsLoopBack)
|| nI.flags() & QNetworkInterface::IsPointToPoint)
|| checkBlackList(nI.humanReadableName())) {
continue;
}
if(!(nI.flags() & QNetworkInterface::IsUp)) {
_networkManager.bringInterfaceUP(nI.humanReadableName());
}
/*
* if(!checkForIsRunning(nI)) {
continue;
}
*/
if (!checkCarrierState(nI.humanReadableName())) {
continue;
}
result.append(nI.humanReadableName());
emit addInterface(nI.humanReadableName());
}
} else {
qDebug() << "no interfaces found!";
}
return result;
}
bool NetworkDiscovery::checkForIsRunning(QNetworkInterface networkInterface) {
bool retval = false;
for (int i = 0; i < 4; i++) {
retval = (networkInterface.flags() & QNetworkInterface::IsRunning);
if(retval) {
return retval;
}
sleep(1);
}
return retval;
}
QList<QString> NetworkDiscovery::checkCarrierState(QList<QString> &interfaces) {
QList<QString> result;
foreach(QString nI, interfaces)
{
if (checkCarrierState(nI)) {
// everything is fine, cable is plugged,
// go on with the next interface
//continue;
result.append(nI);
}
}
return result;
}
bool NetworkDiscovery::checkCarrierState(QString interface) {
qDebug() << "check carrier state for interface " << interface;
QByteArray ba = interface.toAscii();
const char * iface = ba.data();
struct sysfs_class_device *class_device = sysfs_open_class_device("net",
iface);
struct dlist *attrlist = sysfs_get_classdev_attributes(class_device);
if (attrlist != NULL) {
struct sysfs_attribute *attr = NULL;
dlist_for_each_data(attrlist, attr, struct sysfs_attribute) {
if (strcmp("carrier", attr->name) == 0) {
QString value(attr->value);
bool ok = false;
bool * pok = &ok;
int v = value.toInt(pok);
if (*pok) {
if (v == 1) {
qDebug()
<< "carrier is 1. Cable is plugged. return true";
return true;
} else {
qDebug()
<< "carrier is 0. Cable is unplugged. return false";
return false;
}
} else {
qDebug() << "conversion error";
}
}
}
} else {
qDebug() << "attrlist is Null";
}
sysfs_close_class_device(class_device);
return true;
}
void NetworkDiscovery::runDHCPCD(QList<QString> &interfaces) {
foreach(QString nI, interfaces)
{
runDHCPCD(nI);
}
}
void NetworkDiscovery::runDHCPCD(QString interface) {
emit updateStatusLabel(interface, "start DHCP");
_dhcpcdArguments.append(interface);
QProcess * p = new QProcess(this);
qDebug() << _dhcpcdArguments;
_clientProcessToIfNameMap.insert(p, interface);
qDebug() << _clientProcessToIfNameMap;
p->start(_pathToDhcpcdExe, _dhcpcdArguments);
connect(p, SIGNAL(started()), this, SLOT(handleProcessStarted()));
connect(p, SIGNAL(finished(int, QProcess::ExitStatus)), this,
SLOT(handleProcessFinished(int, QProcess::ExitStatus)));
_dhcpcdArguments.removeLast();
}
bool NetworkDiscovery::checkConnectivity(QString ifName) {
int metric = 0;
// get gateway address
QString pathToGatewayFile(DEFAULT_GATEWAY_INFO_LOCATION);
pathToGatewayFile += ifName;
interfaceconfiguration *ifConf = new interfaceconfiguration();
ifConf->readConfigOutOfFile(pathToGatewayFile);
_ifcMap.insert(ifName, ifConf);
// replace default route
qDebug() << _networkManager.replaceDefaultRoute(ifName,
ifConf->getGateway(), 0, AF_INET);
if (checkConnectivityViaTcp(_serverIp)) {
qDebug() << "internet: check passed! for interface" << ifName;
emit
updateStatusLabel(ifName, "connection possible");
if (!_userChoice) {
// blockiere jeden weiteren check
// emite continueBoot
_blocked = true;
emit continueBoot(ifName, 0);
} else {
emit connectionEstablished(ifName);
}
} else {
qDebug() << "no internet connection with interface" << ifName;
emit
updateStatusLabel(ifName, "connection not possible");
}
}
bool NetworkDiscovery::checkConnectivityViaTcp(QString server) {
// check connectivity via tcp connection
QTcpSocket *tcpSocket = new QTcpSocket(this);
tcpSocket->connectToHost(server, 80);
if (!tcpSocket->waitForConnected(500)) {
qDebug() << tcpSocket->errorString();
return false;
} else {
return true;
}
}
/**
*
*/
void NetworkDiscovery::handleNewConnection() {
qDebug() << "New Connection arrived";
/*QLocalSocket **/
_client = _server ->nextPendingConnection();
_clients.insert(_client, _client);
connect(_client, SIGNAL(disconnected()), this,
SLOT(handleClientDisconnect()));
connect(_client, SIGNAL(readyRead()), this, SLOT(handleNewInput()));
}
/**
*
*/
void NetworkDiscovery::handleClientDisconnect() {
QLocalSocket* socket = qobject_cast<QLocalSocket *> (QObject::sender());
QLocalSocket * client = _clients.value(socket);
qDebug() << "disconnect client";
handleNewInput(client);
client->deleteLater();
}
/**
*
*/
void NetworkDiscovery::handleNewInput(QLocalSocket * client) {
qDebug() << "last read before exit";
while (client->canReadLine()) {
QString data(client->readLine());
data = data.trimmed();
qDebug() << data;
QStringList lines = data.split("\n");
for (int i = 0; i < lines.length(); i++) {
handleNewInputLine(client, lines.at(i));
}
}
}
/**
*
* This method is connected to the readyRead Signal of the QLocalSocket
* client.
* send an ACK to the client with every received message.
*/
void NetworkDiscovery::handleNewInput() {
QLocalSocket* socket = qobject_cast<QLocalSocket *> (QObject::sender());
QLocalSocket * client = _clients.value(socket);
QString data(client->read(DHCP_MESSAGE_SIZE));
client->write("ACK", ACK_SIZE);
client->waitForBytesWritten();
data = data.trimmed();
//qDebug() << data;
QStringList lines = data.split("\n");
for (int i = 0; i < lines.length(); i++) {
handleNewInputLine(client, lines.at(i));
}
}
/**
* This Method processes the send messages.
*
* This Method processes the send messages. It splits the line
* into several components. Those components are:
* interface: interface name ==> indicates the process who send the message
* s_state: is the number representation of syslog.h LOG levels
* s_subState: is the number representation of the dhcp.c DHCP states (1 - 8) plus
* the status. h states (9 - ..)
* msg: is a message which can contain additional informations
*
* According to the s_state and s_subState we emit the changeProgressBarValue() signal
* with different values.
*
* @param client
* the client who send the message
*
* @param data
* the message. (format <interfaceName>;<state>;<subState>;<msg> )
*/
void NetworkDiscovery::handleNewInputLine(QLocalSocket * client, QString data) {
QString logMsg(data);
QString interface = logMsg.section(";", 0, 0);
QString s_state = logMsg.section(";", 1, 1);
QString s_subState = logMsg.section(";", 2, 2);
QString msg = logMsg.section(";", 3, 3);
int st = s_state.trimmed().toInt();
int sst = s_subState.trimmed().toInt();
//qDebug() << logMsg;
if (_ifNameToClient.size() < _numberOfProcesses && !_ifNameToClient.contains(
interface)) {
_ifNameToClient.insert(interface, client);
}
switch (st) {
case LOG_INFO:
switch (sst) {
case DHCP_DISCOVER:
emit changeProgressBarValue(interface, 10);
break;
case DHCP_OFFER:
emit changeProgressBarValue(interface, 20);
break;
case DHCP_REQUEST:
emit changeProgressBarValue(interface, 30);
break;
case DHCP_ACK:
emit changeProgressBarValue(interface, 40);
break;
case DHCP_NAK:
emit changeProgressBarValue(interface, 40);
break;
case DHCPCD_ARP_TEST:
emit changeProgressBarValue(interface, 50);
break;
case DHCP_DECLINE:
emit changeProgressBarValue(interface, 60);
break;
case DHCP_RELEASE:
break;
case DHCP_INFORM:
break;
case DHCPCD_CONFIGURE:
emit changeProgressBarValue(interface, 70);
break;
case DHCPCD_WRITE:
emit changeProgressBarValue(interface, 80);
break;
case DHCPCD_EXIT:
emit changeProgressBarValue(interface, 100);
break;
case DHCPCD_LOG:
default:
break;
}
break;
case LOG_ERR:
qDebug() << "received error:" << msg;
break;
default:
//qDebug() << logMsg;
break;
}
}
/**
* This Method is called when a process is finished.
*
* This Method is called when a process is finished. This slot is connected
* with the signal finished() of the QProcess class.
* If the process finishes, it will be checked if the process exited normal
* or if an unexpected error occurred. For this, we determine the sender (which is a
* QProcess), get the corresponding interface (which is stored in a map), and check
* the exitCode. Further actions are taken according to the exitCode check.
* Normal exit:
* emit changeProgressBar() to 100%
* emit updateStatusLabel() to check connection
* checkConnectivity() @see NetworkDiscovery::checkConnectivity()
* Unexpected exit:
* emit updateStatusLabel() to process exited unexpected
* TODO:: the reason for the unexpected exit should be presented in the logfile.
*
* @param exitCode
*
* @param exitStatus
*
* @return bool
* returns true: if the interface name i starts with a letter in the blacklist.
*
* returns false: else
*
* @see NetworkDiscovery::getListOfNetworkInterfaces()
*/
/*
void NetworkDiscovery::handleProcessFinished(int exitCode,
QProcess::ExitStatus exitStatus) {
QProcess* p = qobject_cast<QProcess *> (QObject::sender());
QString ifName = clientProcessToIfNameMap.value(p, "ifName");
if (ifName.compare("ifName") == 0) {
qDebug()
<< "--- \t [NetworkDiscovery::handleProcessFinished] haven't found process!";
} else {
qDebug() << "process for interface" << ifName << "finished" << exitCode
<< exitStatus;
if (exitCode > 0) {
qDebug() << "process exited unexpected";
emit updateStatusLabel(ifName, "process exited unexpected");
} else {
qDebug() << "process normal exit";
qDebug() << "check internet connection";
emit
changeProgressBarValue(ifName, 100);
emit
updateStatusLabel(ifName, "check connectivity");
checkConnectivity(ifName);
}
}
QLocalSocket *client = ifNameToClient.value(ifName, 0);
if (client != 0) {
handleNewInput(client);
}
numberOfProcesses = numberOfProcesses - 1;
if (numberOfProcesses <= 0) {
emit allProcessesFinished();
}
}
*/
void NetworkDiscovery::handleProcessFinished(int exitCode,
QProcess::ExitStatus exitStatus) {
QProcess* p = qobject_cast<QProcess *> (QObject::sender());
QString ifName = _clientProcessToIfNameMap.value(p, "ifName");
if (!_blocked) { //_blocked becomes true, if _userChoice is false and we found a usable interface
if (ifName.compare("ifName") == 0) {
qDebug()
<< "--- \t [NetworkDiscovery::handleProcessFinished] haven't found process!";
} else {
qDebug() << "process for interface" << ifName << "finished"
<< exitCode << exitStatus;
if (exitCode > 0) {
qDebug() << "process exited unexpected";
emit updateStatusLabel(ifName, "process exited unexpected");
} else {
qDebug() << "process normal exit";
emit changeProgressBarValue(ifName, 100);
emit updateStatusLabel(ifName, "check connectivity");
checkConnectivity(ifName);
}
}
if (!_blocked) { //_blocked becomes true, if _userChoice is false and we found a usable interface
QLocalSocket *client = _ifNameToClient.value(ifName, 0);
if (client != 0) {
handleNewInput(client);
}
_numberOfProcesses = _numberOfProcesses - 1;
if (_numberOfProcesses <= 0 && _userChoice) {
emit allProcessesFinished();
}
}
} else {
qDebug() << "already blocked";
emit updateStatusLabel(ifName, "finished DHCP");
}
}
/**
* This Method is called when a process is started.
*
* This Method is called when a process is started.
* It prints the message: "process started for interface: <interfaceName>".
*/
void NetworkDiscovery::handleProcessStarted() {
QProcess* p = qobject_cast<QProcess *> (QObject::sender());
QString ifName = _clientProcessToIfNameMap.value(p, "ifName");
qDebug() << "process started for interface:" << ifName;
}
/**
* This Method implements a blacklist.
*
* This Method implements a blacklist. We check the fist character
* of the interface name. if this letter is in the list, we return true.
* True means, that this interface won't be put into the result list of
* getListOfNetworkInterfaces().
*
* @param i
* is a interface name.
*
* @return bool
* returns true: if the interface name i starts with a letter in the blacklist.
*
* returns false: else
*
* @see NetworkDiscovery::getListOfNetworkInterfaces()
*/
bool NetworkDiscovery::checkBlackList(QString i) {
if (i.startsWith("v", Qt::CaseInsensitive)) {
return true;
} else if (i.startsWith("d", Qt::CaseInsensitive)) {
return true;
} else {
return false;
}
}