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.

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í
- že se nám podařilo z NetworkManageru dostat informaci o síti, do které jsme právě připojeni
- ž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.