Phase 1: implement iwd D-Bus core

iwd_dbus watches net.connman.iwd name ownership, calls
GetManagedObjects, and dispatches InterfacesAdded/Removed to a
callback consumer. iwd_manager owns hashes of Iwd_Device and
Iwd_Network keyed by object path; sub-objects subscribe to their
PropertiesChanged signals via Eldbus and ping the manager so
listeners can refresh. Aggregated state (off/idle/scanning/
connecting/connected) is recomputed from the active station.

iwd_device exposes Powered toggle plus Station Scan/Disconnect.
iwd_network calls Network.Connect() (the iwd Agent will be wired
in next) and Forget via the referenced KnownNetwork object.

Builds against EFL 1.28 / Enlightenment 0.27.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
nemunaire 2026-04-08 22:41:14 +07:00
commit 73d17ff21c
11 changed files with 699 additions and 54 deletions

View file

@ -1,26 +1,159 @@
#include "iwd_dbus.h"
#include <Eina.h>
#include <stdlib.h>
#include <string.h>
#define FDO_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager"
struct _Iwd_Dbus
{
Eldbus_Connection *conn;
Eldbus_Object *root;
Eldbus_Proxy *object_manager;
Eldbus_Signal_Handler *name_owner_sh;
Eldbus_Connection *conn;
Iwd_Dbus_Callbacks cbs;
void *data;
Eldbus_Object *root_obj;
Eldbus_Proxy *root_om;
Eldbus_Signal_Handler *sh_added;
Eldbus_Signal_Handler *sh_removed;
Eina_Bool present;
};
/* TODO:
* - watch IWD_BUS_NAME owner (NameOwnerChanged) for restart handling
* - call org.freedesktop.DBus.ObjectManager.GetManagedObjects on "/"
* - listen for InterfacesAdded / InterfacesRemoved
* - dispatch new objects to iwd_manager
*/
Eldbus_Connection *
iwd_dbus_conn(const Iwd_Dbus *d) { return d ? d->conn : NULL; }
/* Walk the a{oa{sa{sv}}} reply from GetManagedObjects, emitting iface_added
* for every (path, interface) pair. */
static void
_emit_managed(Iwd_Dbus *d, Eldbus_Message_Iter *objects)
{
Eldbus_Message_Iter *entry;
while (eldbus_message_iter_get_and_next(objects, 'e', &entry))
{
const char *path;
Eldbus_Message_Iter *ifaces;
if (!eldbus_message_iter_arguments_get(entry, "oa{sa{sv}}", &path, &ifaces))
continue;
Eldbus_Message_Iter *iface_entry;
while (eldbus_message_iter_get_and_next(ifaces, 'e', &iface_entry))
{
const char *iface;
Eldbus_Message_Iter *props;
if (!eldbus_message_iter_arguments_get(iface_entry, "sa{sv}", &iface, &props))
continue;
if (d->cbs.iface_added)
d->cbs.iface_added(d->data, path, iface, props);
}
}
}
static void
_on_get_managed(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED)
{
Iwd_Dbus *d = data;
const char *errname, *errmsg;
if (eldbus_message_error_get(msg, &errname, &errmsg))
{
/* iwd not present yet — name watcher will retry. */
return;
}
Eldbus_Message_Iter *objects;
if (!eldbus_message_arguments_get(msg, "a{oa{sa{sv}}}", &objects))
return;
_emit_managed(d, objects);
}
static void
_on_iface_added(void *data, const Eldbus_Message *msg)
{
Iwd_Dbus *d = data;
const char *path;
Eldbus_Message_Iter *ifaces, *iface_entry;
if (!eldbus_message_arguments_get(msg, "oa{sa{sv}}", &path, &ifaces))
return;
while (eldbus_message_iter_get_and_next(ifaces, 'e', &iface_entry))
{
const char *iface;
Eldbus_Message_Iter *props;
if (!eldbus_message_iter_arguments_get(iface_entry, "sa{sv}", &iface, &props))
continue;
if (d->cbs.iface_added)
d->cbs.iface_added(d->data, path, iface, props);
}
}
static void
_on_iface_removed(void *data, const Eldbus_Message *msg)
{
Iwd_Dbus *d = data;
const char *path;
Eldbus_Message_Iter *ifaces;
if (!eldbus_message_arguments_get(msg, "oas", &path, &ifaces))
return;
const char *iface;
while (eldbus_message_iter_get_and_next(ifaces, 's', &iface))
{
if (d->cbs.iface_removed)
d->cbs.iface_removed(d->data, path, iface);
}
}
static void
_bind_root(Iwd_Dbus *d)
{
if (d->root_om) return;
d->root_obj = eldbus_object_get(d->conn, IWD_BUS_NAME, "/");
if (!d->root_obj) return;
d->root_om = eldbus_proxy_get(d->root_obj, FDO_OBJECT_MANAGER);
if (!d->root_om) return;
d->sh_added = eldbus_proxy_signal_handler_add(d->root_om, "InterfacesAdded",
_on_iface_added, d);
d->sh_removed = eldbus_proxy_signal_handler_add(d->root_om, "InterfacesRemoved",
_on_iface_removed, d);
eldbus_proxy_call(d->root_om, "GetManagedObjects", _on_get_managed, d, -1, "");
}
static void
_unbind_root(Iwd_Dbus *d)
{
if (d->sh_added) { eldbus_signal_handler_del(d->sh_added); d->sh_added = NULL; }
if (d->sh_removed) { eldbus_signal_handler_del(d->sh_removed); d->sh_removed = NULL; }
if (d->root_om) { eldbus_proxy_unref(d->root_om); d->root_om = NULL; }
if (d->root_obj) { eldbus_object_unref(d->root_obj); d->root_obj = NULL; }
}
static void
_on_name_owner(void *data, const char *bus EINA_UNUSED, const char *old_id, const char *new_id)
{
Iwd_Dbus *d = data;
Eina_Bool now = (new_id && new_id[0]);
Eina_Bool was = (old_id && old_id[0]);
if (now && !d->present)
{
d->present = EINA_TRUE;
_bind_root(d);
if (d->cbs.name_appeared) d->cbs.name_appeared(d->data);
}
else if (!now && (was || d->present))
{
d->present = EINA_FALSE;
_unbind_root(d);
if (d->cbs.name_vanished) d->cbs.name_vanished(d->data);
}
}
Iwd_Dbus *
iwd_dbus_new(Eldbus_Connection *conn)
iwd_dbus_new(Eldbus_Connection *conn, const Iwd_Dbus_Callbacks *cbs, void *data)
{
Iwd_Dbus *d = calloc(1, sizeof(*d));
if (!d) return NULL;
d->conn = conn;
if (cbs) d->cbs = *cbs;
d->data = data;
eldbus_name_owner_changed_callback_add(conn, IWD_BUS_NAME,
_on_name_owner, d, EINA_TRUE);
return d;
}
@ -28,5 +161,7 @@ void
iwd_dbus_free(Iwd_Dbus *d)
{
if (!d) return;
eldbus_name_owner_changed_callback_del(d->conn, IWD_BUS_NAME, _on_name_owner, d);
_unbind_root(d);
free(d);
}

