This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch connman-backend
in repository enlightenment.
View the commit online.
commit ed3a9bd4e1c8559163966d703c7b20c8382defb6
Author: Cedric BAIL <[email protected]>
AuthorDate: Mon Aug 3 11:13:13 2026 -0600
networkmanager - ask connman-vpnd for VPN credentials
VPN connections already show up and connect: connmand publishes vpnd's
connections as ordinary services of type "vpn", so the list, connect and
disconnect paths needed nothing. What was missing was somewhere for vpnd
to ask for a password, so connecting to a VPN that wants one failed with
no prompt.
vpnd is a separate daemon with a separate agent, but it asks the same way
connmand does - same RequestInput signature, same field description - so
the existing dialog serves both. The request handlers now look their
agent up from the interface the call arrived on rather than a global, and
the two exports differ only in object path and the error name used to
decline a request.
vpnd gets its own name owner tracking as it may be absent, start later or
restart on its own, and a restarted one has forgotten our agent.
Verified against the live daemon that it accepts an agent registration at
this path from a normal user session. The credential dialog itself is
untested - there is no VPN configured here to trigger it.
Co-Authored-By: Claude Opus 5 <[email protected]>
---
src/modules/networkmanager/e_net_connman.c | 79 +++++++++-
src/modules/networkmanager/e_net_connman.h | 5 +
src/modules/networkmanager/e_net_connman_agent.c | 188 +++++++++++++++++------
3 files changed, 219 insertions(+), 53 deletions(-)
diff --git a/src/modules/networkmanager/e_net_connman.c b/src/modules/networkmanager/e_net_connman.c
index 4ad920618..64d251217 100644
--- a/src/modules/networkmanager/e_net_connman.c
+++ b/src/modules/networkmanager/e_net_connman.c
@@ -26,6 +26,13 @@ extern const E_Net_Backend e_net_backend_connman;
#define CM_IFACE_SVC CM_BUS ".Service"
#define CM_IFACE_TECH CM_BUS ".Technology"
+/* connman-vpnd is a separate daemon and may not be installed at all. Its
+ * connections already reach us as ordinary services of type "vpn" via
+ * connmand, so the only thing wanted from it directly is somewhere to send
+ * credential requests. */
+#define CM_VPN_BUS "net.connman.vpn"
+#define CM_VPN_IFACE_MGR CM_VPN_BUS ".Manager"
+
#define CM_CONNECT_TIMEOUT (60 * 1000)
typedef struct _Cm_Service
@@ -61,6 +68,7 @@ static void _active_summary_update(void);
static Eldbus_Connection *_conn;
static Eldbus_Proxy *_mgr_proxy;
+static Eldbus_Proxy *_vpn_proxy;
static E_Net_Manager *_mgr;
static Eina_Hash *_services; /* path -> Cm_Service */
static Eina_List *_techs; /* Cm_Technology */
@@ -938,6 +946,61 @@ _cb_name_owner_changed(void *data EINA_UNUSED, const char *bus EINA_UNUSED,
}
}
+/* -------------------------------------------------------------------------- */
+/* VPN daemon */
+/* -------------------------------------------------------------------------- */
+
+static void
+_vpn_teardown(void)
+{
+ Eldbus_Object *obj;
+
+ if (!_vpn_proxy) return;
+
+ obj = eldbus_proxy_object_get(_vpn_proxy);
+ eldbus_proxy_unref(_vpn_proxy);
+ if (obj) eldbus_object_unref(obj);
+ _vpn_proxy = NULL;
+}
+
+static void
+_vpn_setup(void)
+{
+ Eldbus_Object *obj;
+
+ obj = eldbus_object_get(_conn, CM_VPN_BUS, "/");
+ _vpn_proxy = eldbus_proxy_get(obj, CM_VPN_IFACE_MGR);
+ if (!_vpn_proxy)
+ {
+ if (obj) eldbus_object_unref(obj);
+ return;
+ }
+
+ ecm_vpn_agent_register(_vpn_proxy);
+}
+
+static void
+_cb_vpn_name_owner_changed(void *data EINA_UNUSED, const char *bus EINA_UNUSED,
+ const char *from EINA_UNUSED, const char *to)
+{
+ if (to && to[0])
+ {
+ /* Start over rather than bail out if a proxy is already held: a
+ * restarted vpnd has forgotten our agent, and the registration is
+ * the whole point of holding the proxy. */
+ INF("connman-vpnd appeared");
+ _vpn_teardown();
+ _vpn_setup();
+ }
+ else
+ {
+ if (!_vpn_proxy) return;
+ INF("connman-vpnd vanished");
+ /* Nothing to unregister: the agent went with the daemon. */
+ _vpn_teardown();
+ }
+}
+
/* -------------------------------------------------------------------------- */
/* Action resolution */
/* -------------------------------------------------------------------------- */
@@ -984,6 +1047,11 @@ _be_init(void)
eldbus_name_owner_changed_callback_add(_conn, CM_BUS,
_cb_name_owner_changed, NULL,
EINA_TRUE);
+ /* Tracked separately: vpnd is its own daemon and may be absent, start
+ * later, or restart on its own. */
+ eldbus_name_owner_changed_callback_add(_conn, CM_VPN_BUS,
+ _cb_vpn_name_owner_changed, NULL,
+ EINA_TRUE);
return EINA_TRUE;
}
@@ -991,8 +1059,15 @@ static void
_be_shutdown(void)
{
if (_conn)
- eldbus_name_owner_changed_callback_del(_conn, CM_BUS,
- _cb_name_owner_changed, NULL);
+ {
+ eldbus_name_owner_changed_callback_del(_conn, CM_BUS,
+ _cb_name_owner_changed, NULL);
+ eldbus_name_owner_changed_callback_del(_conn, CM_VPN_BUS,
+ _cb_vpn_name_owner_changed,
+ NULL);
+ }
+ ecm_vpn_agent_unregister(_vpn_proxy);
+ _vpn_teardown();
_manager_teardown();
ecm_agent_shutdown();
diff --git a/src/modules/networkmanager/e_net_connman.h b/src/modules/networkmanager/e_net_connman.h
index 914947686..b3d3e1101 100644
--- a/src/modules/networkmanager/e_net_connman.h
+++ b/src/modules/networkmanager/e_net_connman.h
@@ -23,4 +23,9 @@ void ecm_agent_shutdown(void);
void ecm_agent_register(Eldbus_Proxy *mgr_proxy);
void ecm_agent_unregister(Eldbus_Proxy *mgr_proxy);
+/* Same, for the separate agent connman-vpnd asks for VPN credentials on.
+ * ecm_agent_init() exports both; these take the vpn manager proxy. */
+void ecm_vpn_agent_register(Eldbus_Proxy *vpn_proxy);
+void ecm_vpn_agent_unregister(Eldbus_Proxy *vpn_proxy);
+
#endif /* E_NET_CONNMAN_H */
diff --git a/src/modules/networkmanager/e_net_connman_agent.c b/src/modules/networkmanager/e_net_connman_agent.c
index f1509d0ce..8103e48cc 100644
--- a/src/modules/networkmanager/e_net_connman_agent.c
+++ b/src/modules/networkmanager/e_net_connman_agent.c
@@ -31,6 +31,17 @@
#define CM_AGENT_IFACE "net.connman.Agent"
#define CM_AGENT_KEY "agent"
+/*
+ * connman-vpnd runs as its own daemon with its own agent, asking for the
+ * credentials of a VPN connection the same way connmand asks for a wifi
+ * passphrase — same RequestInput signature, same field description — so the
+ * dialog below serves both. Only the object path and the error name used to
+ * decline a request differ, which is why the request handlers look their
+ * agent up from the interface they arrived on instead of a single global.
+ */
+#define CM_VPN_AGENT_PATH "/org/enlightenment/network/vpn/agent"
+#define CM_VPN_AGENT_IFACE "net.connman.vpn.Agent"
+
typedef struct _Cm_Agent_Input
{
char *key;
@@ -52,10 +63,20 @@ typedef struct _Cm_Agent
Eldbus_Service_Interface *iface;
Eldbus_Message *msg; /* the in-flight RequestInput */
Eldbus_Connection *conn;
+ const char *path;
+ const char *cancel_error; /* name to decline a request with */
Eina_Bool canceled;
} Cm_Agent;
-static Cm_Agent *_agent;
+static Cm_Agent *_agent; /* net.connman.Agent — services */
+static Cm_Agent *_vpn_agent; /* net.connman.vpn.Agent — vpn connections */
+
+static Cm_Agent *
+_agent_of(const Eldbus_Service_Interface *iface)
+{
+ if (!iface) return NULL;
+ return eldbus_service_object_data_get(iface, CM_AGENT_KEY);
+}
/* -------------------------------------------------------------------------- */
/* Reply building */
@@ -141,8 +162,7 @@ _dialog_send_cancel(Cm_Agent *agent)
Eldbus_Message *reply;
if (!agent->msg) return;
- reply = eldbus_message_error_new(agent->msg,
- "net.connman.Agent.Error.Canceled",
+ reply = eldbus_message_error_new(agent->msg, agent->cancel_error,
"User canceled dialog");
eldbus_connection_send(agent->conn, reply, NULL, NULL, -1);
}
@@ -313,11 +333,13 @@ _dialog_new(Cm_Agent *agent)
/* -------------------------------------------------------------------------- */
static Eldbus_Message *
-_m_release(const Eldbus_Service_Interface *iface EINA_UNUSED,
+_m_release(const Eldbus_Service_Interface *iface,
const Eldbus_Message *msg)
{
+ Cm_Agent *agent = _agent_of(iface);
+
DBG("agent released");
- if (_agent && _agent->dialog) e_object_del(E_OBJECT(_agent->dialog));
+ if (agent && agent->dialog) e_object_del(E_OBJECT(agent->dialog));
return eldbus_message_method_return_new(msg);
}
@@ -414,29 +436,30 @@ _field_parse(Cm_Field *field, Eldbus_Message_Iter *value)
}
static Eldbus_Message *
-_m_request_input(const Eldbus_Service_Interface *iface EINA_UNUSED,
+_m_request_input(const Eldbus_Service_Interface *iface,
const Eldbus_Message *msg)
{
Eldbus_Message_Iter *array, *entry;
+ Cm_Agent *agent = _agent_of(iface);
const char *path;
- if (!_agent) return NULL;
+ if (!agent) return NULL;
- if (_agent->msg == msg)
+ if (agent->msg == msg)
{
ERR("RequestInput re-entered with the same message");
return NULL;
}
/* Only one request can be outstanding; a new one supersedes the old. */
- if (_agent->msg) eldbus_message_unref(_agent->msg);
- _agent->msg = eldbus_message_ref((Eldbus_Message *)msg);
+ if (agent->msg) eldbus_message_unref(agent->msg);
+ agent->msg = eldbus_message_ref((Eldbus_Message *)msg);
enm_popups_del();
- if (_agent->dialog) e_object_del(E_OBJECT(_agent->dialog));
- _agent->dialog = _dialog_new(_agent);
- if (!_agent->dialog) goto err;
+ if (agent->dialog) e_object_del(E_OBJECT(agent->dialog));
+ agent->dialog = _dialog_new(agent);
+ if (!agent->dialog) goto err;
if (!eldbus_message_arguments_get(msg, "oa{sv}", &path, &array))
goto err;
@@ -458,7 +481,7 @@ _m_request_input(const Eldbus_Service_Interface *iface EINA_UNUSED,
else if (!field.requirement)
ERR("agent field '%s' with no requirement", field.name);
else
- _dialog_field_add(_agent, &field);
+ _dialog_field_add(agent, &field);
}
/* Replied to later, from the dialog. */
@@ -466,26 +489,31 @@ _m_request_input(const Eldbus_Service_Interface *iface EINA_UNUSED,
err:
WRN("could not parse RequestInput");
- if (_agent->msg)
+ if (agent->msg)
{
- eldbus_message_unref(_agent->msg);
- _agent->msg = NULL;
+ eldbus_message_unref(agent->msg);
+ agent->msg = NULL;
}
- return eldbus_message_method_return_new(msg);
+ /* Not a method return: the reply is declared to carry the filled-in
+ * fields, and an empty one would be malformed. */
+ return eldbus_message_error_new(msg, agent->cancel_error,
+ "Could not parse the input request");
}
static Eldbus_Message *
-_m_cancel(const Eldbus_Service_Interface *iface EINA_UNUSED,
+_m_cancel(const Eldbus_Service_Interface *iface,
const Eldbus_Message *msg)
{
+ Cm_Agent *agent = _agent_of(iface);
+
DBG("agent request canceled by connman");
/* connman has withdrawn the request, so the dialog must go away without
* replying to it. */
- if (_agent && _agent->dialog)
+ if (agent && agent->dialog)
{
- _agent->canceled = EINA_FALSE;
- e_object_del(E_OBJECT(_agent->dialog));
+ agent->canceled = EINA_FALSE;
+ e_object_del(E_OBJECT(agent->dialog));
}
return eldbus_message_method_return_new(msg);
}
@@ -508,66 +536,108 @@ static const Eldbus_Service_Interface_Desc _desc =
CM_AGENT_IFACE, _methods, NULL, NULL, NULL, NULL
};
+/* connman-vpnd has no captive portal to open, so no RequestBrowser. */
+static const Eldbus_Method _vpn_methods[] =
+{
+ { "Release", NULL, NULL, _m_release, 0 },
+ { "ReportError", ELDBUS_ARGS({"o", "connection"}, {"s", "error"}), NULL,
+ _m_report_error, 0 },
+ { "RequestInput", ELDBUS_ARGS({"o", "connection"}, {"a{sv}", "fields"}),
+ ELDBUS_ARGS({"a{sv}", ""}), _m_request_input, 0 },
+ { "Cancel", NULL, NULL, _m_cancel, 0 },
+ { NULL, NULL, NULL, NULL, 0 }
+};
+
+static const Eldbus_Service_Interface_Desc _vpn_desc =
+{
+ CM_VPN_AGENT_IFACE, _vpn_methods, NULL, NULL, NULL, NULL
+};
+
/* -------------------------------------------------------------------------- */
/* Lifecycle */
/* -------------------------------------------------------------------------- */
+static Cm_Agent *
+_agent_new(Eldbus_Connection *conn, const char *path,
+ const Eldbus_Service_Interface_Desc *desc, const char *cancel_error)
+{
+ Cm_Agent *agent;
+
+ agent = E_NEW(Cm_Agent, 1);
+ if (!agent) return NULL;
+
+ agent->conn = conn;
+ agent->path = path;
+ agent->cancel_error = cancel_error;
+ agent->iface = eldbus_service_interface_register(conn, path, desc);
+ if (!agent->iface)
+ {
+ ERR("could not export the agent interface at %s", path);
+ free(agent);
+ return NULL;
+ }
+ eldbus_service_object_data_set(agent->iface, CM_AGENT_KEY, agent);
+ return agent;
+}
+
+static void
+_agent_free(Cm_Agent **agentp)
+{
+ Cm_Agent *agent = *agentp;
+
+ if (!agent) return;
+ *agentp = NULL;
+
+ if (agent->dialog)
+ {
+ agent->canceled = EINA_TRUE;
+ e_object_del(E_OBJECT(agent->dialog));
+ }
+ if (agent->msg) eldbus_message_unref(agent->msg);
+ if (agent->iface) eldbus_service_object_unregister(agent->iface);
+ free(agent);
+}
+
void
ecm_agent_init(Eldbus_Connection *conn)
{
if (_agent) return;
- _agent = E_NEW(Cm_Agent, 1);
- if (!_agent) return;
-
- _agent->conn = conn;
- _agent->iface = eldbus_service_interface_register(conn, CM_AGENT_PATH,
- &_desc);
- if (!_agent->iface)
- {
- ERR("could not export the connman agent interface");
- free(_agent);
- _agent = NULL;
- return;
- }
- eldbus_service_object_data_set(_agent->iface, CM_AGENT_KEY, _agent);
+ _agent = _agent_new(conn, CM_AGENT_PATH, &_desc,
+ "net.connman.Agent.Error.Canceled");
+ /* Exported whether or not connman-vpnd is around — registering it is what
+ * waits on the daemon. */
+ _vpn_agent = _agent_new(conn, CM_VPN_AGENT_PATH, &_vpn_desc,
+ "net.connman.vpn.Agent.Error.Canceled");
}
void
ecm_agent_shutdown(void)
{
- if (!_agent) return;
-
- if (_agent->dialog)
- {
- _agent->canceled = EINA_TRUE;
- e_object_del(E_OBJECT(_agent->dialog));
- }
- if (_agent->msg) eldbus_message_unref(_agent->msg);
- if (_agent->iface) eldbus_service_object_unregister(_agent->iface);
- free(_agent);
- _agent = NULL;
+ _agent_free(&_agent);
+ _agent_free(&_vpn_agent);
}
static void
-_cb_register(void *data EINA_UNUSED, const Eldbus_Message *msg,
+_cb_register(void *data, const Eldbus_Message *msg,
Eldbus_Pending *pending EINA_UNUSED)
{
const char *ename, *etext;
+ const char *what = data;
if (eldbus_message_error_get(msg, &ename, &etext))
{
- WRN("could not register the connman agent: %s: %s", ename, etext);
+ WRN("could not register the %s agent: %s: %s", what, ename, etext);
return;
}
- INF("connman agent registered");
+ INF("%s agent registered", what);
}
void
ecm_agent_register(Eldbus_Proxy *mgr_proxy)
{
if (!mgr_proxy || !_agent) return;
- eldbus_proxy_call(mgr_proxy, "RegisterAgent", _cb_register, NULL, -1,
+ eldbus_proxy_call(mgr_proxy, "RegisterAgent", _cb_register, "connman", -1,
"o", CM_AGENT_PATH);
}
@@ -578,3 +648,19 @@ ecm_agent_unregister(Eldbus_Proxy *mgr_proxy)
eldbus_proxy_call(mgr_proxy, "UnregisterAgent", NULL, NULL, -1,
"o", CM_AGENT_PATH);
}
+
+void
+ecm_vpn_agent_register(Eldbus_Proxy *vpn_proxy)
+{
+ if (!vpn_proxy || !_vpn_agent) return;
+ eldbus_proxy_call(vpn_proxy, "RegisterAgent", _cb_register, "connman vpn",
+ -1, "o", CM_VPN_AGENT_PATH);
+}
+
+void
+ecm_vpn_agent_unregister(Eldbus_Proxy *vpn_proxy)
+{
+ if (!vpn_proxy || !_vpn_agent) return;
+ eldbus_proxy_call(vpn_proxy, "UnregisterAgent", NULL, NULL, -1,
+ "o", CM_VPN_AGENT_PATH);
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.