Get list of available WiFi connections in Linux - qt

I am try to get list of SSID in Fedora 31 Linux, by D-Bus message, using Qt5.
I am checking many tutorials, but still cant communicate by D-Bus, and I still do not understand differences between interface, path and service. With documentation help (https://developer.gnome.org/NetworkManager/stable/spec.html) and Internet I wrote:
QDBusInterface nm("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager", "org.freedesktop.NetworkManager.Device.Wireless", QDBusConnection::systemBus());
if(nm.isValid()) {
QDBusMessage msg = nm.call("GetAllAccessPoints");
}
But variable "msg" receiving one argument:
"No interface „org.freedesktop.NetworkManager.Device.Wireless” in object at path /org/freedesktop/NetworkManager"
How can I connect to D-Bus ?

Your confusion is justified, as the process is not really intuitive. Basically what you need to do is to first create a QDBusInterface representing NetworkManager itself. Via that object you need to get the list of the network interfaces, iterate through them, filter out the WiFi interface(s), creating a corresponding QDBusInterface, instruct the interface to scan the available networks, and then request the list of visible access points. Then you get the SSID property of each Access Point object. Here is a simple example which demonstrates the process with plain Qt:
list_ssid.pro:
QT -= gui
QT += dbus
SOURCES += list_ssid.cpp
list_ssid.cpp:
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QStringList>
#include <QtDBus/QtDBus>
#include <QDebug>
#include <QThread>
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
// get the interface to nm
QDBusInterface nm("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager",
"org.freedesktop.NetworkManager", QDBusConnection::systemBus());
if(!nm.isValid())
{
qFatal("Failed to connect to the system bus");
}
// get all devices
QDBusMessage msg = nm.call("GetDevices");
qDebug() << "GetDevices reply: " << msg << endl;
QDBusArgument arg = msg.arguments().at(0).value<QDBusArgument>();
if(arg.currentType() != QDBusArgument::ArrayType)
{
qFatal("Something went wrong with getting the device list");
}
QList<QDBusObjectPath> pathsLst = qdbus_cast<QList<QDBusObjectPath> >(arg);
foreach(QDBusObjectPath p, pathsLst)
{
qDebug() << "DEV PATH: " << p.path();
// creating an interface used to gather this devices properties
QDBusInterface device("org.freedesktop.NetworkManager", p.path(),
"org.freedesktop.NetworkManager.Device", QDBusConnection::systemBus());
// 2 is WiFi dev, see https://people.freedesktop.org/~lkundrak/nm-docs/nm-dbus-types.html#NMDeviceType
if (device.property("DeviceType").toInt() != 2)
{
continue;
}
// we got a wifi device, let's get an according dbus interface
QDBusInterface wifi_device("org.freedesktop.NetworkManager", p.path(),
"org.freedesktop.NetworkManager.Device.Wireless", QDBusConnection::systemBus());
// we need to call scan on the inteface prior to request the list of interfaces
QMap<QString, QVariant> argList;
QDBusMessage msg = wifi_device.call("RequestScan", argList);
QThread::sleep(2); // not the best solution, but here we just wait for the scan
// doing the actual call
msg = wifi_device.call("GetAllAccessPoints");
qDebug()<< "Answer for GetAllAccessPoints: " << msg << endl << endl;
// dig out the paths of the Access Point objects:
QDBusArgument ap_list_arg = msg.arguments().at(0).value<QDBusArgument>();
QList<QDBusObjectPath> ap_path_list = qdbus_cast<QList<QDBusObjectPath> >(ap_list_arg);
// and iterate through the list
foreach(QDBusObjectPath p ,ap_path_list)
{
// for each Access Point we create an interface
QDBusInterface ap_interface("org.freedesktop.NetworkManager", p.path(),
"org.freedesktop.NetworkManager.AccessPoint", QDBusConnection::systemBus());
// and getting the name of the SSID
qDebug() << "SSID: " << ap_interface.property("Ssid").toString();
}
}
return 0;
}
The same using networkmanager-qt, for the sake of comparison:
CMakeLists.txt:
project(ssid_list LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt5 REQUIRED COMPONENTS
Core
Gui
Network
DBus
)
find_package(KF5NetworkManagerQt REQUIRED)
add_executable(ssid_list
ssid_list.cpp
)
target_link_libraries(ssid_list Qt5::Core Qt5::DBus Qt5::Network KF5::NetworkManagerQt)
ssid_list.cpp
#include <arpa/inet.h>
#include <QThread>
#include <NetworkManagerQt/Manager>
#include <NetworkManagerQt/Device>
#include <NetworkManagerQt/WirelessDevice>
#include <NetworkManagerQt/AccessPoint>
int main()
{
// getting all of the devices, and iterate through them
NetworkManager::Device::List list = NetworkManager::networkInterfaces();
Q_FOREACH (NetworkManager::Device::Ptr dev, list)
{
if(dev->type() != NM_DEVICE_TYPE_WIFI)
{
//skipping non-wifi interfaces
continue;
}
// creating a Wifi device with this object path
NetworkManager::WirelessDevice wifi_dev(dev->uni());
wifi_dev.requestScan();
QThread::sleep(2); // still not the best solution:w
//get the Object Path of all the visible access points
// and iterate through
foreach(QString ap_path, wifi_dev.accessPoints())
{
// creating an AccessPoint object with this path
NetworkManager::AccessPoint ap(ap_path);
// and finally get the SSID
qDebug() << "SSID:" << ap.ssid();
}
}
}

