Posted by & filed under Linux.

 

Dost často se mi stává, že potřebuji vědět, kde jsem se před časem pracovně pohyboval. Když píšu pracovně, téměř ve většině případů to znamená, že jsem tam i otevřel notebook. A když jsem tam otevřel notebook, připojil jsem se na místní wifi síť. Už dlouho tedy přemýšlím o tom, jak jednoduše programově získat jméno sítě, ke které jsem právě připojen, abych tuto informaci posléze přidal k dalším špehovacím informacím a poslal do Google kalendáře. Dobrá zpráva je, že jsem konečně našel chvilku to vyzkoušet. Povedlo se a nakonec to ani nebolelo.

freedesktop-dbus
Protože v současném Debianu se, stejně jako ve spoustě dalších distribucí, pro management síťovách připojení používá NetwokManager, vedly mě první myšlenky právě k němu. NetworkManager komunikuje se zbytkem světa pomocí systému DBus. Proto se problém po chvilce zúžil na nalezení způsobu jak NetworkManager, pomocí systému DBus, o informaci požádat.

DBus

je systém pro inter-process komunikaci (IPC) používaný v Linuxu už řadu let. Komunikační rozhraní pro dbus existují snad ve všech myslitelných programovacích jazycích. Zkusím trochu přiblížit jak to funguje.

Bus

Program, který plánuje komunikovat s ostatními procesy, zveřejní v DBus systému svojí Bus. Busy jsou nazývané podobně jako domény. K oddělení jednotlivých úrovní se používají tečky. Z leva se uvádí, na rozdíl od domén, nejprve nejobecnější úroveň např. org a každá další úroveň název zpřesňuje.

např:

org.freedesktop.NetworkManager
org.freedesktop.ModemManager

Objekts (objekty)

V rámci každé Bus, zveřejní Program jeden a více Objektů. Jejich názvy se vytvářejí podobně jako u Bus, ale jako oddělovač se používá lomítko. Vzniká tak analogie s adresářovou strukturou.

např:

/org/freedesktop/NetworkManager
/org/freedesktop/PolicyKit1/Authority

Proxy

DBus system je vytvořen tak, aby se na něho dalo nahlížet objektově. Tím pádem se přímo nabízí využít pro klientskou část některý z objektových jazyků. Proxy je objekt, který v konkrétní implementaci reprezentuje DBus Objekt. (trochu se to zamotává, že?)

Interfaces

Každý z DBus Objektů poskytuje jeden, případně více Interfaců. Chápat to lze dobře jako datový typ (třídu) a dědičnost. Pokud tedy Objekt poskytuje více Interfaců, znaméná to, že si můžete vybrat ze kterého úhlu pohledu budete na objekt nahlížet. Tomu bude odpovídat i sada metod a vlastností, které budete moci (a bude i dávat smysl), v daném kontextu, použít.

Methods & Properties

Methosds slouží ke komunikaci s dbus objektem. Je možné je vzv. volat, předávat jim parametry a mohou vracet návratové hodnoty. Pomocí Methods můžete číst a nastavovat Properties – vlastnosti Objektu.

Signals

Jde o druhý způsob komunikace s DBus Objektem. Narozdíl od volání Method, se client registruje k odběru Signálu a zareaguje až ve chvíli, kdy Program Signál pošle.

Network Manager

Hledanou informaci, tedy název sítě, ke které je notebook aktuálně připojen má NetworkManager – aplikace, starající se o připojování notebooku do sítí. Nabízí seznam aktuálních wifi sítí, udržuje databázi s již známými sítěmi a automaticky do těchto sítí připojuje. Navíc komunikuje přes DBus, čehož využijeme.

Podívejme se jaké Objekty má NetworkManager v DBus registrovány. Existuje celá řada nástrojů pro práci s DBus systémem. Graficko klikací varianta je např D-Feet (k dispozici je balík pro debian)

sudo apt-get install d-feet

Případně CLI pro QT4 aplikace qdbus

sudo apt-get install qdbus

Na začátku jsem psal, že bych rád informaci z NetworkManageru dostal programově, proto jsem sáhl pro qdbus. D-Feet můžete použít ke kontrole, případně vám pomůže si udělat o celém DBusu obrázek.

Vypišme všechny systémové Busy. Mezi nimi je i Bus, který registroval NetworkManager.

$ qdbus --system
...
org.freedesktop.NetworkManager
...

Připojme se k Busu a vypišme, které objekty poskytuje.

$ qdbus --system org.freedesktop.NetworkManager
...
/org/freedesktop/NetworkManager
...

