From 5aa568daae23daa7ac190e56df469e9566075257 Mon Sep 17 00:00:00 2001 From: Niklas Date: Mon, 10 Oct 2011 17:08:35 +0200 Subject: new veriosn of the fbgui src (same as in fbgui-ng) combined with my networkDiscovery in front. --- src/networkdiscovery.cpp | 781 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 781 insertions(+) create mode 100644 src/networkdiscovery.cpp (limited to 'src/networkdiscovery.cpp') diff --git a/src/networkdiscovery.cpp b/src/networkdiscovery.cpp new file mode 100644 index 0000000..da2cb1d --- /dev/null +++ b/src/networkdiscovery.cpp @@ -0,0 +1,781 @@ +#include "networkdiscovery.h" + +#include "../common/fbgui.h" + + +/** + * constructor + */ +NetworkDiscovery::NetworkDiscovery(QObject *parent) { + _tag = "[nd:NetworkDiscovery]"; + _server = new QLocalServer(this); +} + + + +/** + * destructor + */ +NetworkDiscovery::~NetworkDiscovery() { + +} + + + +/** + * initialize all important class members and start the main work. + * + * @param serverIp + * the ip of the server with which we are testing the connectivity. + * + * @param userChoice + * true if the user wishes to have a user choice. true: the chooseInterfaceDialog will be showed. + * + * @param autoUp + * true if we want to "auto Up" all down interfaces. + * + * @param pathToLogFile + * the path to the log file. + * + * @param serverPath + * the path to the server socket (default value: DEFAULT_QTSOCKETADDRESS "/var/tmp/qt_c_socket_default") + * + * @param pathToExe + * the path to the customdhcpcd exe. (default value: #define DEFAULT_QTSOCKETADDRESS "/var/tmp/qt_c_socket_default") + * + * @param args + * additional arguments for the customdhcpcd client. (default value: NULL) + */ +void NetworkDiscovery::initAndRun(QString serverIp, bool userChoice, bool autoUp, QString pathToLogFile, QString serverPath, QString pathToExe, + QStringList* args) { + + _serverIp = serverIp; + _userChoice = userChoice; + _autoUp = autoUp; + _pathToLogFile = pathToLogFile; + + _pathToDhcpcdExe = pathToExe; + _blocked = false; + _numberOfProcesses = 0; + _ifUpCountdown = 10; + + 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)) { + // emit signal to the gui that a critial error occoured + qDebug() << _tag << "Unable to start server: " + << _server->errorString(); + emit + abortBoot("Unable to start server: " + _server->errorString()); + return; + } + + // check if the path to the customdhcpcd file is correct + QFileInfo fInfo(_pathToDhcpcdExe); + if (!fInfo.exists()) { + qDebug() << _tag + << "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; + } + + connect(_server, SIGNAL(newConnection()), this, SLOT(handleNewConnection())); + connect(this, SIGNAL(readyForRun()), this, SLOT(slotReadyForRun())); + + if (args != NULL && !args->isEmpty()) { + qDebug() << _tag << "added additional args"; + _dhcpcdArguments.append(*args); + } + + // start the main work: + if (_autoUp) { + getListOfNetworkInterfacesWithAutoUp(); + _timer = new QTimer(this); + connect(_timer, SIGNAL(timeout()), this, SLOT(checkForIsRunning())); + _timer->start(1000); + + } else { + getListOfNetworkInterfaces(); + emit readyForRun(); + } +} + + + +/** + * emits the addInterface signal for each interface name in _ifUpList + * and calls the runDHCPCD method. + * if the _ifUpList is empty, this method emits an abortBoot signal. + * connected to the readyForRun signal. + */ +void NetworkDiscovery::slotReadyForRun() { + if (_ifUpList.size() > 0) { + foreach(QString i, _ifUpList) { + emit addInterface(i); + } + _numberOfProcesses = _ifUpList.size(); + runDHCPCD( _ifUpList); + } else { + qDebug() << _tag << "list is empty. Have not found usable interface."; + emit + abortBoot("Have not found usable interface"); + return; + } +} + + + +/** + * only called if autoUp == true. + * check the IsRunning flag of each interface in the _ifDownList. + * connected to the timeout signal of the timer. + */ +void NetworkDiscovery::checkForIsRunning() { + bool isRunning = false; + QList copyOfIfDownList(_ifDownList); + foreach(QString i, _ifDownList) { + QNetworkInterface networkInterface = QNetworkInterface::interfaceFromName(i); + isRunning = (networkInterface.flags() & QNetworkInterface::IsRunning); + if (isRunning) { + _ifUpList.append(i); + _ifDownList.removeAt(_ifDownList.indexOf(i)); + } + } + _ifUpCountdown--; + if ((_ifUpCountdown <= 0 ) || _ifDownList.isEmpty()) { + // shut down timer + _timer->stop(); + emit readyForRun(); + } +} + + + +/** + * replace the default route. sets af automatically to AF_INET + * + * @param ifName + * interface name + * + * @param gateway + * gateway address + * + * @param mss + * mss value (i think this is the metric. in most cases this value is 0) + */ +int NetworkDiscovery::ip4_replaceDefaultRoute(QString ifName, QString gateway, int mss) { + return _networkManager.replaceDefaultRoute(ifName, gateway, mss, AF_INET); +} + + + +/** + * replace the dhcp configuration with the manual config, entered by the user. + * if we can not establish a connection with the entered values, reset to the old + * dhcp values. + * + * @param result + * a json object formated string. + * + * @return + * 0 if everything ok + */ +int NetworkDiscovery::ip4_setManualConfiguration(QVariantMap result) { + QList 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() << _tag << "set man conf. and check connectivity"; + + if (!checkConnectivityViaTcp(_serverIp)) { + qDebug() << _tag << "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; +} + + + +/** + * returns the gateway address, written into the dhcp config file. + * + * @param ifName + * name of the interface. + * + * @return + * gateway address as string. + */ +QString NetworkDiscovery::getGatewayForInterface(QString ifName) { + interfaceconfiguration * ifConf = _ifcMap.value(ifName); + return ifConf->getGateway(); +} + + + +/** + * reads the log file. + * + * @return the log file as one string. + */ +QString NetworkDiscovery::readLogFile() { + // path to log file is in _pathToLogFile. initialized in initAndRun(). + QString retval("the log file"); + QFile logFile(_pathToLogFile); + if (logFile.exists()) { + if (logFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + while (!logFile.atEnd()) { + retval.append(logFile.readLine()); + } + return retval; + } + } + +} + + + +/** + * ================================================================================ + ********************************* Private Methods ******************************** + * ================================================================================ + **/ + + + +/** + * searches for usable interfaces and puts them into a list. + * if the interface is down, put it in the _ifDownList, try to bring it up. + * else put it in the _ifUpList. + * usable interfaces are: can Broadcast, no loopback, no point to point, name is not in the BlackList, + */ +void NetworkDiscovery::getListOfNetworkInterfacesWithAutoUp() { + QList nIList = QNetworkInterface::allInterfaces(); + + 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::IsRunning)) { + _ifUpList.append(nI.humanReadableName()); + } + else if (!(nI.flags() & QNetworkInterface::IsUp)) { + _networkManager.bringInterfaceUP(nI.humanReadableName()); + qDebug() << _tag << " bring up .."; + _ifDownList.append(nI.humanReadableName()); + } + } + } else { + qDebug() << _tag << "no interfaces found!"; + } +} + + + +/** + * searches for usable interfaces which are up and running and put them into a list. + * usable interfaces are: can Broadcast, no loopback, no point to point, name is not in the BlackList, + */ +void NetworkDiscovery::getListOfNetworkInterfaces() { + QList nIList = QNetworkInterface::allInterfaces(); + + if (nIList.size() > 0) { + foreach(QNetworkInterface nI, nIList) + { + if (((!(nI.flags() & QNetworkInterface::CanBroadcast) + || nI.flags() & QNetworkInterface::IsLoopBack) + || nI.flags() & QNetworkInterface::IsPointToPoint) + || !(nI.flags() & QNetworkInterface::IsUp) + || !(nI.flags() & QNetworkInterface::IsRunning) + || checkBlackList(nI.humanReadableName())) { + continue; + } + _ifUpList.append(nI.humanReadableName()); + } + } else { + qDebug() << _tag << "no interfaces found!"; + } +} + + + +/** + * not used so far. checks the carrier state using the sysfs library. + * if carrier = 1 ==> the interface is running. + * interfaces have to be up in order to get right results. + * + * @param interface + * name of the interface + */ +bool NetworkDiscovery::checkCarrierState(QString interface) { + + qDebug() << _tag << "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() << _tag << "conversion error"; + } + } + } + } else { + qDebug() << _tag << "attrlist is Null"; + } + sysfs_close_class_device(class_device); + + return true; +} + + + +/** + * call for every interface in the list the runDHCPCD method. + * + * @param interfaces + * list of interface names. + */ +void NetworkDiscovery::runDHCPCD(QList &interfaces) { + foreach(QString nI, interfaces) + { + runDHCPCD(nI); + } +} + + + +/** + * start a cdhcpcd process with the interface name as last argument. + * + * @param interface + * name of an interface. + */ +void NetworkDiscovery::runDHCPCD(QString interface) { + emit updateStatusLabel(interface, "start DHCP"); + _dhcpcdArguments.append(interface); + QProcess * p = new QProcess(this); + + qDebug() << _tag << _dhcpcdArguments; + + _clientProcessToIfNameMap.insert(p, interface); + 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(); +} + + + +/** + * checks the connectivity. tries to open a TCP connection to the + * server (see _serverIp). For this it adjusts the routing table. + * (sets the gateway of the interface as default gateway) + * Gateway is written into the dhcpcd config file of this interface. + * (see DEFAULT_INTERFACE_CONF_LOCATION "/var/tmp/conf_") + * + * @param ifName + * name of a interface. + * + * @return + * true: connection is possible + * false: connection not possible + */ +bool NetworkDiscovery::checkConnectivity(QString ifName) { + int mss = 0; + + // get gateway address + QString pathToGatewayFile(DEFAULT_INTERFACE_CONF_LOCATION); + pathToGatewayFile += ifName; + interfaceconfiguration *ifConf = new interfaceconfiguration(); + ifConf->readConfigOutOfFile(pathToGatewayFile); + _ifcMap.insert(ifName, ifConf); + + // replace default route + qDebug() << _tag << "replace default route"; + _networkManager.replaceDefaultRoute(ifName, + ifConf->getGateway(), mss, AF_INET); + + if (checkConnectivityViaTcp(_serverIp)) { + qDebug() << _tag << "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); + } + return true; + } else { + qDebug() << _tag << "no internet connection with interface" << ifName; + emit + updateStatusLabel(ifName, "connection not possible"); + return false; + } +} + + + +/** + * try to open a tcp connection to the server + * + * @param server + * a ip address. + * + * @return + * true: connection is possible + * false: 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() << _tag << tcpSocket->errorString(); + return false; + } else { + return true; + } +} + + + +/** + * connected to the new client arrived signal. + * connects the client readyRead signal with the handleNewInput slot. + */ +void NetworkDiscovery::handleNewConnection() { + qDebug() << _tag << "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())); +} + + + +/** + * called when a client disconnects. + */ +void NetworkDiscovery::handleClientDisconnect() { + QLocalSocket* socket = qobject_cast (QObject::sender()); + + QLocalSocket * client = _clients.value(socket); + + qDebug() << _tag << "disconnect client"; + handleNewInput(client); + client->deleteLater(); +} + + + +/** + * same function as handleNewInput() but with a client as parameter. + * + * @param cleint + * a client + */ +void NetworkDiscovery::handleNewInput(QLocalSocket * client) { + qDebug() << _tag << "last read before exit"; + while (client->canReadLine()) { + QString data(client->readLine()); + + data = data.trimmed(); + qDebug() << _tag << 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 (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() << _tag << 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 ;;; ) + */ +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() << _tag << 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() << _tag << "received error:" << msg; + break; + default: + //qDebug() << _tag << 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 (QObject::sender()); + QString ifName = _clientProcessToIfNameMap.value(p, "ifName"); + if (!_blocked) { //_blocked becomes true, if _userChoice is false and we already found a usable interface + if (ifName.compare("ifName") == 0) { + qDebug() << _tag << "--- \t [NetworkDiscovery::handleProcessFinished] haven't found process!"; + } else { + qDebug() << _tag << "process for interface" << ifName << "finished" + << exitCode << exitStatus; + if (exitCode > 0) { + qDebug() << _tag << "process exited unexpected"; + emit updateStatusLabel(ifName, "process exited unexpected"); + } else { + qDebug() << _tag << "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() << _tag << "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: ". + */ +void NetworkDiscovery::handleProcessStarted() { + QProcess* p = qobject_cast (QObject::sender()); + QString ifName = _clientProcessToIfNameMap.value(p, "ifName"); + qDebug() << _tag << "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; + } +} -- cgit v1.2.3-55-g7522