Related

Make Peer-to-Peer communication on Qt Remote Objects

I want to make simple communication example on Qt Remote Objects. I want to make the communication peer-to-peer, therefore I'm trying to merge both Source and Replica of the same remote object functionality in one application (REPC_MERGED tool used to generate Source and Replica base classes).
#include <QtCore/QCoreApplication>
#include "MyPeerHost.h"
#include "Client.h"
#include <QDebug>
static QString peer_node_name(int number)
{
QString ret = QString("peer_%1").arg(number);
return ret;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyPeerHost peerHost; // just inherits auto-generated MyPeerSimpleSource
QUrl thisAddress = "local:" + peer_node_name(0);
QRemoteObjectHost sourceNode(thisAddress);
if(sourceNode.enableRemoting(&peerHost))
{
qInfo() << "Source remoting enabled successfully" << thisAddress;
QUrl remoteAddress = "local:" + peer_node_name(1);
QSharedPointer<MyPeerReplica> replica;
QRemoteObjectNode replicaNode;
if(replicaNode.connectToNode(remoteAddress))
{
qInfo() << "Replica connected to the address" << remoteAddress << "successfully";
replica.reset(replicaNode.acquire<MyPeerReplica>());
QString sourceClassName = peerHost.staticMetaObject.className();
qDebug() << "Replica wait for Source" << sourceClassName << "...";
if(replica->waitForSource(1000))
{
qInfo() << "Replica object completely initialized";
Client client;
client.setReplicaObject(replica);
client.sendMessage("AAA");
}
else
{
qCritical() << "Replica wait for Source" << sourceClassName << "FAILED" << replicaNode.lastError();
}
}
else
{
qCritical() << "Replica connect to the address" << remoteAddress << "FAILED" << replicaNode.lastError();
}
}
else
{
qCritical() << "Source remoting enable FAILED" << sourceNode.lastError();
}
return a.exec();
}
Application output:
Source remoting enabled successfully QUrl("local:peer_0")
Replica connected to the address QUrl("local:peer_1") successfully
Replica wait for Source "MyPeerHost" ...
Replica wait for Source "MyPeerHost" FAILED QRemoteObjectNode::NoError
As you see, replicaNode successfully connected to the non-existent node QUrl("local:peer_1").
What I am doing wrong?
You don't have valid Qt code.
Qt relies on an event loop to handle asynchronous behavior, which is started by the a.exec() at the end of your main() routine. Qt Remote Objects, in turn, relies on the event loop for all of its communication.
In your code, you create your objects on the stack, but in code blocks that go out of scope before you start the event loop. They will therefore be destructed before the event loop is kicked off.
I'd recommend starting with some of the examples, make sure they work, then grow what you are trying to do from there.

Registring QList<CustomType>

I got a runtime crash when registering a QList (CustomType is a Q_GADGET generate by the repc (Qt remote object compiler)).
Actually I am trying to expose a list of custom type using Qt Remote Object.
file.rep :
POD Service(QString name, QUrl enpoint)
POD Services(QList<Service> svcs)
class ROObject
{
PROP(bool state = false);
PROP(Services services);
}
main.cpp:
#include <QCoreApplication>
#include <QTimer>
#include <QRemoteObjectHost>
#include "rotest.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qRegisterMetaType<QList<Service>>();
ROTest t;
QList<Service> l;
l.push_back(Service("houssem", QUrl("local:houssem")));
l.push_back(Service("houss", QUrl("local:houss")));
Services ss(l);
t.setServices(ss);
QRemoteObjectHost host;
host.setHostUrl(QUrl("local:s"));
host.enableRemoting(&t);
qDebug() << "Services : " << t.services().svcs().count();
return a.exec();
}
error:
Thank you for your help.
Qt doesn't know how to serialize your custom types.
In addition to registering it as a meta type, you need to implement and register stream operators for it:
void qRegisterMetaTypeStreamOperators(const char *typeName)
Registers the stream operators for the type T called typeName.
Afterward, the type can be streamed using QMetaType::load() and
QMetaType::save(). These functions are used when streaming a QVariant.
qRegisterMetaTypeStreamOperators<MyClass>("MyClass");
// The stream operators should have the following signatures:
QDataStream &operator<<(QDataStream &out, const MyClass &myObj);
QDataStream &operator>>(QDataStream &in, MyClass &myObj);
Also, what you actually need to register as a meta type is Service and not QList<Service>. Once service is registered and its streaming operators are implemented you should be all set, Qt knows how to handle the QList part.

