summaryrefslogtreecommitdiffstats
path: root/src/connman-agent.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/connman-agent.c')
-rw-r--r--src/connman-agent.c239
1 files changed, 239 insertions, 0 deletions
diff --git a/src/connman-agent.c b/src/connman-agent.c
new file mode 100644
index 0000000..46209fe
--- /dev/null
+++ b/src/connman-agent.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2018,2019,2022 Konsulko Group
+ * Author: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "connman-glib.h"
+#include "common.h"
+#include "connman-call.h"
+#include "call_work.h"
+
+static connman_agent_event_cb_t agent_event_cb = NULL;
+static gpointer agent_event_cb_data = NULL;
+static GMutex agent_event_cb_mutex;
+
+EXPORT void connman_add_agent_event_callback(connman_agent_event_cb_t cb, gpointer user_data)
+{
+ g_mutex_lock(&agent_event_cb_mutex);
+ if (agent_event_cb == NULL) {
+ agent_event_cb = cb;
+ agent_event_cb_data = user_data;
+ } else {
+ ERROR("Agent event callback already set");
+ }
+ g_mutex_unlock(&agent_event_cb_mutex);
+}
+
+static void run_callback(const gchar *service, const int id, GVariant *properties)
+{
+ g_mutex_lock(&agent_event_cb_mutex);
+ if (agent_event_cb) {
+ (*agent_event_cb)(service, id, properties, agent_event_cb_data);
+ }
+ g_mutex_unlock(&agent_event_cb_mutex);
+}
+
+/* Introspection data for the agent service */
+static const gchar introspection_xml[] =
+"<node>"
+" <interface name='net.connman.Agent'>"
+" <method name='RequestInput'>"
+" <arg type='o' name='service' direction='in'/>"
+" <arg type='a{sv}' name='fields' direction='in'/>"
+" <arg type='a{sv}' name='fields' direction='out'/>"
+" </method>"
+" <method name='ReportError'>"
+" <arg type='o' name='service' direction='in'/>"
+" <arg type='s' name='error' direction='in'/>"
+" </method>"
+" </interface>"
+"</node>";
+
+static void handle_method_call(GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ struct connman_state *ns = user_data;
+ struct call_work *cw;
+ const gchar *service = NULL;
+ const gchar *path = NULL;
+
+ INFO("sender=%s", sender_name);
+ INFO("object_path=%s", object_path);
+ INFO("interface=%s", interface_name);
+ INFO("method=%s", method_name);
+
+ DEBUG("parameters = %s", g_variant_print(parameters, TRUE));
+
+ if (!g_strcmp0(method_name, "RequestInput")) {
+ GVariant *var = NULL;
+ g_variant_get(parameters, "(&o@a{sv})", &path, &var);
+ service = connman_strip_path(path);
+
+ call_work_lock(ns);
+
+ /* can only occur while a connect is issued */
+ cw = call_work_lookup_unlocked(ns,
+ "service",
+ service,
+ "connect_service");
+
+ /* if nothing is pending return an error */
+ if (!cw) {
+ call_work_unlock(ns);
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ "net.connman.Agent.Error.Canceled",
+ "No connection pending");
+ return;
+ }
+
+ cw->agent_method = "RequestInput";
+ cw->invocation = invocation;
+ int id = cw->id;
+
+ call_work_unlock(ns);
+
+ run_callback(service, id, var);
+
+ g_variant_unref(var);
+
+ return;
+ }
+
+ if (!g_strcmp0(method_name, "ReportError")) {
+ const gchar *strerr = NULL;
+ g_variant_get(parameters, "(&o&s)", &path, &strerr);
+
+ INFO("ReportError: service_path=%s error=%s", path, strerr);
+
+ return g_dbus_method_invocation_return_value(invocation, NULL);
+ }
+
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ "org.freedesktop.DBus.Error.UnknownMethod",
+ "Unknown method");
+}
+
+static const GDBusInterfaceVTable interface_vtable = {
+ .method_call = handle_method_call,
+ .get_property = NULL,
+ .set_property = NULL,
+};
+
+static void on_bus_acquired(GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ struct init_data *id = user_data;
+ struct connman_state *ns = id->ns;
+ GVariant *result;
+ GError *error = NULL;
+
+ INFO("agent bus acquired - registering %s", ns->agent_path);
+
+ ns->registration_id =
+ g_dbus_connection_register_object(connection,
+ ns->agent_path,
+ ns->introspection_data->interfaces[0],
+ &interface_vtable,
+ ns, /* user data */
+ NULL, /* user_data_free_func */
+ NULL);
+ if (!ns->registration_id) {
+ ERROR("failed to register agent to dbus");
+ goto err_unable_to_register_bus;
+
+ }
+
+ result = connman_call(ns, CONNMAN_AT_MANAGER, NULL,
+ "RegisterAgent",
+ g_variant_new("(o)", ns->agent_path),
+ &error);
+ if (!result) {
+ ERROR("failed to register agent to connman");
+ goto err_unable_to_register_connman;
+ }
+ g_variant_unref(result);
+
+ ns->agent_registered = TRUE;
+
+ INFO("agent registered at %s", ns->agent_path);
+ if (id->init_done_cb)
+ (*id->init_done_cb)(id, TRUE);
+
+ return;
+
+err_unable_to_register_connman:
+ g_dbus_connection_unregister_object(ns->conn, ns->registration_id);
+ ns->registration_id = 0;
+err_unable_to_register_bus:
+ if (id->init_done_cb)
+ (*id->init_done_cb)(id, FALSE);
+}
+
+int connman_register_agent(struct init_data *id)
+{
+ struct connman_state *ns = id->ns;
+
+ ns->agent_path = g_strdup_printf("%s/agent%d", CONNMAN_PATH, getpid());
+ if (!ns->agent_path) {
+ ERROR("can't create agent path");
+ goto out_no_agent_path;
+ }
+
+ ns->introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL);
+ if (!ns->introspection_data) {
+ ERROR("can't create introspection data");
+ goto out_no_introspection_data;
+ }
+
+ ns->agent_id = g_bus_own_name(G_BUS_TYPE_SYSTEM,
+ AGENT_SERVICE,
+ G_BUS_NAME_OWNER_FLAGS_REPLACE |
+ G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
+ on_bus_acquired,
+ NULL,
+ NULL,
+ id,
+ NULL);
+ if (!ns->agent_id) {
+ ERROR("can't create agent bus instance");
+ goto out_no_bus_name;
+ }
+
+ return 0;
+
+out_no_bus_name:
+ g_dbus_node_info_unref(ns->introspection_data);
+out_no_introspection_data:
+ g_free(ns->agent_path);
+out_no_agent_path:
+ return -1;
+}
+
+void connman_unregister_agent(struct connman_state *ns)
+{
+ g_bus_unown_name(ns->agent_id);
+ g_dbus_node_info_unref(ns->introspection_data);
+ g_free(ns->agent_path);
+}