#include "backend.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../core/logcat.hpp" #include "../../dbus/properties.hpp" #include "../qml.hpp" #include "dbus_nm_backend.h" #include "dbus_nm_device.h" #include "dbus_types.hpp" #include "device.hpp" #include "enums.hpp" #include "wired.hpp" #include "wireless.hpp" namespace qs::network { namespace { QS_LOGGING_CATEGORY(logNetworkManager, "quickshell.network.networkmanager", QtWarningMsg); } NetworkManager::NetworkManager(QObject* parent): NetworkBackend(parent) { qCDebug(logNetworkManager) << "Connecting to NetworkManager"; qDBusRegisterMetaType(); auto bus = QDBusConnection::systemBus(); if (!bus.isConnected()) { qCWarning( logNetworkManager ) << "Could not connect to DBus. NetworkManager backend will not work."; return; } // clang-format off QObject::connect(&this->serviceWatcher, &QDBusServiceWatcher::serviceRegistered, this, &NetworkManager::onServiceRegistered); QObject::connect(&this->serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &NetworkManager::onServiceUnregistered); // clang-format on this->serviceWatcher.setWatchMode( QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration ); this->serviceWatcher.addWatchedService("org.freedesktop.NetworkManager"); this->serviceWatcher.setConnection(bus); this->proxy = new DBusNetworkManagerProxy( "org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager", bus, this ); // clang-format off QObject::connect(this->proxy, &DBusNetworkManagerProxy::DeviceAdded, this, &NetworkManager::onDevicePathAdded); QObject::connect(this->proxy, &DBusNetworkManagerProxy::DeviceRemoved, this, &NetworkManager::onDevicePathRemoved); // clang-format on this->dbusProperties.setInterface(this->proxy); if (!this->proxy->isValid()) { qCDebug( logNetworkManager ) << "NetworkManager is not currently running. This network backend will not work"; } else { this->onServiceRegistered(); } } void NetworkManager::checkConnectivity() { auto pending = this->proxy->CheckConnectivity(); auto* call = new QDBusPendingCallWatcher(pending, this); auto responseCallback = [](QDBusPendingCallWatcher* call) { const QDBusPendingReply reply = *call; if (reply.isError()) { qCInfo(logNetworkManager) << "Failed to check connectivity: " << reply.error().message(); } delete call; }; QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); } void NetworkManager::registerDevices() { auto pending = this->proxy->GetAllDevices(); auto* call = new QDBusPendingCallWatcher(pending, this); auto responseCallback = [this](QDBusPendingCallWatcher* call) { const QDBusPendingReply> reply = *call; if (reply.isError()) { qCWarning(logNetworkManager) << "Failed to get devices: " << reply.error().message(); } else { for (const QDBusObjectPath& devicePath: reply.value()) { this->registerDevice(devicePath.path()); } } delete call; }; QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); } void NetworkManager::registerDevice(const QString& path) { if (this->mDevices.contains(path)) { qCDebug(logNetworkManager) << "Skipping duplicate registration of device" << path; return; } auto* temp = new DBusNMDeviceProxy( "org.freedesktop.NetworkManager", path, QDBusConnection::systemBus(), this ); auto callback = [this, path, temp](uint value, const QDBusError& error) { if (error.isValid()) { qCWarning(logNetworkManager) << "Failed to get device type:" << error; } else { auto type = static_cast(value); NMDevice* dev = nullptr; this->mDevices.insert(path, nullptr); switch (type) { case NMDeviceType::Wifi: dev = new NMWirelessDevice(path); break; case NMDeviceType::Ethernet: dev = new NMWiredDevice(path); break; default: break; } if (dev) { qCDebug(logNetworkManager) << "Device added:" << path; if (!dev->isValid()) { qCWarning(logNetworkManager) << "Ignoring invalid registration of" << path; delete dev; } else { this->mDevices[path] = dev; // clang-format off QObject::connect(dev, &NMDevice::addAndActivateConnection, this, &NetworkManager::addAndActivateConnection); QObject::connect(dev, &NMDevice::activateConnection, this, &NetworkManager::activateConnection); // clang-format on QObject::connect(dev, &NMDevice::loaded, this, [this, dev]() { emit this->deviceAdded(dev->frontend()); }); } } else { qCDebug(logNetworkManager) << "Ignoring registration of unsupported device:" << path; } temp->deleteLater(); } }; qs::dbus::asyncReadProperty(*temp, "DeviceType", callback); } void NetworkManager::onDevicePathAdded(const QDBusObjectPath& path) { this->registerDevice(path.path()); } void NetworkManager::onDevicePathRemoved(const QDBusObjectPath& path) { auto iter = this->mDevices.find(path.path()); if (iter == this->mDevices.end()) { qCWarning(logNetworkManager) << "Sent removal signal for" << path.path() << "which is not registered."; } else { auto* dev = iter.value(); this->mDevices.erase(iter); if (dev) { qCDebug(logNetworkManager) << "Device removed:" << path.path(); emit this->deviceRemoved(dev->frontend()); delete dev; } } } void NetworkManager::activateConnection( const QDBusObjectPath& connPath, const QDBusObjectPath& devPath ) { auto pending = this->proxy->ActivateConnection(connPath, devPath, QDBusObjectPath("/")); auto* call = new QDBusPendingCallWatcher(pending, this); auto responseCallback = [](QDBusPendingCallWatcher* call) { const QDBusPendingReply reply = *call; if (reply.isError()) { qCWarning(logNetworkManager) << "Failed to activate connection:" << reply.error().message(); } delete call; }; QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); } void NetworkManager::addAndActivateConnection( const NMSettingsMap& settings, const QDBusObjectPath& devPath, const QDBusObjectPath& specificObjectPath ) { auto pending = this->proxy->AddAndActivateConnection(settings, devPath, specificObjectPath); auto* call = new QDBusPendingCallWatcher(pending, this); auto responseCallback = [](QDBusPendingCallWatcher* call) { const QDBusPendingReply reply = *call; if (reply.isError()) { qCWarning(logNetworkManager) << "Failed to add and activate connection:" << reply.error().message(); } delete call; }; QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); } void NetworkManager::setConnectivityCheckEnabled(bool enabled) { if (enabled == this->bConnectivityCheckEnabled) return; this->bConnectivityCheckEnabled = enabled; this->pConnectivityCheckEnabled.write(); } void NetworkManager::setWifiEnabled(bool enabled) { if (enabled == this->bWifiEnabled) return; this->bWifiEnabled = enabled; this->pWifiEnabled.write(); } void NetworkManager::onServiceRegistered() { qCDebug(logNetworkManager) << "NetworkManager service registered"; this->dbusProperties.updateAllViaGetAll(); this->registerDevices(); } void NetworkManager::onServiceUnregistered() { qCDebug(logNetworkManager) << "NetworkManager service unregistered"; // Clear all remaining devices const auto devices = this->mDevices; this->mDevices.clear(); for (auto* device: devices) { if (!device) continue; qCDebug(logNetworkManager) << "Device removed:" << device->path(); emit this->deviceRemoved(device->frontend()); delete device; } } bool NetworkManager::isAvailable() const { return this->proxy && this->proxy->isValid(); }; } // namespace qs::network namespace qs::dbus { DBusResult DBusDataTransform::fromWire(quint32 wire) { return DBusResult(static_cast(wire)); } } // namespace qs::dbus