View file

@ -2,6 +2,7 @@
#define IWD_DBUS_H
#include <Eldbus.h>
#include <Eina.h>
#define IWD_BUS_NAME "net.connman.iwd"
#define IWD_IFACE_ADAPTER "net.connman.iwd.Adapter"
@ -14,7 +15,21 @@
typedef struct _Iwd_Dbus Iwd_Dbus;
Iwd_Dbus *iwd_dbus_new (Eldbus_Connection *conn);
/* Callbacks the consumer (iwd_manager) registers to react to bus state. */
typedef struct _Iwd_Dbus_Callbacks
{
void (*name_appeared) (void *data);
void (*name_vanished) (void *data);
/* iface_props is an iter positioned on a{sv} for the given interface. */
void (*iface_added) (void *data, const char *path, const char *iface,
Eldbus_Message_Iter *iface_props);
void (*iface_removed) (void *data, const char *path, const char *iface);
} Iwd_Dbus_Callbacks;
Iwd_Dbus *iwd_dbus_new (Eldbus_Connection *conn,
const Iwd_Dbus_Callbacks *cbs, void *data);
void iwd_dbus_free(Iwd_Dbus *d);
Eldbus_Connection *iwd_dbus_conn(const Iwd_Dbus *d);
#endif

View file

@ -1,26 +1,158 @@
#include "iwd_device.h"
#include "iwd_dbus.h"
#include "iwd_props.h"
#include "iwd_manager.h"
#include <stdlib.h>
#include <string.h>
static Iwd_Station_State
_state_from_str(const char *s)
{
if (!s) return IWD_STATION_DISCONNECTED;
if (!strcmp(s, "connected")) return IWD_STATION_CONNECTED;
if (!strcmp(s, "connecting")) return IWD_STATION_CONNECTING;
if (!strcmp(s, "disconnecting")) return IWD_STATION_DISCONNECTING;
if (!strcmp(s, "roaming")) return IWD_STATION_ROAMING;
return IWD_STATION_DISCONNECTED;
}
static void
_dev_prop_cb(void *data, const char *key, Eldbus_Message_Iter *v)
{
Iwd_Device *d = data;
if (!strcmp(key, "Name")) { free(d->name); d->name = iwd_props_str_dup(v); }
else if (!strcmp(key, "Address")) { free(d->address); d->address = iwd_props_str_dup(v); }
else if (!strcmp(key, "Adapter")) { free(d->adapter_path); d->adapter_path = iwd_props_str_dup(v); }
else if (!strcmp(key, "Powered")) { d->powered = iwd_props_bool(v); }
}
static void
_sta_prop_cb(void *data, const char *key, Eldbus_Message_Iter *v)
{
Iwd_Device *d = data;
if (!strcmp(key, "State"))
{
char *s = iwd_props_str_dup(v);
d->station_state = _state_from_str(s);
free(s);
}
else if (!strcmp(key, "Scanning")) { d->scanning = iwd_props_bool(v); }
else if (!strcmp(key, "ConnectedNetwork")) { free(d->connected_network); d->connected_network = iwd_props_str_dup(v); }
}
void
iwd_device_apply_device_props(Iwd_Device *d, Eldbus_Message_Iter *props)
{
iwd_props_for_each(props, _dev_prop_cb, d);
}
void
iwd_device_apply_station_props(Iwd_Device *d, Eldbus_Message_Iter *props)
{
d->has_station = EINA_TRUE;
iwd_props_for_each(props, _sta_prop_cb, d);
}
/* org.freedesktop.DBus.Properties.PropertiesChanged: (s, a{sv}, as) */
static void
_on_dev_props_changed(void *data, const Eldbus_Message *msg)
{
Iwd_Device *d = data;
const char *iface;
Eldbus_Message_Iter *changed, *invalidated;
if (!eldbus_message_arguments_get(msg, "sa{sv}as", &iface, &changed, &invalidated))
return;
if (strcmp(iface, IWD_IFACE_DEVICE) != 0) return;
iwd_props_for_each(changed, _dev_prop_cb, d);
if (d->manager) iwd_manager_notify(d->manager);
}
static void
_on_sta_props_changed(void *data, const Eldbus_Message *msg)
{
Iwd_Device *d = data;
const char *iface;
Eldbus_Message_Iter *changed, *invalidated;
if (!eldbus_message_arguments_get(msg, "sa{sv}as", &iface, &changed, &invalidated))
return;
if (strcmp(iface, IWD_IFACE_STATION) != 0) return;
iwd_props_for_each(changed, _sta_prop_cb, d);
if (d->manager) iwd_manager_notify(d->manager);
}
Iwd_Device *
iwd_device_new(Eldbus_Connection *conn EINA_UNUSED, const char *path)
iwd_device_new(Eldbus_Connection *conn, const char *path, void *manager)
{
Iwd_Device *d = calloc(1, sizeof(*d));
if (path) d->path = strdup(path);
/* TODO: build proxies for Device + Station; subscribe to PropertiesChanged */
if (!d) return NULL;
d->path = path ? strdup(path) : NULL;
d->manager = manager;
d->obj = eldbus_object_get(conn, IWD_BUS_NAME, path);
if (d->obj)
{
d->device_proxy = eldbus_proxy_get(d->obj, IWD_IFACE_DEVICE);
if (d->device_proxy)
d->sh_dev_props = eldbus_proxy_properties_changed_callback_add(
d->device_proxy, _on_dev_props_changed, d);
}
return d;
}
void
iwd_device_attach_station(Iwd_Device *d)
{
if (!d || d->station_proxy || !d->obj) return;
d->station_proxy = eldbus_proxy_get(d->obj, IWD_IFACE_STATION);
if (d->station_proxy)
{
d->has_station = EINA_TRUE;
d->sh_sta_props = eldbus_proxy_properties_changed_callback_add(
d->station_proxy, _on_sta_props_changed, d);
}
}
void
iwd_device_detach_station(Iwd_Device *d)
{
if (!d) return;
if (d->sh_sta_props) { eldbus_signal_handler_del(d->sh_sta_props); d->sh_sta_props = NULL; }
if (d->station_proxy) { eldbus_proxy_unref(d->station_proxy); d->station_proxy = NULL; }
d->has_station = EINA_FALSE;
}
void
iwd_device_free(Iwd_Device *d)
{
if (!d) return;
iwd_device_detach_station(d);
if (d->sh_dev_props) eldbus_signal_handler_del(d->sh_dev_props);
if (d->device_proxy) eldbus_proxy_unref(d->device_proxy);
if (d->obj) eldbus_object_unref(d->obj);
free(d->path);
free(d->name);
free(d->address);
free(d->adapter_path);
free(d->connected_network);
free(d);
}
void
iwd_device_set_powered(Iwd_Device *d EINA_UNUSED, Eina_Bool on EINA_UNUSED)
iwd_device_set_powered(Iwd_Device *d, Eina_Bool on)
{
/* TODO: Set("Powered") on Device interface */
if (!d || !d->device_proxy) return;
eldbus_proxy_property_set(d->device_proxy, "Powered", "b", &on, NULL, NULL);
}
void
iwd_device_scan(Iwd_Device *d)
{
if (!d || !d->station_proxy) return;
eldbus_proxy_call(d->station_proxy, "Scan", NULL, NULL, -1, "");
}
void
iwd_device_disconnect(Iwd_Device *d)
{
if (!d || !d->station_proxy) return;
eldbus_proxy_call(d->station_proxy, "Disconnect", NULL, NULL, -1, "");
}