Adding a custom sqlite function to a Qt application

I am trying to add a custom sqlite3 regexp function into my Qt application (as recommended by this answer). But as soon as I call the sqlite3_create_function function, I get the message The program has unexpectedly finished. When I debug, it terminates in a segmentation fault in sqlite3_mutex_enter. There is a MWE below, with apologies for the absolute file paths.
The regexp implementation in my code is from this site; it also fails with the msign function here. The various checks of driver()->handle() are straight from the Qt docs.
Incidentally, I used select sqlite_version(); to determine that Qt 5.5 uses sqlite version 3.8.8.2. I found that version by looking through old commits in the Qt GitHub repository.
MWE.pro
QT += core gui
TARGET = MWE
TEMPLATE = app
QT += sql
SOURCES += main.cpp \
D:\Qt\Qt5.5.0\5.5\Src\3rdparty\sqlite\sqlite3.c
HEADERS += D:\Qt\Qt5.5.0\5.5\Src\3rdparty\sqlite\sqlite3.h
main.cpp
#include <QtSql>
#include "D:/Qt/Qt5.5.0/5.5/Src/3rdparty/sqlite/sqlite3.h"
void qtregexp(sqlite3_context* ctx, int argc, sqlite3_value** argv)
{
QRegExp regex;
QString str1((const char*)sqlite3_value_text(argv[0]));
QString str2((const char*)sqlite3_value_text(argv[1]));
regex.setPattern(str1);
regex.setCaseSensitivity(Qt::CaseInsensitive);
bool b = str2.contains(regex);
if (b)
{
sqlite3_result_int(ctx, 1);
}
else
{
sqlite3_result_int(ctx, 0);
}
}
int main(int argc, char *argv[])
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("my.db");
db.open();
QVariant v = db.driver()->handle();
if (v.isValid() && qstrcmp(v.typeName(), "sqlite3*")==0) {
sqlite3 *db_handle = *static_cast<sqlite3 **>(v.data());
if (db_handle != 0) { // check that it is not NULL
// This shows that the database handle is generally valid:
qDebug() << sqlite3_db_filename(db_handle, "main");
sqlite3_create_function(db_handle, "regexp", 2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, &qtregexp, NULL, NULL);
qDebug() << "This won't be reached."
QSqlQuery query;
query.prepare("select regexp('p$','tap');");
query.exec();
query.next();
qDebug() << query.value(0).toString();
}
}
db.close();
}
You need to call sqlite3_initialize() after you get the database handle from Qt, according to this forum post.
...
sqlite3 *db_handle = *static_cast<sqlite3 **>(v.data());
if (db_handle != 0) { // check that it is not NULL
sqlite3_initialize();
...
This solves the issue.

QNetworkManager uploading file to FTP crash

I am trying to upload a simple test text file to a FTP server. In order to achieve this I am using QNetworkAccessManager, since QFtp has been deprecated in Qt 5.1.
I created a test.txt file in the programs directory and using QFile I am opening it as QIODevice::ReadWrite | QIODevice::Text.
The problem is when I set the connection and tell the QNetworkAccessManager to upload a file the program crashes ("FTPConnectionTest does not respond"). It happens both when I am trying to use an external FTP server or a local one created with FileZilla.
I connected all signals emitted by the reply (functions: uploadFinish, uploadProgress, uploadError) however no feedback is beeing captured.
Question: Is this problem lying on the side of FTP server or am I doing something wrong in my code?
Code snipped below:
Main.cpp
#include <QCoreApplication>
#include <ftp.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Ftp ftp;
return a.exec();
}
ftp.cpp
#include "ftp.h"
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <QFile>
#include <QUrl>
#include <QDebug>
Ftp::Ftp()
{
QFile file("test.txt");
if (file.open(QIODevice::ReadWrite | QIODevice::Text)) {
url = QUrl("ftp://127.0.0.1/test.txt");
url.setUserName("user");
url.setPassword("password");
qDebug() << "URL set" << url;
QNetworkAccessManager* nam = new QNetworkAccessManager();
qDebug() << "nam set";
QNetworkReply *rep = nam->put(QNetworkRequest(url), &file);
qDebug() << "after rep";
connect(rep, SIGNAL(finished()), this, SLOT(uploadFinish()));
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(uploadError(QNetworkReply::NetworkError)));
connect(rep, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(uploadProgress(qint64,qint64)));
}
else qDebug() << "failed to open";
}
void Ftp::uploadFinish()
{
qDebug() << "finished uploading file";
}
void Ftp::uploadProgress(qint64 a, qint64 b)
{
qDebug() << a << "/" << b;
}
void Ftp::uploadError(QNetworkReply::NetworkError state)
{
qDebug() << "State" << state;
}
See the QNetworkAccessManager::put documentation:
data must be opened for reading when this function is called and must remain valid until the finished() signal is emitted for this reply.
Your file object falls out of scope when the constructor finishes execution, so QNetworkAccessManager probably tries to read from object that is already deleted. You need to make file a class member variable or create it using QFile* file = new QFile().

