From 6ea19e22522338e7ab44e8570c5ce25bac2b86f6 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Apr 2026 22:42:38 +0700 Subject: [PATCH] Phase 1: register iwd Agent for passphrase prompts Export net.connman.iwd.Agent at /net/eiwd/agent and register it via AgentManager. RequestPassphrase replies are deferred so the UI can prompt asynchronously; the manager exposes iwd_manager_set_passphrase_handler for the UI layer to plug in. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/iwd/iwd_agent.c | 149 ++++++++++++++++++++++++++++++++++++++++++ src/iwd/iwd_agent.h | 23 +++++++ src/iwd/iwd_dbus.c | 10 ++- src/iwd/iwd_manager.c | 24 ++++++- src/iwd/iwd_manager.h | 7 ++ src/meson.build | 1 + 6 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 src/iwd/iwd_agent.c create mode 100644 src/iwd/iwd_agent.h diff --git a/src/iwd/iwd_agent.c b/src/iwd/iwd_agent.c new file mode 100644 index 0000000..f0337fc --- /dev/null +++ b/src/iwd/iwd_agent.c @@ -0,0 +1,149 @@ +#include "iwd_agent.h" +#include "iwd_dbus.h" +#include +#include + +#define IWD_AGENT_PATH "/net/eiwd/agent" + +struct _Iwd_Agent +{ + Eldbus_Connection *conn; + Eldbus_Service_Interface *svc; + Eldbus_Object *am_obj; + Eldbus_Proxy *am_proxy; + Iwd_Agent_Passphrase_Cb cb; + void *data; +}; + +struct _Iwd_Agent_Request +{ + Iwd_Agent *agent; + Eldbus_Message *msg; /* original RequestPassphrase message */ +}; + +static Iwd_Agent *_self = NULL; /* one agent per process is plenty */ + +/* ----- Method handlers ------------------------------------------------- */ + +static Eldbus_Message * +_release_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, + const Eldbus_Message *msg) +{ + return eldbus_message_method_return_new(msg); +} + +static Eldbus_Message * +_cancel_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, + const Eldbus_Message *msg) +{ + /* iwd dropped the auth attempt; we just ack. */ + return eldbus_message_method_return_new(msg); +} + +static Eldbus_Message * +_request_passphrase_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, + const Eldbus_Message *msg) +{ + const char *path = NULL; + if (!eldbus_message_arguments_get(msg, "o", &path)) + return eldbus_message_error_new(msg, "net.connman.iwd.Error.InvalidArgs", + "Expected object path"); + if (!_self || !_self->cb) + return eldbus_message_error_new(msg, "net.connman.iwd.Agent.Error.Canceled", + "No UI handler"); + + Iwd_Agent_Request *req = calloc(1, sizeof(*req)); + req->agent = _self; + req->msg = eldbus_message_ref((Eldbus_Message *)msg); + _self->cb(_self->data, req, path); + /* Deferred reply: returning NULL keeps the message pending. */ + return NULL; +} + +static const Eldbus_Method _methods[] = { + { "Release", NULL, + NULL, _release_cb, 0 }, + { "RequestPassphrase", + ELDBUS_ARGS({ "o", "network" }), + ELDBUS_ARGS({ "s", "passphrase" }), + _request_passphrase_cb, 0 }, + { "Cancel", + ELDBUS_ARGS({ "s", "reason" }), + NULL, _cancel_cb, 0 }, + { NULL, NULL, NULL, NULL, 0 } +}; + +static const Eldbus_Service_Interface_Desc _iface_desc = { + IWD_IFACE_AGENT, _methods, NULL, NULL, NULL, NULL +}; + +/* ----- Reply / cancel from the UI ------------------------------------- */ + +void +iwd_agent_reply(Iwd_Agent_Request *req, const char *passphrase) +{ + if (!req) return; + Eldbus_Message *r = eldbus_message_method_return_new(req->msg); + eldbus_message_arguments_append(r, "s", passphrase ? passphrase : ""); + eldbus_connection_send(req->agent->conn, r, NULL, NULL, -1); + eldbus_message_unref(req->msg); + free(req); +} + +void +iwd_agent_cancel(Iwd_Agent_Request *req) +{ + if (!req) return; + Eldbus_Message *e = eldbus_message_error_new(req->msg, + "net.connman.iwd.Agent.Error.Canceled", + "User canceled"); + eldbus_connection_send(req->agent->conn, e, NULL, NULL, -1); + eldbus_message_unref(req->msg); + free(req); +} + +/* ----- Registration with iwd ------------------------------------------ */ + +static void +_on_register(void *data EINA_UNUSED, const Eldbus_Message *msg, + Eldbus_Pending *p EINA_UNUSED) +{ + const char *en, *em; + if (eldbus_message_error_get(msg, &en, &em)) + fprintf(stderr, "e_iwd: agent register failed: %s: %s\n", en, em); +} + +Iwd_Agent * +iwd_agent_new(Eldbus_Connection *conn, Iwd_Agent_Passphrase_Cb cb, void *data) +{ + Iwd_Agent *a = calloc(1, sizeof(*a)); + if (!a) return NULL; + a->conn = conn; + a->cb = cb; + a->data = data; + _self = a; + + a->svc = eldbus_service_interface_register(conn, IWD_AGENT_PATH, &_iface_desc); + if (!a->svc) { free(a); _self = NULL; return NULL; } + + a->am_obj = eldbus_object_get(conn, IWD_BUS_NAME, "/net/connman/iwd"); + if (a->am_obj) + { + a->am_proxy = eldbus_proxy_get(a->am_obj, IWD_IFACE_AGENT_MANAGER); + if (a->am_proxy) + eldbus_proxy_call(a->am_proxy, "RegisterAgent", _on_register, NULL, -1, + "o", IWD_AGENT_PATH); + } + return a; +} + +void +iwd_agent_free(Iwd_Agent *a) +{ + if (!a) return; + if (a->svc) eldbus_service_interface_unregister(a->svc); + if (a->am_proxy) eldbus_proxy_unref(a->am_proxy); + if (a->am_obj) eldbus_object_unref(a->am_obj); + if (_self == a) _self = NULL; + free(a); +} diff --git a/src/iwd/iwd_agent.h b/src/iwd/iwd_agent.h new file mode 100644 index 0000000..3f899b8 --- /dev/null +++ b/src/iwd/iwd_agent.h @@ -0,0 +1,23 @@ +#ifndef IWD_AGENT_H +#define IWD_AGENT_H + +#include + +typedef struct _Iwd_Agent Iwd_Agent; +typedef struct _Iwd_Agent_Request Iwd_Agent_Request; + +/* The UI registers a single handler that is called whenever iwd asks for + * a passphrase. The handler must eventually call iwd_agent_reply() or + * iwd_agent_cancel() with the request token. */ +typedef void (*Iwd_Agent_Passphrase_Cb)(void *data, + Iwd_Agent_Request *req, + const char *network_path); + +Iwd_Agent *iwd_agent_new (Eldbus_Connection *conn, + Iwd_Agent_Passphrase_Cb cb, void *data); +void iwd_agent_free(Iwd_Agent *a); + +void iwd_agent_reply (Iwd_Agent_Request *req, const char *passphrase); +void iwd_agent_cancel(Iwd_Agent_Request *req); + +#endif diff --git a/src/iwd/iwd_dbus.c b/src/iwd/iwd_dbus.c index b95c471..32167c2 100644 --- a/src/iwd/iwd_dbus.c +++ b/src/iwd/iwd_dbus.c @@ -1,4 +1,5 @@ #include "iwd_dbus.h" +#include #include #include @@ -40,6 +41,7 @@ _emit_managed(Iwd_Dbus *d, Eldbus_Message_Iter *objects) Eldbus_Message_Iter *props; if (!eldbus_message_iter_arguments_get(iface_entry, "sa{sv}", &iface, &props)) continue; + fprintf(stderr, "e_iwd: %s :: %s\n", path, iface); if (d->cbs.iface_added) d->cbs.iface_added(d->data, path, iface, props); } @@ -53,12 +55,16 @@ _on_get_managed(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending E const char *errname, *errmsg; if (eldbus_message_error_get(msg, &errname, &errmsg)) { - /* iwd not present yet — name watcher will retry. */ + fprintf(stderr, "e_iwd: GetManagedObjects error: %s: %s\n", errname, errmsg); return; } Eldbus_Message_Iter *objects; if (!eldbus_message_arguments_get(msg, "a{oa{sa{sv}}}", &objects)) - return; + { + fprintf(stderr, "e_iwd: GetManagedObjects: failed to parse top-level dict\n"); + return; + } + fprintf(stderr, "e_iwd: GetManagedObjects reply received, walking objects\n"); _emit_managed(d, objects); } diff --git a/src/iwd/iwd_manager.c b/src/iwd/iwd_manager.c index 622cbb3..61bfe83 100644 --- a/src/iwd/iwd_manager.c +++ b/src/iwd/iwd_manager.c @@ -14,13 +14,33 @@ typedef struct _Listener struct _Iwd_Manager { Iwd_Dbus *dbus; + Iwd_Agent *agent; Eina_Hash *devices; /* path → Iwd_Device * */ Eina_Hash *networks; /* path → Iwd_Network * */ Eina_List *listeners; /* Listener * */ Iwd_State state; Eina_Bool notify_pending; + + Iwd_Agent_Passphrase_Cb pass_cb; + void *pass_data; }; +static void +_passphrase_trampoline(void *data, Iwd_Agent_Request *req, const char *path) +{ + Iwd_Manager *m = data; + if (m->pass_cb) m->pass_cb(m->pass_data, req, path); + else iwd_agent_cancel(req); +} + +void +iwd_manager_set_passphrase_handler(Iwd_Manager *m, Iwd_Agent_Passphrase_Cb cb, void *data) +{ + if (!m) return; + m->pass_cb = cb; + m->pass_data = data; +} + static void _recompute_state(Iwd_Manager *m); /* ----- listeners ------------------------------------------------------- */ @@ -183,7 +203,8 @@ iwd_manager_new(Eldbus_Connection *conn) .iface_added = _on_iface_added, .iface_removed = _on_iface_removed, }; - m->dbus = iwd_dbus_new(conn, &cbs, m); + m->dbus = iwd_dbus_new(conn, &cbs, m); + m->agent = iwd_agent_new(conn, _passphrase_trampoline, m); return m; } @@ -191,6 +212,7 @@ void iwd_manager_free(Iwd_Manager *m) { if (!m) return; + iwd_agent_free(m->agent); iwd_dbus_free(m->dbus); eina_hash_free(m->devices); eina_hash_free(m->networks); diff --git a/src/iwd/iwd_manager.h b/src/iwd/iwd_manager.h index 0fd4b5b..25ccddf 100644 --- a/src/iwd/iwd_manager.h +++ b/src/iwd/iwd_manager.h @@ -3,6 +3,7 @@ #include #include +#include "iwd_agent.h" typedef enum { IWD_STATE_OFF, @@ -35,4 +36,10 @@ 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); +/* The UI installs its passphrase prompt here. The handler must + * eventually call iwd_agent_reply()/iwd_agent_cancel() with the request. */ +void iwd_manager_set_passphrase_handler(Iwd_Manager *m, + Iwd_Agent_Passphrase_Cb cb, + void *data); + #endif diff --git a/src/meson.build b/src/meson.build index a07b912..3aa556b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -5,6 +5,7 @@ e_iwd_sources = [ 'e_mod_popup.c', 'iwd/iwd_dbus.c', 'iwd/iwd_props.c', + 'iwd/iwd_agent.c', 'iwd/iwd_manager.c', 'iwd/iwd_device.c', 'iwd/iwd_network.c',