View file

@ -6,17 +6,47 @@
typedef struct _Iwd_Device Iwd_Device;
typedef enum {
IWD_STATION_DISCONNECTED,
IWD_STATION_CONNECTING,
IWD_STATION_CONNECTED,
IWD_STATION_DISCONNECTING,
IWD_STATION_ROAMING,
} Iwd_Station_State;
struct _Iwd_Device
{
char *path; /* D-Bus object path */
char *name;
char *address;
Eina_Bool powered;
Eldbus_Proxy *station; /* may be NULL */
char *path;
char *name;
char *address;
char *adapter_path;
Eina_Bool powered;
/* Station-side state, valid when station_proxy != NULL */
Eina_Bool has_station;
Iwd_Station_State station_state;
Eina_Bool scanning;
char *connected_network;
Eldbus_Object *obj;
Eldbus_Proxy *device_proxy;
Eldbus_Proxy *station_proxy;
Eldbus_Signal_Handler *sh_dev_props;
Eldbus_Signal_Handler *sh_sta_props;
void *manager; /* back-ref, opaque (Iwd_Manager *) */
};
Iwd_Device *iwd_device_new (Eldbus_Connection *conn, const char *path);
Iwd_Device *iwd_device_new (Eldbus_Connection *conn, const char *path, void *manager);
void iwd_device_free(Iwd_Device *d);
void iwd_device_set_powered(Iwd_Device *d, Eina_Bool on);
void iwd_device_apply_device_props (Iwd_Device *d, Eldbus_Message_Iter *props);
void iwd_device_apply_station_props(Iwd_Device *d, Eldbus_Message_Iter *props);
void iwd_device_attach_station (Iwd_Device *d);
void iwd_device_detach_station (Iwd_Device *d);
void iwd_device_set_powered(Iwd_Device *d, Eina_Bool on);
void iwd_device_scan (Iwd_Device *d);
void iwd_device_disconnect (Iwd_Device *d);
#endif

