This is an automated email from the git hooks/post-receive script.

git pushed a commit to reference refs/pull/142/head
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.

Reply via email to