Simple QT console TCP application. What am I doing wrong?

#include <QtCore/QCoreApplication>
#include <QTCore>
#include <QtNetwork>
#include <QDebug>
#define CONNECT(sndr, sig, rcvr, slt) connect(sndr, SIGNAL(sig), rcvr, SLOT(slt))
class mynet : QObject
{
Q_OBJECT
public:
mynet()
{}
void start()
{
CONNECT(tcpServer, newConnection(), this, acceptConnection());
CONNECT(tcpClient, connected(), this, startTransfer());
CONNECT(tcpClient, bytesWritten(qint64), this, updateClientProgress(qint64));
CONNECT(tcpClient, error(QAbstractSocket::SocketError), this, displayError(QAbstractSocket::SocketError));
// start server listening
tcpServer->listen();
while(!tcpServer->isListening());
// make client connection
tcpClient->connectToHost(QHostAddress::LocalHost, tcpServer->serverPort());
}
public slots:
void acceptConnection()
{
tcpServerConnection = tcpServer->nextPendingConnection();
CONNECT(tcpServerConnection, readyRead(), this, updateServerProgress());
CONNECT(tcpServerConnection, error(QAbstractSocket::SocketError), this, displayError(QAbstractSocket));
tcpServer->close();
}
void startTransfer()
{
bytesToWrite = TotalBytes - (int)tcpClient->write(QByteArray(PayloadSize, '#'));
}
void updateServerProgress()
{
bytesReceived += (int)tcpServerConnection->bytesAvailable();
tcpServerConnection->readAll();
if (bytesReceived == TotalBytes)
{
qDebug() << "done";
tcpServerConnection->close();
}
}
void updateClientProgress(qint64 numBytes)
{
// callen when the TCP client has written some bytes
bytesWritten += (int)numBytes;
// only write more if not finished and when the Qt write buffer is below a certain size.
if (bytesToWrite > 0 && tcpClient->bytesToWrite() <= 4*PayloadSize)
bytesToWrite -= (int)tcpClient->write(QByteArray(qMin(bytesToWrite, PayloadSize), '#'));
}
void displayError(QAbstractSocket::SocketError socketError)
{
if (socketError == QTcpSocket::RemoteHostClosedError)
return;
qDebug() << tcpClient->errorString();
tcpClient->close();
tcpServer->close();
}
private:
QTcpServer* tcpServer;
QTcpSocket* tcpClient;
QTcpSocket* tcpServerConnection;
int bytesToWrite;
int bytesWritten;
int bytesReceived;
int TotalBytes;
int PayloadSize;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
mynet m1;
m1.start();
return a.exec();
}
I get an
Undefined symbols for architecture x86_64:
"vtable for mynet", referenced from:
mynet::mynet() in main.o
mynet::~mynet()in main.o.
Please advise what I am doing wrong. Can I not inline the method definitions in the class for some reason in Qt?
You need to add your class to the .pro file
HEADERS += mynet.h
SOURCES += mynet.cpp
so the meta-object compiler can scan them and work out they need moc'ing and generate the relevant stubs.
Assuming that your source file is named foo.cpp, you have to put the following line at the very end:
#include "foo.moc"
This line tells qmake and the VS Qt add-in that the file should be run via moc, and that the generated moc file should be named foo.moc.
You also have problems in the #include lines for Qt headers. I've found that the following work:
#include <QtCore/QCoreApplication>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
Make sure to add network to your .pro file. This will create the correct linking to the network library functions.
QT += core network
Two things:
1) You should publicly derive from QObject.
2) Are you moc'ing this file and then compiling and linking the output? If you include the Q_OBJECT macro and don't moc, you will get an error like that.

Resources