View file

@ -1,26 +1,189 @@
#include "iwd_manager.h"
#include "iwd_dbus.h"
#include "iwd_device.h"
#include "iwd_network.h"
#include <stdlib.h>
#include <string.h>
typedef struct _Listener
{
Iwd_Manager_Cb cb;
void *data;
} Listener;
struct _Iwd_Manager
{
Iwd_Dbus *dbus;
Eina_List *devices; /* Iwd_Device * */
Eina_List *listeners;
Iwd_State state;
Iwd_Dbus *dbus;
Eina_Hash *devices; /* path → Iwd_Device * */
Eina_Hash *networks; /* path → Iwd_Network * */
Eina_List *listeners; /* Listener * */
Iwd_State state;
Eina_Bool notify_pending;
};
/* TODO:
* - Build the device/station/network tree from iwd_dbus events
* - Aggregate state from active station
* - Notify listeners on any change
*/
static void _recompute_state(Iwd_Manager *m);
/* ----- listeners ------------------------------------------------------- */
void
iwd_manager_listener_add(Iwd_Manager *m, Iwd_Manager_Cb cb, void *data)
{
if (!m || !cb) return;
Listener *l = calloc(1, sizeof(*l));
l->cb = cb; l->data = data;
m->listeners = eina_list_append(m->listeners, l);
}
void
iwd_manager_listener_del(Iwd_Manager *m, Iwd_Manager_Cb cb, void *data)
{
if (!m) return;
Eina_List *l;
Listener *li;
EINA_LIST_FOREACH(m->listeners, l, li)
if (li->cb == cb && li->data == data)
{
m->listeners = eina_list_remove_list(m->listeners, l);
free(li);
return;
}
}
void
iwd_manager_notify(Iwd_Manager *m)
{
if (!m) return;
_recompute_state(m);
Eina_List *l;
Listener *li;
EINA_LIST_FOREACH(m->listeners, l, li)
li->cb(li->data, m);
}
/* ----- state aggregation ---------------------------------------------- */
static void
_recompute_state(Iwd_Manager *m)
{
Iwd_State s = IWD_STATE_OFF;
Eina_Iterator *it = eina_hash_iterator_data_new(m->devices);
Iwd_Device *d;
Eina_Bool any_powered = EINA_FALSE;
EINA_ITERATOR_FOREACH(it, d)
{
if (d->powered) any_powered = EINA_TRUE;
if (!d->has_station) continue;
if (d->scanning && s < IWD_STATE_SCANNING) s = IWD_STATE_SCANNING;
if (d->station_state == IWD_STATION_CONNECTING && s < IWD_STATE_CONNECTING) s = IWD_STATE_CONNECTING;
if (d->station_state == IWD_STATION_CONNECTED && s < IWD_STATE_CONNECTED) s = IWD_STATE_CONNECTED;
}
eina_iterator_free(it);
if (s == IWD_STATE_OFF && any_powered) s = IWD_STATE_IDLE;
m->state = s;
}
/* ----- D-Bus callbacks ------------------------------------------------- */
static void
_on_iface_added(void *data, const char *path, const char *iface, Eldbus_Message_Iter *props)
{
Iwd_Manager *m = data;
Eldbus_Connection *conn = iwd_dbus_conn(m->dbus);
if (!strcmp(iface, IWD_IFACE_DEVICE))
{
Iwd_Device *d = eina_hash_find(m->devices, path);
if (!d)
{
d = iwd_device_new(conn, path, m);
if (d) eina_hash_add(m->devices, path, d);
}
if (d) iwd_device_apply_device_props(d, props);
}
else if (!strcmp(iface, IWD_IFACE_STATION))
{
Iwd_Device *d = eina_hash_find(m->devices, path);
if (!d)
{
d = iwd_device_new(conn, path, m);
if (d) eina_hash_add(m->devices, path, d);
}
if (d)
{
iwd_device_attach_station(d);
iwd_device_apply_station_props(d, props);
}
}
else if (!strcmp(iface, IWD_IFACE_NETWORK))
{
Iwd_Network *n = eina_hash_find(m->networks, path);
if (!n)
{
n = iwd_network_new(conn, path, m);
if (n) eina_hash_add(m->networks, path, n);
}
if (n) iwd_network_apply_props(n, props);
}
/* Adapter / KnownNetwork: TODO (not needed for first connect path) */
iwd_manager_notify(m);
}
static void
_on_iface_removed(void *data, const char *path, const char *iface)
{
Iwd_Manager *m = data;
if (!strcmp(iface, IWD_IFACE_STATION))
{
Iwd_Device *d = eina_hash_find(m->devices, path);
if (d) iwd_device_detach_station(d);
}
else if (!strcmp(iface, IWD_IFACE_DEVICE))
{
eina_hash_del(m->devices, path, NULL);
}
else if (!strcmp(iface, IWD_IFACE_NETWORK))
{
eina_hash_del(m->networks, path, NULL);
}
iwd_manager_notify(m);
}
static void
_on_name_appeared(void *data EINA_UNUSED) { /* GetManagedObjects will populate */ }
static void
_on_name_vanished(void *data)
{
Iwd_Manager *m = data;
eina_hash_free_buckets(m->devices);
eina_hash_free_buckets(m->networks);
m->state = IWD_STATE_OFF;
iwd_manager_notify(m);
}
/* ----- lifecycle ------------------------------------------------------- */
static void _device_free_cb (void *d) { iwd_device_free(d); }
static void _network_free_cb(void *d) { iwd_network_free(d); }
Iwd_Manager *
iwd_manager_new(Eldbus_Connection *conn)
{
Iwd_Manager *m = calloc(1, sizeof(*m));
m->dbus = iwd_dbus_new(conn);
m->state = IWD_STATE_OFF;
if (!m) return NULL;
m->devices = eina_hash_string_superfast_new(_device_free_cb);
m->networks = eina_hash_string_superfast_new(_network_free_cb);
m->state = IWD_STATE_OFF;
Iwd_Dbus_Callbacks cbs = {
.name_appeared = _on_name_appeared,
.name_vanished = _on_name_vanished,
.iface_added = _on_iface_added,
.iface_removed = _on_iface_removed,
};
m->dbus = iwd_dbus_new(conn, &cbs, m);
return m;
}
@ -29,16 +192,33 @@ iwd_manager_free(Iwd_Manager *m)
{
if (!m) return;
iwd_dbus_free(m->dbus);
eina_list_free(m->devices);
eina_list_free(m->listeners);
eina_hash_free(m->devices);
eina_hash_free(m->networks);
Listener *li;
EINA_LIST_FREE(m->listeners, li) free(li);
free(m);
}
Iwd_State iwd_manager_state(const Iwd_Manager *m) { return m ? m->state : IWD_STATE_OFF; }
const Eina_List *iwd_manager_devices(const Iwd_Manager *m) { return m ? m->devices : NULL; }
Iwd_State iwd_manager_state (const Iwd_Manager *m) { return m ? m->state : IWD_STATE_OFF; }
const Eina_Hash *iwd_manager_devices (const Iwd_Manager *m) { return m ? m->devices : NULL; }
const Eina_Hash *iwd_manager_networks (const Iwd_Manager *m) { return m ? m->networks : NULL; }
void iwd_manager_scan_request(Iwd_Manager *m EINA_UNUSED) { /* TODO */ }
void iwd_manager_set_powered (Iwd_Manager *m EINA_UNUSED, Eina_Bool on EINA_UNUSED) { /* TODO */ }
void
iwd_manager_scan_request(Iwd_Manager *m)
{
if (!m) return;
Eina_Iterator *it = eina_hash_iterator_data_new(m->devices);
Iwd_Device *d;
EINA_ITERATOR_FOREACH(it, d) iwd_device_scan(d);
eina_iterator_free(it);
}
void iwd_manager_listener_add(Iwd_Manager *m EINA_UNUSED, Iwd_Manager_Cb cb EINA_UNUSED, void *data EINA_UNUSED) { /* TODO */ }
void iwd_manager_listener_del(Iwd_Manager *m EINA_UNUSED, Iwd_Manager_Cb cb EINA_UNUSED, void *data EINA_UNUSED) { /* TODO */ }
void
iwd_manager_set_powered(Iwd_Manager *m, Eina_Bool on)
{
if (!m) return;
Eina_Iterator *it = eina_hash_iterator_data_new(m->devices);
Iwd_Device *d;
EINA_ITERATOR_FOREACH(it, d) iwd_device_set_powered(d, on);
eina_iterator_free(it);
}

