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) <noreply@anthropic.com>
This commit is contained in:
nemunaire 2026-04-08 22:42:38 +07:00
commit 6ea19e2252
6 changed files with 211 additions and 3 deletions

149
src/iwd/iwd_agent.c Normal file
View file

@ -0,0 +1,149 @@
#include "iwd_agent.h"
#include "iwd_dbus.h"
#include <stdlib.h>
#include <string.h>
#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);
}

23
src/iwd/iwd_agent.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef IWD_AGENT_H
#define IWD_AGENT_H
#include <Eldbus.h>
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

View file

@ -1,4 +1,5 @@
#include "iwd_dbus.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -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);
}

View file

@ -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);

View file

@ -3,6 +3,7 @@
#include <Eldbus.h>
#include <Eina.h>
#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

View file

@ -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',