Zeptejme se Objektu, na jeho metody a vlastnosti (tady trochu matu, ale všiměte si kompletních názvů, vyplynou z nich i názvy jednotlivých interfaců)

$ qdbus --system org.freedesktop.NetworkManager /org/freedesktop/NetworkManager
...
method QString org.freedesktop.DBus.Introspectable.Introspect()
method QDBusVariant org.freedesktop.DBus.Properties.Get(QString interface, QString propname)
property read QList org.freedesktop.NetworkManager.ActiveConnections
...

Zkusme teď metodou Get z Interfacu org.freedesktop.DBus.Properties získat seznam aktivních připojení ActiveConnections z Interfacu org.freedesktop.NetworkManager

$ qdbus --literal --system org.freedesktop.NetworkManager /org/freedesktop/NetworkManager org.freedesktop.DBus.Properties.Get org.freedesktop.NetworkManager ActiveConnections

[Variant: [Argument: ao {[ObjectPath: /org/freedesktop/NetworkManager/ActiveConnection/45]}]]

Bystrý čtenář si jistě všiml, že Objekt /org/freedesktop/NetworkManager/ActiveConnection/45 byl už mezi objekty v dotazu o několik řádků výše. Ano je to tak. Pro prohledání vlastností aktivního připojení použijeme naprosto stejný postup.

Zjistíme tak, že aktivní připojení je k dispozici prostřednictvím interfacu org.freedesktop.NetworkManager.Connection.Active, který poskytuje vlastnost Connection. Hodnotou je pak cesta k Objektu /org/freedesktop/NetworkManager/Settings/27. Zavoláním metody GetSettings Interfacu org.freedesktop.NetworkManager.Settings.Connection se dostaneme ke kýžené informaci.

$ qdbus --literal --system org.freedesktop.NetworkManager /org/freedesktop/NetworkManager/Settings/27 org.freedesktop.NetworkManager.Settings.Connection.GetSettings

[Argument: a{sa{sv}} {"802-11-wireless" = [Argument: a{sv} {"ssid" = [Variant(QByteArray): {65, 76, 90, 65, 70, 82, 69, 69}], "mode" = [Variant(QString): "infrastructure"], "mac-address" = [Variant(QByteArray): {44, -48, 90, -72, 66, -35}]}], "connection" = [Argument: a{sv} {"id" = [Variant(QString): "Auto ALZAFREE"], "uuid" = [Variant(QString): "7363562a-e92b-49d1-9912-12e7b5ab55a4"], "timestamp" = [Variant(qulonglong): 1396886163], "type" = [Variant(QString): "802-11-wireless"]}], "ipv4" = [Argument: a{sv} {"addresses" = [Variant: [Argument: aau {}]], "dns" = [Variant: [Argument: au {}]], "method" = [Variant(QString): "auto"], "routes" = [Variant: [Argument: aau {}]]}], "ipv6" = [Argument: a{sv} {"addresses" = [Variant: [Argument: a(ayuay) {}]], "dns" = [Variant: [Argument: aay {}]], "method" = [Variant(QString): "auto"], "routes" = [Variant: [Argument: a(ayuayu) {}]], "ip6-privacy" = [Variant(int): 2]}]}]

Z výpisu je patrno několik věcí

  1. že se nám podařilo z NetworkManageru dostat informaci o síti, do které jsme právě připojeni
  2. že se nám (mě určitě) moc nelíbí práce s qdbus

Zkusme to celé znovu, tentokrát v Pythonu

import dbus

bus = dbus.SystemBus()

service_name = 'org.freedesktop.NetworkManager'

proxy = bus.get_object(service_name, '/org/freedesktop/NetworkManager')
mgr_props = dbus.Interface(proxy, "org.freedesktop.DBus.Properties")
active = mgr_props.Get("org.freedesktop.NetworkManager", "ActiveConnections")

for a in active:
    a_proxy = bus.get_object(service_name, a)
    a_props = dbus.Interface(a_proxy, "org.freedesktop.DBus.Properties")
    connection_path = a_props.Get("org.freedesktop.NetworkManager.Connection.Active", "Connection")
    c_proxy = bus.get_object(service_name, connection_path)
    connection = dbus.Interface(c_proxy, "org.freedesktop.NetworkManager.Settings.Connection")
    settings = connection.GetSettings()
    print(settings['connection']['id'])

To už vypadá podstatně lépe. Mimochodem jde o téměř doslovný příklad použití modulu dbus z jeho dokumentace. Zdá se, že podobný problém už někdo přede mnou řešil 🙂
Vyzkoušejte a dejte vědět, jestli se vám to povedlo.