View file

@ -19,14 +19,20 @@ Iwd_Manager *iwd_manager_new (Eldbus_Connection *conn);
void iwd_manager_free(Iwd_Manager *m);
Iwd_State iwd_manager_state (const Iwd_Manager *m);
const Eina_List *iwd_manager_devices (const Iwd_Manager *m);
/* Hash<path, Iwd_Device*> */
const Eina_Hash *iwd_manager_devices (const Iwd_Manager *m);
/* Hash<path, Iwd_Network*> */
const Eina_Hash *iwd_manager_networks(const Iwd_Manager *m);
void iwd_manager_scan_request (Iwd_Manager *m);
void iwd_manager_set_powered (Iwd_Manager *m, Eina_Bool on);
/* Event callback signature for UI layer */
typedef void (*Iwd_Manager_Cb)(void *data, Iwd_Manager *m);
void iwd_manager_listener_add (Iwd_Manager *m, Iwd_Manager_Cb cb, void *data);
void iwd_manager_listener_del (Iwd_Manager *m, Iwd_Manager_Cb cb, void *data);
/* Internal: invoked by sub-objects when their state changes. */
void iwd_manager_notify (Iwd_Manager *m);
#endif

View file

@ -1,11 +1,67 @@
#include "iwd_network.h"
#include "iwd_dbus.h"
#include "iwd_props.h"
#include "iwd_manager.h"
#include <stdlib.h>
#include <string.h>
static Iwd_Security
_sec_from_str(const char *s)
{
if (!s) return IWD_SEC_UNKNOWN;
if (!strcmp(s, "open")) return IWD_SEC_OPEN;
if (!strcmp(s, "psk")) return IWD_SEC_PSK;
if (!strcmp(s, "8021x")) return IWD_SEC_8021X;
if (!strcmp(s, "wep")) return IWD_SEC_WEP;
return IWD_SEC_UNKNOWN;
}
static void
_prop_cb(void *data, const char *key, Eldbus_Message_Iter *v)
{
Iwd_Network *n = data;
if (!strcmp(key, "Name")) { free(n->ssid); n->ssid = iwd_props_str_dup(v); }
else if (!strcmp(key, "Type")) { char *s = iwd_props_str_dup(v); n->security = _sec_from_str(s); free(s); }
else if (!strcmp(key, "Connected")) { n->connected = iwd_props_bool(v); }
else if (!strcmp(key, "Device")) { free(n->device_path); n->device_path = iwd_props_str_dup(v); }
else if (!strcmp(key, "KnownNetwork")) { free(n->known_path); n->known_path = iwd_props_str_dup(v); }
}
void
iwd_network_apply_props(Iwd_Network *n, Eldbus_Message_Iter *props)
{
iwd_props_for_each(props, _prop_cb, n);
}
static void
_on_props_changed(void *data, const Eldbus_Message *msg)
{
Iwd_Network *n = data;
const char *iface;
Eldbus_Message_Iter *changed, *invalidated;
if (!eldbus_message_arguments_get(msg, "sa{sv}as", &iface, &changed, &invalidated))
return;
if (strcmp(iface, IWD_IFACE_NETWORK) != 0) return;
iwd_props_for_each(changed, _prop_cb, n);
if (n->manager) iwd_manager_notify(n->manager);
}
Iwd_Network *
iwd_network_new(Eldbus_Connection *conn EINA_UNUSED, const char *path)
iwd_network_new(Eldbus_Connection *conn, const char *path, void *manager)
{
Iwd_Network *n = calloc(1, sizeof(*n));
if (path) n->path = strdup(path);
/* TODO: read Name, Type, KnownNetwork properties */
if (!n) return NULL;
n->path = path ? strdup(path) : NULL;
n->manager = manager;
n->security = IWD_SEC_UNKNOWN;
n->obj = eldbus_object_get(conn, IWD_BUS_NAME, path);
if (n->obj)
{
n->proxy = eldbus_proxy_get(n->obj, IWD_IFACE_NETWORK);
if (n->proxy)
n->sh_props = eldbus_proxy_properties_changed_callback_add(
n->proxy, _on_props_changed, n);
}
return n;
}
@ -13,10 +69,37 @@ void
iwd_network_free(Iwd_Network *n)
{
if (!n) return;
if (n->sh_props) eldbus_signal_handler_del(n->sh_props);
if (n->proxy) eldbus_proxy_unref(n->proxy);
if (n->obj) eldbus_object_unref(n->obj);
free(n->path);
free(n->ssid);
free(n->device_path);
free(n->known_path);
free(n);
}
void iwd_network_connect(Iwd_Network *n EINA_UNUSED) { /* TODO: Network.Connect() */ }
void iwd_network_forget (Iwd_Network *n EINA_UNUSED) { /* TODO: KnownNetwork.Forget() */ }
void
iwd_network_connect(Iwd_Network *n)
{
if (!n || !n->proxy) return;
/* Network.Connect() takes no args; iwd will dial the registered Agent
* for a passphrase if needed. */
eldbus_proxy_call(n->proxy, "Connect", NULL, NULL, -1, "");
}
void
iwd_network_forget(Iwd_Network *n)
{
if (!n || !n->known_path || !n->obj) return;
Eldbus_Connection *conn = eldbus_object_connection_get(n->obj);
Eldbus_Object *kobj = eldbus_object_get(conn, IWD_BUS_NAME, n->known_path);
if (!kobj) return;
Eldbus_Proxy *kp = eldbus_proxy_get(kobj, IWD_IFACE_KNOWN_NETWORK);
if (kp)
{
eldbus_proxy_call(kp, "Forget", NULL, NULL, -1, "");
eldbus_proxy_unref(kp);
}
eldbus_object_unref(kobj);
}

