0

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 ?

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
InnerWorld
  • 585
  • 7
  • 26
  • Not really an answer, but have you consider using networkmanager-qt: https://api.kde.org/frameworks/networkmanager-qt/html/index.html In my experience it's less finicky than directly using the dbus interface – zgyarmati Apr 16 '20 at 21:19

1 Answers1

3

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();

        }
    }
}
InnerWorld
  • 585
  • 7
  • 26
zgyarmati
  • 1,135
  • 8
  • 15
  • Your answer give me many information, and I learnt from it a lot. I found one differences. RequestScan need to have input parameter to work, instead refresh networks doesn't work. I have made edit in your answer. I rewroted your code to work with classes and now try to learn how to connect to signals. – InnerWorld Apr 19 '20 at 20:06