View file

@ -9,6 +9,7 @@ typedef enum {
IWD_SEC_PSK,
IWD_SEC_8021X,
IWD_SEC_WEP,
IWD_SEC_UNKNOWN,
} Iwd_Security;
typedef struct _Iwd_Network Iwd_Network;
@ -17,16 +18,25 @@ struct _Iwd_Network
{
char *path;
char *ssid;
char *device_path;
char *known_path;
Iwd_Security security;
int signal; /* 0..100 */
Eina_Bool known;
Eina_Bool connected;
Eldbus_Object *obj;
Eldbus_Proxy *proxy;
Eldbus_Signal_Handler *sh_props;
void *manager;
};
Iwd_Network *iwd_network_new (Eldbus_Connection *conn, const char *path);
Iwd_Network *iwd_network_new (Eldbus_Connection *conn, const char *path, void *manager);
void iwd_network_free(Iwd_Network *n);
void iwd_network_apply_props(Iwd_Network *n, Eldbus_Message_Iter *props);
void iwd_network_connect (Iwd_Network *n);
/* Forget acts on the KnownNetwork object referenced by this network. */
void iwd_network_forget (Iwd_Network *n);
#endif

38
src/iwd/iwd_props.c Normal file
View file

@ -0,0 +1,38 @@
#include "iwd_props.h"
#include <string.h>
void
iwd_props_for_each(Eldbus_Message_Iter *dict, Iwd_Prop_Cb cb, void *data)
{
if (!dict || !cb) return;
Eldbus_Message_Iter *entry;
while (eldbus_message_iter_get_and_next(dict, 'e', &entry))
{
const char *key;
Eldbus_Message_Iter *variant;
if (!eldbus_message_iter_arguments_get(entry, "sv", &key, &variant))
continue;
cb(data, key, variant);
}
}
char *
iwd_props_str_dup(Eldbus_Message_Iter *variant)
{
if (!variant) return NULL;
const char *sig = eldbus_message_iter_signature_get(variant);
if (!sig || (sig[0] != 's' && sig[0] != 'o')) { free((void *)sig); return NULL; }
const char *s = NULL;
char want[2] = { sig[0], 0 };
free((void *)sig);
if (!eldbus_message_iter_arguments_get(variant, want, &s)) return NULL;
return s ? strdup(s) : NULL;
}
Eina_Bool
iwd_props_bool(Eldbus_Message_Iter *variant)
{
Eina_Bool b = EINA_FALSE;
if (variant) eldbus_message_iter_arguments_get(variant, "b", &b);
return b;
}

15
src/iwd/iwd_props.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef IWD_PROPS_H
#define IWD_PROPS_H
#include <Eldbus.h>
#include <Eina.h>
/* Iterate an a{sv} message iter, calling cb for every entry. */
typedef void (*Iwd_Prop_Cb)(void *data, const char *key, Eldbus_Message_Iter *value);
void iwd_props_for_each(Eldbus_Message_Iter *dict, Iwd_Prop_Cb cb, void *data);
/* Extract a string from a variant iter ("s" or "o"). Returns strdup'd copy. */
char *iwd_props_str_dup(Eldbus_Message_Iter *variant);
Eina_Bool iwd_props_bool(Eldbus_Message_Iter *variant);
#endif

View file

@ -4,6 +4,7 @@ e_iwd_sources = [
'e_mod_gadget.c',
'e_mod_popup.c',
'iwd/iwd_dbus.c',
'iwd/iwd_props.c',
'iwd/iwd_manager.c',
'iwd/iwd_device.c',
'iwd/iwd_network.c',