aboutsummaryrefslogtreecommitdiffstats
path: root/binding/network-api.c
diff options
context:
space:
mode:
Diffstat (limited to 'binding/network-api.c')
-rw-r--r--binding/network-api.c1886
1 files changed, 1886 insertions, 0 deletions
diff --git a/binding/network-api.c b/binding/network-api.c
new file mode 100644
index 0000000..3c4cf29
--- /dev/null
+++ b/binding/network-api.c
@@ -0,0 +1,1886 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+
+#include <glib.h>
+#include <stdlib.h>
+#include <gio/gio.h>
+#include <glib-object.h>
+
+#include <json-c/json.h>
+
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
+
+#include "network-api.h"
+#include "network-common.h"
+
+struct network_state *global_ns;
+
+/**
+ * The global thread
+ */
+static GThread *global_thread;
+
+struct init_data {
+ GCond cond;
+ GMutex mutex;
+ gboolean init_done;
+ struct network_state *ns; /* before setting global_ns */
+ int rc;
+};
+
+static void signal_init_done(struct init_data *id, int rc);
+
+static void call_work_lock(struct network_state *ns)
+{
+ g_mutex_lock(&ns->cw_mutex);
+}
+
+static void call_work_unlock(struct network_state *ns)
+{
+ g_mutex_unlock(&ns->cw_mutex);
+}
+
+struct call_work *call_work_lookup_unlocked(
+ struct network_state *ns,
+ const char *access_type, const char *type_arg,
+ const char *method)
+{
+ struct call_work *cw;
+ GSList *list;
+
+ /* we can only allow a single pending call */
+ for (list = ns->cw_pending; list; list = g_slist_next(list)) {
+ cw = list->data;
+ if (!g_strcmp0(access_type, cw->access_type) &&
+ !g_strcmp0(type_arg, cw->type_arg) &&
+ !g_strcmp0(method, cw->method))
+ return cw;
+ }
+ return NULL;
+}
+
+struct call_work *call_work_lookup(
+ struct network_state *ns,
+ const char *access_type, const char *type_arg,
+ const char *method)
+{
+ struct call_work *cw;
+
+ g_mutex_lock(&ns->cw_mutex);
+ cw = call_work_lookup_unlocked(ns, access_type, type_arg, method);
+ g_mutex_unlock(&ns->cw_mutex);
+
+ return cw;
+}
+
+int call_work_pending_id(
+ struct network_state *ns,
+ const char *access_type, const char *type_arg,
+ const char *method)
+{
+ struct call_work *cw;
+ int id = -1;
+
+ g_mutex_lock(&ns->cw_mutex);
+ cw = call_work_lookup_unlocked(ns, access_type, type_arg, method);
+ if (cw)
+ id = cw->id;
+ g_mutex_unlock(&ns->cw_mutex);
+
+ return id;
+}
+
+struct call_work *call_work_lookup_by_id_unlocked(
+ struct network_state *ns, int id)
+{
+ struct call_work *cw;
+ GSList *list;
+
+ /* we can only allow a single pending call */
+ for (list = ns->cw_pending; list; list = g_slist_next(list)) {
+ cw = list->data;
+ if (cw->id == id)
+ return cw;
+ }
+ return NULL;
+}
+
+struct call_work *call_work_lookup_by_id(
+ struct network_state *ns, int id)
+{
+ struct call_work *cw;
+
+ g_mutex_lock(&ns->cw_mutex);
+ cw = call_work_lookup_by_id_unlocked(ns, id);
+ g_mutex_unlock(&ns->cw_mutex);
+
+ return cw;
+}
+
+struct call_work *call_work_create_unlocked(struct network_state *ns,
+ const char *access_type, const char *type_arg,
+ const char *method, const char *connman_method,
+ GError **error)
+{
+
+ struct call_work *cw = NULL;
+
+ cw = call_work_lookup_unlocked(ns, access_type, type_arg, method);
+ if (cw) {
+ g_set_error(error, NB_ERROR, NB_ERROR_CALL_IN_PROGRESS,
+ "another call in progress (%s/%s/%s)",
+ access_type, type_arg, method);
+ return NULL;
+ }
+
+ /* no other pending; allocate */
+ cw = g_malloc0(sizeof(*cw));
+ cw->ns = ns;
+ do {
+ cw->id = ns->next_cw_id;
+ if (++ns->next_cw_id < 0)
+ ns->next_cw_id = 1;
+ } while (call_work_lookup_by_id_unlocked(ns, cw->id));
+
+ cw->access_type = g_strdup(access_type);
+ cw->type_arg = g_strdup(type_arg);
+ cw->method = g_strdup(method);
+ cw->connman_method = g_strdup(connman_method);
+
+ ns->cw_pending = g_slist_prepend(ns->cw_pending, cw);
+
+ return cw;
+}
+
+struct call_work *call_work_create(struct network_state *ns,
+ const char *access_type, const char *type_arg,
+ const char *method, const char *connman_method,
+ GError **error)
+{
+
+ struct call_work *cw;
+
+ g_mutex_lock(&ns->cw_mutex);
+ cw = call_work_create_unlocked(ns,
+ access_type, type_arg, method, connman_method,
+ error);
+ g_mutex_unlock(&ns->cw_mutex);
+
+ return cw;
+}
+
+void call_work_destroy_unlocked(struct call_work *cw)
+{
+ struct network_state *ns = cw->ns;
+ struct call_work *cw2;
+
+ /* verify that it's something we know about */
+ cw2 = call_work_lookup_by_id_unlocked(ns, cw->id);
+ if (cw2 != cw) {
+ AFB_ERROR("Bad call work to destroy");
+ return;
+ }
+
+ /* remove it */
+ ns->cw_pending = g_slist_remove(ns->cw_pending, cw);
+
+ g_free(cw->access_type);
+ g_free(cw->type_arg);
+ g_free(cw->method);
+ g_free(cw->connman_method);
+}
+
+void call_work_destroy(struct call_work *cw)
+{
+ struct network_state *ns = cw->ns;
+
+ g_mutex_lock(&ns->cw_mutex);
+ call_work_destroy_unlocked(cw);
+ g_mutex_unlock(&ns->cw_mutex);
+}
+
+static struct afb_event *get_event_from_value(struct network_state *ns,
+ const char *value)
+{
+ if (!g_strcmp0(value, "global_state"))
+ return &ns->global_state_event;
+
+ if (!g_strcmp0(value, "technologies"))
+ return &ns->technologies_event;
+
+ if (!g_strcmp0(value, "technology_properties"))
+ return &ns->technology_properties_event;
+
+ if (!g_strcmp0(value, "services"))
+ return &ns->services_event;
+
+ if (!g_strcmp0(value, "service_properties"))
+ return &ns->service_properties_event;
+
+ if (!g_strcmp0(value, "counter"))
+ return &ns->counter_event;
+
+ if (!g_strcmp0(value, "agent"))
+ return &ns->agent_event;
+
+ return NULL;
+}
+
+static void network_manager_signal_callback(
+ GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ struct network_state *ns = user_data;
+ GVariantIter *array1, *array2, *array3;
+ GError *error = NULL;
+ GVariant *var = NULL;
+ const gchar *path = NULL;
+ const gchar *key = NULL;
+ const gchar *basename;
+ json_object *jresp = NULL, *jobj, *jprop;
+ struct afb_event *event = NULL;
+ GVariantIter *array;
+ gboolean is_config, ret;
+
+ /* AFB_INFO("sender=%s", sender_name);
+ AFB_INFO("object_path=%s", object_path);
+ AFB_INFO("interface=%s", interface_name);
+ AFB_INFO("signal=%s", signal_name); */
+
+ if (!g_strcmp0(signal_name, "TechnologyAdded")) {
+
+ g_variant_get(parameters, "o(a{sv})", &path, &array);
+
+ basename = connman_strip_path(path);
+ g_assert(basename); /* guaranteed by dbus */
+
+ jresp = json_object_new_object();
+
+ json_object_object_add(jresp, "technology",
+ json_object_new_string(basename));
+ json_object_object_add(jresp, "action",
+ json_object_new_string("added"));
+
+ jobj = json_object_new_object();
+ while (g_variant_iter_loop(array, "{sv}", &key, &var)) {
+ ret = technology_property_dbus2json(jobj,
+ key, var, &is_config, &error);
+ if (!ret) {
+ AFB_WARNING("%s property %s - %s",
+ "technology",
+ key, error->message);
+ g_clear_error(&error);
+ }
+ }
+ g_variant_iter_free(array);
+
+ json_object_object_add(jresp, "properties", jobj);
+
+ event = &ns->technologies_event;
+
+
+ } else if (!g_strcmp0(signal_name, "TechnologyRemoved")) {
+
+ g_variant_get(parameters, "o", &path);
+
+ basename = connman_strip_path(path);
+ g_assert(basename); /* guaranteed by dbus */
+
+ jresp = json_object_new_object();
+
+ json_object_object_add(jresp, "technology",
+ json_object_new_string(basename));
+ json_object_object_add(jresp, "action",
+ json_object_new_string("removed"));
+
+ event = &ns->technologies_event;
+
+ } else if (!g_strcmp0(signal_name, "ServicesChanged")) {
+
+ jresp = json_object_new_array();
+
+ g_variant_get(parameters, "(a(oa{sv})ao)", &array1, &array2);
+
+ while (g_variant_iter_loop(array1, "(oa{sv})", &path, &array3)) {
+
+ basename = connman_strip_path(path);
+ g_assert(basename); /* guaranteed by dbus */
+
+ jobj = json_object_new_object();
+
+ json_object_object_add(jobj, "service",
+ json_object_new_string(basename));
+
+ jprop = NULL;
+ while (g_variant_iter_loop(array3,
+ "{sv}", &key, &var)) {
+ if (!jprop)
+ jprop = json_object_new_object();
+ ret = service_property_dbus2json(jprop, key, var,
+ &is_config, &error);
+ if (!ret) {
+ AFB_WARNING("%s property %s - %s",
+ "service",
+ key, error->message);
+ g_clear_error(&error);
+ }
+ }
+
+ json_object_object_add(jobj, "action",
+ json_object_new_string(jprop ?
+ "changed" : "unchanged"));
+
+ if (jprop)
+ json_object_object_add(jobj, "properties",
+ jprop);
+
+ json_object_array_add(jresp, jobj);
+ }
+
+ while (g_variant_iter_loop(array2, "o", &path)) {
+
+ basename = connman_strip_path(path);
+ g_assert(basename); /* guaranteed by dbus */
+
+ jobj = json_object_new_object();
+
+ json_object_object_add(jobj, "service",
+ json_object_new_string(basename));
+
+ json_object_object_add(jobj, "action",
+ json_object_new_string("removed"));
+
+ json_object_array_add(jresp, jobj);
+ }
+
+ g_variant_iter_free(array2);
+ g_variant_iter_free(array1);
+
+ event = &ns->services_event;
+
+ } else if (!g_strcmp0(signal_name, "PropertyChanged")) {
+
+ jresp = json_object_new_object();
+
+ g_variant_get(parameters, "(sv)", &key, &var);
+ g_clear_error(&error);
+ ret = manager_property_dbus2json(jresp,
+ key, var, &is_config, &error);
+ if (!ret) {
+ AFB_WARNING("%s property %s - %s",
+ "manager",
+ key, error->message);
+ g_clear_error(&error);
+ json_object_put(jresp);
+ jresp = NULL;
+ } else
+ event = &ns->global_state_event;
+ }
+
+ /* if (jresp)
+ printf("manager-signal\n%s\n",
+ json_object_to_json_string_ext(jresp,
+ JSON_C_TO_STRING_PRETTY)); */
+
+ if (event && jresp) {
+ afb_event_push(*event, jresp);
+ jresp = NULL;
+ }
+
+ json_object_put(jresp);
+}
+
+static void network_technology_signal_callback(
+ GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ struct network_state *ns = user_data;
+ GError *error = NULL;
+ GVariant *var = NULL;
+ const gchar *key = NULL;
+ const gchar *basename;
+ json_object *jresp = NULL, *jobj;
+ struct afb_event *event = NULL;
+ gboolean is_config, ret;
+
+ /* AFB_INFO("sender=%s", sender_name);
+ AFB_INFO("object_path=%s", object_path);
+ AFB_INFO("interface=%s", interface_name);
+ AFB_INFO("signal=%s", signal_name); */
+
+ /* a basename must exist and be at least 1 character wide */
+ basename = connman_strip_path(object_path);
+ g_assert(basename);
+
+ if (!g_strcmp0(signal_name, "PropertyChanged")) {
+
+ jobj = json_object_new_object();
+
+ g_variant_get(parameters, "(sv)", &key, &var);
+
+ g_clear_error(&error);
+ ret = technology_property_dbus2json(jobj, key, var, &is_config,
+ &error) ;
+ g_variant_unref(var);
+ var = NULL;
+
+ if (!ret) {
+ AFB_ERROR("unhandled manager property %s - %s",
+ key, error->message);
+ json_object_put(jobj);
+ return;
+ }
+
+ jresp = json_object_new_object();
+
+ json_object_object_add(jresp, "technology",
+ json_object_new_string(basename));
+
+ json_object_object_add(jresp, "properties", jobj);
+ event = &ns->technology_properties_event;
+ }
+
+ /* if (jresp)
+ printf("technology-signal\n%s\n",
+ json_object_to_json_string_ext(jresp,
+ JSON_C_TO_STRING_PRETTY)); */
+
+ if (event && jresp) {
+ afb_event_push(*event, jresp);
+ jresp = NULL;
+ }
+
+ json_object_put(jresp);
+}
+
+static void network_service_signal_callback(
+ GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ struct network_state *ns = user_data;
+ GError *error = NULL;
+ GVariant *var = NULL;
+ const gchar *key = NULL;
+ const gchar *basename;
+ json_object *jresp = NULL, *jobj;
+ struct afb_event *event = NULL;
+ gboolean is_config, ret;
+
+ /* AFB_INFO("sender=%s", sender_name);
+ AFB_INFO("object_path=%s", object_path);
+ AFB_INFO("interface=%s", interface_name);
+ AFB_INFO("signal=%s", signal_name); */
+
+ /* a basename must exist and be at least 1 character wide */
+ basename = connman_strip_path(object_path);
+ g_assert(basename);
+
+ if (!g_strcmp0(signal_name, "PropertyChanged")) {
+
+ g_variant_get(parameters, "(sv)", &key, &var);
+
+ jobj = json_object_new_object();
+ ret = service_property_dbus2json(jobj,
+ key, var, &is_config, &error);
+
+ g_variant_unref(var);
+ var = NULL;
+
+ if (!ret) {
+ AFB_ERROR("unhandled %s property %s - %s",
+ "service", key, error->message);
+ json_object_put(jobj);
+ return;
+ }
+
+ jresp = json_object_new_object();
+
+ json_object_object_add(jresp, "service",
+ json_object_new_string(basename));
+
+ json_object_object_add(jresp, "properties",
+ jobj);
+
+ event = &ns->service_properties_event;
+ }
+
+ /* if (jresp)
+ printf("service-signal\n%s\n",
+ json_object_to_json_string_ext(jresp,
+ JSON_C_TO_STRING_PRETTY)); */
+
+ if (event && jresp) {
+ afb_event_push(*event, jresp);
+ jresp = NULL;
+ }
+
+ json_object_put(jresp);
+}
+
+/* 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 const struct property_info agent_request_input_props[] = {
+ {
+ .name = "Name",
+ .fmt = "{sv}",
+ .sub = (const struct property_info []) {
+ { .name = "Type", .fmt = "s", },
+ { .name = "Requirement", .fmt = "s", },
+ { .name = "Alternates", .fmt = "s", },
+ { },
+ },
+ }, {
+ .name = "SSID",
+ .fmt = "{sv}",
+ .sub = (const struct property_info []) {
+ { .name = "Type", .fmt = "s", },
+ { .name = "Requirement", .fmt = "s", },
+ { },
+ },
+ }, {
+ .name = "Identity",
+ .fmt = "{sv}",
+ .sub = (const struct property_info []) {
+ { .name = "Type", .fmt = "s", },
+ { .name = "Requirement", .fmt = "s", },
+ { },
+ },
+ }, {
+ .name = "Passphrase",
+ .fmt = "{sv}",
+ .sub = (const struct property_info []) {
+ { .name = "Type", .fmt = "s", },
+ { .name = "Requirement", .fmt = "s", },
+ { },
+ },
+ }, {
+ .name = "WPS",
+ .fmt = "{sv}",
+ .sub = (const struct property_info []) {
+ { .name = "Type", .fmt = "s", },
+ { .name = "Requirement", .fmt = "s", },
+ { },
+ },
+ },
+ { }
+};
+
+static const struct property_info agent_request_input_out_props[] = {
+ {
+ .name = "Name",
+ .fmt = "s",
+ }, {
+ .name = "SSID",
+ .fmt = "s",
+ }, {
+ .name = "Identity",
+ .fmt = "s",
+ }, {
+ .name = "Passphrase",
+ .fmt = "s",
+ }, {
+ .name = "WPS",
+ .fmt = "s",
+ },
+ { }
+};
+
+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 network_state *ns = user_data;
+ struct call_work *cw;
+ json_object *jev = NULL, *jprop;
+ GVariantIter *array;
+ const gchar *path = NULL;
+ const gchar *service = NULL;
+ const gchar *key = NULL;
+ const gchar *strerr = NULL;
+ GVariant *var = NULL;
+ gboolean is_config;
+
+ /* AFB_INFO("sender=%s", sender_name);
+ AFB_INFO("object_path=%s", object_path);
+ AFB_INFO("interface=%s", interface_name);
+ AFB_INFO("method=%s", method_name); */
+
+ if (!g_strcmp0(method_name, "RequestInput")) {
+
+ jev = json_object_new_object();
+ jprop = json_object_new_object();
+ g_variant_get(parameters, "(oa{sv})", &path, &array);
+ while (g_variant_iter_loop(array, "{sv}", &key, &var)) {
+ root_property_dbus2json(jprop, agent_request_input_props,
+ key, var, &is_config);
+ }
+ g_variant_iter_free(array);
+
+ 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);
+ json_object_put(jprop);
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ "net.connman.Agent.Error.Canceled",
+ "No connection pending");
+ return;
+ }
+
+ json_object_object_add(jev, "id",
+ json_object_new_int(cw->id));
+ json_object_object_add(jev, "method",
+ json_object_new_string("request-input"));
+ json_object_object_add(jev, "service",
+ json_object_new_string(service));
+ json_object_object_add(jev, "fields", jprop);
+
+ cw->agent_method = "RequestInput";
+ cw->invocation = invocation;
+
+ call_work_unlock(ns);
+
+ /* AFB_INFO("request-input: jev=%s",
+ json_object_to_json_string(jev)); */
+
+ afb_event_push(ns->agent_event, jev);
+
+ return;
+ }
+
+ if (!g_strcmp0(method_name, "ReportError")) {
+
+ g_variant_get(parameters, "(os)", &path, &strerr);
+
+ AFB_INFO("report-error: 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",
+ "Uknown 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 network_state *ns = id->ns;
+ GVariant *result;
+ GError *error = NULL;
+
+ AFB_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) {
+ AFB_ERROR("failed to register agent to dbus");
+ goto err_unable_to_register_bus;
+
+ }
+
+ result = manager_call(ns, "RegisterAgent",
+ g_variant_new("(o)", ns->agent_path),
+ &error);
+ if (!result) {
+ AFB_ERROR("failed to register agent to connman");
+ goto err_unable_to_register_connman;
+ }
+ g_variant_unref(result);
+
+ ns->agent_registered = TRUE;
+
+ AFB_INFO("agent registered at %s", ns->agent_path);
+ signal_init_done(id, 0);
+
+ 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:
+ signal_init_done(id, -1);
+}
+
+static int network_register_agent(struct init_data *id)
+{
+ struct network_state *ns = id->ns;
+
+ ns->agent_path = g_strdup_printf("%s/agent%d",
+ CONNMAN_PATH, getpid());
+ if (!ns->agent_path) {
+ AFB_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) {
+ AFB_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) {
+ AFB_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;
+}
+
+static void network_unregister_agent(struct network_state *ns)
+{
+ g_bus_unown_name(ns->agent_id);
+ g_dbus_node_info_unref(ns->introspection_data);
+ g_free(ns->agent_path);
+}
+
+static struct network_state *network_init(GMainLoop *loop)
+{
+ struct network_state *ns;
+ GError *error = NULL;
+
+ ns = g_try_malloc0(sizeof(*ns));
+ if (!ns) {
+ AFB_ERROR("out of memory allocating network state");
+ goto err_no_ns;
+ }
+
+ AFB_INFO("connecting to dbus");
+
+ ns->loop = loop;
+ ns->conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (!ns->conn) {
+ if (error)
+ g_dbus_error_strip_remote_error(error);
+ AFB_ERROR("Cannot connect to D-Bus, %s",
+ error ? error->message : "unspecified");
+ g_error_free(error);
+ goto err_no_conn;
+
+ }
+
+ AFB_INFO("connected to dbus");
+
+ ns->global_state_event =
+ afb_daemon_make_event("global_state");
+ ns->technologies_event =
+ afb_daemon_make_event("tecnologies_event");
+ ns->technology_properties_event =
+ afb_daemon_make_event("tecnology_properties");
+ ns->services_event =
+ afb_daemon_make_event("services_event");
+ ns->service_properties_event =
+ afb_daemon_make_event("service_properties");
+ ns->counter_event =
+ afb_daemon_make_event("counter");
+ ns->agent_event =
+ afb_daemon_make_event("agent");
+
+ if (!afb_event_is_valid(ns->global_state_event) ||
+ !afb_event_is_valid(ns->technologies_event) ||
+ !afb_event_is_valid(ns->technology_properties_event) ||
+ !afb_event_is_valid(ns->services_event) ||
+ !afb_event_is_valid(ns->service_properties_event) ||
+ !afb_event_is_valid(ns->counter_event) ||
+ !afb_event_is_valid(ns->agent_event)) {
+ AFB_ERROR("Cannot create events");
+ goto err_no_events;
+ }
+
+ ns->manager_sub = g_dbus_connection_signal_subscribe(
+ ns->conn,
+ NULL, /* sender */
+ CONNMAN_MANAGER_INTERFACE,
+ NULL, /* member */
+ NULL, /* object path */
+ NULL, /* arg0 */
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ network_manager_signal_callback,
+ ns,
+ NULL);
+ if (!ns->manager_sub) {
+ AFB_ERROR("Unable to subscribe to manager signal");
+ goto err_no_manager_sub;
+ }
+
+ ns->technology_sub = g_dbus_connection_signal_subscribe(
+ ns->conn,
+ NULL, /* sender */
+ CONNMAN_TECHNOLOGY_INTERFACE,
+ NULL, /* member */
+ NULL, /* object path */
+ NULL, /* arg0 */
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ network_technology_signal_callback,
+ ns,
+ NULL);
+ if (!ns->technology_sub) {
+ AFB_ERROR("Unable to subscribe to technology signal");
+ goto err_no_technology_sub;
+ }
+
+ ns->service_sub = g_dbus_connection_signal_subscribe(
+ ns->conn,
+ NULL, /* sender */
+ CONNMAN_SERVICE_INTERFACE,
+ NULL, /* member */
+ NULL, /* object path */
+ NULL, /* arg0 */
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ network_service_signal_callback,
+ ns,
+ NULL);
+ if (!ns->service_sub) {
+ AFB_ERROR("Unable to subscribe to service signal");
+ goto err_no_service_sub;
+ }
+
+ g_mutex_init(&ns->cw_mutex);
+ ns->next_cw_id = 1;
+
+ return ns;
+
+err_no_service_sub:
+ g_dbus_connection_signal_unsubscribe(ns->conn, ns->technology_sub);
+err_no_technology_sub:
+ g_dbus_connection_signal_unsubscribe(ns->conn, ns->manager_sub);
+err_no_manager_sub:
+ /* no way to clear the events */
+err_no_events:
+ g_dbus_connection_close(ns->conn, NULL, NULL, NULL);
+err_no_conn:
+ g_free(ns);
+err_no_ns:
+ return NULL;
+}
+
+static void network_cleanup(struct network_state *ns)
+{
+ g_dbus_connection_signal_unsubscribe(ns->conn, ns->service_sub);
+ g_dbus_connection_signal_unsubscribe(ns->conn, ns->technology_sub);
+ g_dbus_connection_signal_unsubscribe(ns->conn, ns->manager_sub);
+ g_dbus_connection_close(ns->conn, NULL, NULL, NULL);
+ g_free(ns);
+}
+
+static void signal_init_done(struct init_data *id, int rc)
+{
+ g_mutex_lock(&id->mutex);
+ id->init_done = TRUE;
+ id->rc = rc;
+ g_cond_signal(&id->cond);
+ g_mutex_unlock(&id->mutex);
+}
+
+static gpointer network_func(gpointer ptr)
+{
+ struct init_data *id = ptr;
+ struct network_state *ns;
+ GMainLoop *loop;
+ int rc;
+
+ loop = g_main_loop_new(NULL, FALSE);
+ if (!loop) {
+ AFB_ERROR("Unable to create main loop");
+ goto err_no_loop;
+ }
+
+ /* real network init */
+ ns = network_init(loop);
+ if (!ns) {
+ AFB_ERROR("network_init() failed");
+ goto err_no_ns;
+ }
+
+ id->ns = ns;
+ rc = network_register_agent(id);
+ if (rc) {
+ AFB_ERROR("network_register_agent() failed");
+ goto err_no_agent;
+ }
+
+ /* note that we wait for agent registration to signal done */
+
+ global_ns = ns;
+ g_main_loop_run(loop);
+
+ g_main_loop_unref(ns->loop);
+
+ network_unregister_agent(ns);
+
+ network_cleanup(ns);
+ global_ns = NULL;
+
+ return NULL;
+
+err_no_agent:
+ network_cleanup(ns);
+
+err_no_ns:
+ g_main_loop_unref(loop);
+
+err_no_loop:
+ signal_init_done(id, -1);
+
+ return NULL;
+}
+
+static int init(void)
+{
+ struct init_data init_data, *id = &init_data;
+ gint64 end_time;
+
+ memset(id, 0, sizeof(*id));
+ id->init_done = FALSE;
+ id->rc = 0;
+ g_cond_init(&id->cond);
+ g_mutex_init(&id->mutex);
+
+ global_thread = g_thread_new("agl-service-network",
+ network_func,
+ id);
+
+ AFB_INFO("network-binding waiting for init done");
+
+ /* wait maximum 10 seconds for init done */
+ end_time = g_get_monotonic_time () + 10 * G_TIME_SPAN_SECOND;
+ g_mutex_lock(&id->mutex);
+ while (!id->init_done) {
+ if (!g_cond_wait_until(&id->cond, &id->mutex, end_time))
+ break;
+ }
+ g_mutex_unlock(&id->mutex);
+
+ if (!id->init_done) {
+ AFB_ERROR("network-binding init timeout");
+ return -1;
+ }
+
+ if (id->rc)
+ AFB_ERROR("network-binding init thread returned %d",
+ id->rc);
+ else
+ AFB_INFO("network-binding operational");
+
+ return id->rc;
+}
+
+static void network_ping(struct afb_req request)
+{
+ struct network_state *ns = global_ns;
+ json_object *jresp = json_object_new_object();
+ json_object *jev = json_object_new_object();
+
+ /* push fake counter event */
+ ns->ping_counter++;
+ jev = json_object_new_object();
+ json_object_object_add(jev, "counter",
+ json_object_new_int(ns->ping_counter));
+ afb_event_push(ns->counter_event, jev);
+
+ afb_req_success(request, jresp, "Network binding - pong");
+}
+
+static void network_subscribe_unsubscribe(struct afb_req request,
+ gboolean unsub)
+{
+ struct network_state *ns = global_ns;
+ json_object *jresp = json_object_new_object();
+ const char *value;
+ struct afb_event *event;
+ int rc;
+
+ /* if value exists means to set offline mode */
+ value = afb_req_value(request, "value");
+ if (!value) {
+ afb_req_fail_f(request, "failed", "Missing \"value\" event");
+ return;
+ }
+
+ event = get_event_from_value(ns, value);
+ if (!event) {
+ afb_req_fail_f(request, "failed", "Bad \"value\" event \"%s\"",
+ value);
+ return;
+ }
+
+ if (!unsub)
+ rc = afb_req_subscribe(request, *event);
+ else
+ rc = afb_req_unsubscribe(request, *event);
+ if (rc != 0) {
+ afb_req_fail_f(request, "failed",
+ "%s error on \"value\" event \"%s\"",
+ !unsub ? "subscribe" : "unsubscribe",
+ value);
+ return;
+ }
+
+ afb_req_success_f(request, jresp, "Network %s to event \"%s\"",
+ !unsub ? "subscribed" : "unsubscribed",
+ value);
+}
+
+static void network_subscribe(struct afb_req request)
+{
+ network_subscribe_unsubscribe(request, FALSE);
+}
+
+static void network_unsubscribe(struct afb_req request)
+{
+ network_subscribe_unsubscribe(request, TRUE);
+}
+
+static void network_state(struct afb_req request)
+{
+ struct network_state *ns = global_ns;
+ GError *error = NULL;
+ json_object *jresp;
+
+ jresp = manager_get_property(ns, FALSE, "State", &error);
+ if (!jresp) {
+ afb_req_fail_f(request, "failed", "property %s error %s",
+ "State", error->message);
+ return;
+ }
+
+ afb_req_success(request, jresp, "Network - state");
+}
+
+static void network_offline(struct afb_req request)
+{
+ struct network_state *ns = global_ns;
+ GError *error = NULL;
+ json_object *jresp = NULL;
+ const char *value;
+ int set_to;
+ gboolean ret;
+
+ /* if value exists means to set offline mode */
+ value = afb_req_value(request, "value");
+ if (!value) {
+ jresp = manager_get_property(ns, FALSE, "OfflineMode", &error);
+ if (!jresp) {
+ afb_req_fail_f(request, "failed", "property %s error %s",
+ "OfflineMode", error->message);
+ g_error_free(error);
+ return;
+ }
+ } else {
+ set_to = str2boolean(value);
+ if (set_to < 0) {
+ afb_req_fail_f(request, "failed", "bad value \"%s\"",
+ value);
+ return;
+ }
+ ret = manager_set_property(ns, FALSE, "OfflineMode",
+ json_object_new_boolean(set_to), &error);
+ if (!ret) {
+ afb_req_fail_f(request, "failed",
+ "Network - offline mode set to %s failed - %s",
+ set_to ? "true" : "false", error->message);
+ g_error_free(error);
+ return;
+ }
+ }
+
+ afb_req_success(request, jresp, "Network - offline mode");
+}
+
+static void network_technologies(struct afb_req request)
+{
+ struct network_state *ns = global_ns;
+ json_object *jresp;
+ GError *error = NULL;
+
+ /* if value exists means to set offline mode */
+ jresp = technology_properties(ns, &error, NULL);
+ if (!jresp) {
+ afb_req_fail_f(request, "failed", "technology properties error %s",
+ error->message);
+ g_error_free(error);
+ return;
+ }
+
+ afb_req_success(request, jresp, "Network - Network Technologies");
+}
+
+static void network_services(struct afb_req request)
+{
+ struct network_state *ns = global_ns;
+ json_object *jresp;
+ GError *error = NULL;
+
+ jresp = service_properties(ns, &error, NULL);
+ if (!jresp) {
+ afb_req_fail_f(request, "failed", "service properties error %s",
+ error->message);
+ g_error_free(error);
+ return;
+ }
+
+ afb_req_success(request, jresp, "Network - Network Services");
+}
+
+static void network_technology_set_powered(struct afb_req request,
+ gboolean powered)
+{
+ struct network_state *ns = global_ns;
+ GError *error = NULL;
+ const char *technology;
+ json_object *jpowered;
+ gboolean ret, this_powered;
+
+ /* if value exists means to set offline mode */
+ technology = afb_req_value(request, "technology");
+ if (!technology) {
+ afb_req_fail(request, "failed",
+ "technology argument missing");
+ return;
+ }
+
+ jpowered = technology_get_property(ns, technology,
+ FALSE, "Powered", &error);
+ if (!jpowered) {
+ afb_req_fail_f(request, "failed",
+ "Network - failed to get current Powered state - %s",
+ error->message);
+ g_error_free(error);
+ return;
+ }
+ this_powered = json_object_get_boolean(jpowered);
+ json_object_put(jpowered);
+ jpowered = NULL;
+
+ if (this_powered == powered) {
+ afb_req_success_f(request, NULL,
+ "Network - Technology %s already %s",
+ technology, powered ? "enabled" : "disabled");
+ return;
+ }
+
+ ret = technology_set_property(ns, technology,
+ FALSE, "Powered",
+ json_object_new_boolean(powered), &error);
+ if (!ret) {
+ afb_req_fail_f(request, "failed",
+ "Network - failed to set Powered state - %s",
+ error->message);
+ g_error_free(error);
+ return;
+ }
+
+ afb_req_success_f(request, NULL, "Network - Technology %s %s",
+ technology, powered ? "enabled" : "disabled");
+}
+
+static void network_enable_technology(struct afb_req request)
+{
+ return network_technology_set_powered(request, TRUE);
+}
+
+static void network_disable_technology(struct afb_req request)
+{
+ return network_technology_set_powered(request, FALSE);
+}
+
+static void network_scan_services(struct afb_req request)
+{
+ struct network_state *ns = global_ns;
+ GVariant *reply = NULL;
+ GError *error = NULL;
+ const char *technology;
+
+ /* if value exists means to set offline mode */
+ technology = afb_req_value(request, "technology");
+ if (!technology) {
+ afb_req_fail(request, "failed", "No technology given to enable");
+ return;
+ }
+
+ reply = technology_call(ns, technology, "Scan", NULL, &error);
+ if (!reply) {
+ afb_req_fail_f(request, "failed",
+ "technology %s method %s error %s",
+ technology, "Scan", error->message);
+ g_error_free(error);
+ return;
+ }
+ g_variant_unref(reply);
+
+ afb_req_success_f(request, json_object_new_object(),
+ "Network - technology %s scan complete",
+ technology);
+}
+
+static void network_move_service(struct afb_req request)
+{
+ struct network_state *ns = global_ns;
+ GVariant *reply = NULL;
+ GError *error = NULL;
+ const char *service;
+ const char *other_service;
+ const char *method_name;
+ gboolean before_after; /* false=before, true=after */
+
+ /* first service */
+ service = afb_req_value(request, "service");
+ if (!service) {
+ afb_req_fail(request, "failed", "No service given to move");
+ return;
+ }
+
+ before_after = FALSE;
+ other_service = afb_req_value(request, "before_service");
+ if (!other_service) {
+ before_after = TRUE;
+ other_service = afb_req_value(request, "after_service");
+ }
+
+ if (!other_service) {
+ afb_req_fail(request, "failed", "No other service given for move");
+ return;
+ }
+
+ method_name = before_after ? "MoveAfter" : "MoveBefore";
+
+ reply = service_call(ns, service, method_name,
+ g_variant_new("o", CONNMAN_SERVICE_PATH(other_service)),
+ &error);
+ if (!reply) {
+ afb_req_fail_f(request, "failed", "%s error %s",
+ method_name,
+ error ? error->message : "unspecified");
+ g_error_free(error);
+ return;
+ }
+ g_variant_unref(reply);
+
+ afb_req_success_f(request, NULL, "Network - service %s moved %s service %s",
+ service, before_after ? "before" : "after", other_service);
+}
+
+static void network_remove_service(struct afb_req request)
+{
+ struct network_state *ns = global_ns;
+ GVariant *reply = NULL;
+ GError *error = NULL;
+ const char *service;
+
+ /* first service */
+ service = afb_req_value(request, "service");
+ if (!service) {
+ afb_req_fail(request, "failed", "No service");
+ return;
+ }
+
+ reply = service_call(ns, service, "Remove", NULL, &error);
+ if (!reply) {
+ afb_req_fail_f(request, "failed", "Remove error %s",
+ error ? error->message : "unspecified");
+ g_error_free(error);
+ return;
+ }
+ g_variant_unref(reply);
+
+ afb_req_success_f(request, NULL, "Network - service %s removed",
+ service);
+}
+
+/* reset_counters as async implementation for illustrative purposes */
+
+static void reset_counters_callback(void *user_data,
+ GVariant *result, GError **error)
+{
+ struct call_work *cw = user_data;
+ struct network_state *ns = cw->ns;
+
+ connman_decode_call_error(ns,
+ cw->access_type, cw->type_arg, cw->connman_method,
+ error);
+ if (error && *error) {
+ afb_req_fail_f(cw->request, "failed", "%s error %s",
+ cw->method, (*error)->message);
+ goto out_free;
+ }
+
+ if (result)
+ g_variant_unref(result);
+
+ afb_req_success_f(cw->request, NULL, "Network - service %s %s",
+ cw->type_arg, cw->method);
+out_free:
+ afb_req_unref(cw->request);
+ call_work_destroy(cw);
+}
+
+static void network_reset_counters(struct afb_req request)
+{
+ struct network_state *ns = global_ns;
+ GError *error = NULL;
+ const char *service;
+ struct call_work *cw;
+
+ /* first service */
+ service = afb_req_value(request, "service");
+ if (!service) {
+ afb_req_fail(request, "failed",
+ "No service given");
+ return;
+ }
+
+ cw = call_work_create(ns, "service", service,
+ "reset_counters", "ResetCounters", &error);
+ if (!cw) {
+ afb_req_fail_f(request, "failed", "can't queue work %s",
+ error->message);
+ g_error_free(error);
+ return;
+ }
+
+ cw->request = request;
+ afb_req_addref(request);
+ cw->cpw = connman_call_async(ns, "service", service,
+ "ResetCounters", NULL, &error,
+ reset_counters_callback, cw);
+ if (!cw->cpw) {
+ afb_req_fail_f(request, "failed", "reset counters error %s",
+ error->message);
+ call_work_destroy(cw);
+ g_error_free(error);
+ return;
+ }
+}
+
+static void connect_service_callback(void *user_data,
+ GVariant *result, GError **error)
+{
+ struct call_work *cw = user_data;
+ struct network_state *ns = cw->ns;
+ struct json_object *jerr;
+ GError *sub_error = NULL;
+
+ connman_decode_call_error(ns,
+ cw->access_type, cw->type_arg, cw->connman_method,
+ error);
+ if (error && *error) {
+ /* read the Error property (if available to be specific) */
+
+ jerr = service_get_property(ns, cw->type_arg, FALSE,
+ "Error", &sub_error);
+ g_clear_error(&sub_error);
+ if (jerr) {
+ /* clear property error */
+ service_call(ns, cw->type_arg, "ClearProperty",
+ NULL, &sub_error);
+ g_clear_error(&sub_error);
+
+ afb_req_fail_f(cw->request, "failed", "Connect error: %s",
+ json_object_get_string(jerr));
+ json_object_put(jerr);
+ jerr = NULL;
+ } else
+ afb_req_fail_f(cw->request, "failed", "Connect error: %s",
+ (*error)->message);
+ goto out_free;
+ }
+
+ if (result)
+ g_variant_unref(result);
+
+ afb_req_success_f(cw->request, NULL, "Network - service %s connected",
+ cw->type_arg);
+out_free:
+ afb_req_unref(cw->request);
+ call_work_destroy(cw);
+}
+
+static void network_connect_service(struct afb_req request)
+{
+ struct network_state *ns = global_ns;
+ GError *error = NULL;
+ const char *service;
+ struct call_work *cw;
+
+ /* first service */
+ service = afb_req_value(request, "service");
+ if (!service) {
+ afb_req_fail(request, "failed",
+ "No service given");
+ return;
+ }
+
+ cw = call_work_create(ns, "service", service,
+ "connect_service", "Connect", &error);
+ if (!cw) {
+ afb_req_fail_f(request, "failed", "can't queue work %s",
+ error->message);
+ g_error_free(error);
+ return;
+ }
+
+ cw->request = request;
+ afb_req_addref(request);
+ cw->cpw = connman_call_async(ns, "service", service,
+ "Connect", NULL, &error,
+ connect_service_callback, cw);
+ if (!cw->cpw) {
+ afb_req_fail_f(request, "failed", "connection error %s",
+ error->message);
+ call_work_destroy(cw);
+ g_error_free(error);
+ return;
+ }
+}
+
+static void network_disconnect_service(struct afb_req request)
+{
+ struct network_state *ns = global_ns;
+ json_object *jresp;
+ GVariant *reply = NULL;
+ GError *error = NULL;
+ const char *service;
+
+ /* first service */
+ service = afb_req_value(request, "service");
+ if (!service) {
+ afb_req_fail(request, "failed", "No service given to move");
+ return;
+ }
+
+ reply = service_call(ns, service, "Disconnect", NULL, &error);
+ if (!reply) {
+ afb_req_fail_f(request, "failed", "Disconnect error %s",
+ error ? error->message : "unspecified");
+ g_error_free(error);
+ return;
+ }
+
+ g_variant_unref(reply);
+
+ jresp = json_object_new_object();
+ afb_req_success_f(request, jresp, "Network - service %s disconnected",
+ service);
+}
+
+static void network_get_property(struct afb_req request)
+{
+ struct network_state *ns = global_ns;
+ json_object *jobj = afb_req_json(request);
+ const char *technology, *service;
+ const char *access_type;
+ const char *type_arg;
+ GError *error = NULL;
+ json_object *jprop, *jrespprop = NULL, *jreqprop = NULL;
+
+ (void)ns;
+
+ /* printf("%s\n", json_object_to_json_string_ext(jobj,
+ JSON_C_TO_STRING_PRETTY)); */
+
+ /* either or both may be NULL */
+ technology = afb_req_value(request, "technology");
+ service = afb_req_value(request, "service");
+
+ if (technology) {
+ access_type = CONNMAN_AT_TECHNOLOGY;
+ type_arg = technology;
+ } else if (service) {
+ access_type = CONNMAN_AT_SERVICE;
+ type_arg = service;
+ } else {
+ access_type = CONNMAN_AT_MANAGER;
+ type_arg = NULL;
+ }
+
+ jprop = connman_get_properties(ns, access_type, type_arg, &error);
+ if (!jprop) {
+ afb_req_fail_f(request, "failed", "%s property error %s",
+ access_type, error->message);
+ g_error_free(error);
+ return;
+ }
+
+ /* if properties exist, copy out only those */
+ if (json_object_object_get_ex(jobj, "properties", &jreqprop)) {
+ /* and now copy (if found) */
+ jrespprop = get_property_collect(jreqprop, jprop,
+ &error);
+ json_object_put(jprop);
+
+ if (!jrespprop) {
+ afb_req_fail_f(request, "failed", "error - %s",
+ error->message);
+ g_error_free(error);
+ return;
+ }
+ } else /* otherwise everything */
+ jrespprop = jprop;
+
+ afb_req_success_f(request, jrespprop, "Network - %s property get",
+ access_type);
+}
+
+static void network_set_property(struct afb_req request)
+{
+ struct network_state *ns = global_ns;
+ json_object *jobj = afb_req_json(request);
+ const char *technology, *service;
+ const char *access_type;
+ const char *type_arg;
+ json_object *jprop = NULL;
+ GError *error = NULL;
+ gboolean ret;
+ int count;
+
+ /* printf("%s\n", json_object_to_json_string_ext(jobj,
+ JSON_C_TO_STRING_PRETTY)); */
+
+ if (!json_object_object_get_ex(jobj, "properties", &jprop)) {
+ afb_req_fail_f(request, "failed", "Network - property set no properties");
+ return;
+ }
+
+ /* either or both may be NULL */
+ technology = afb_req_value(request, "technology");
+ service = afb_req_value(request, "service");
+
+ if (technology) {
+ access_type = CONNMAN_AT_TECHNOLOGY;
+ type_arg = technology;
+ } else if (service) {
+ access_type = CONNMAN_AT_SERVICE;
+ type_arg = service;
+ } else {
+ access_type = CONNMAN_AT_MANAGER;
+ type_arg = NULL;
+ }
+
+ /* iterate */
+ count = 0;
+ json_object_object_foreach(jprop, key, jval) {
+ ret = FALSE;
+
+ /* keep jval from being consumed */
+ json_object_get(jval);
+ ret = connman_set_property(ns, access_type, type_arg,
+ TRUE, key, jval, &error);
+ if (!ret) {
+ afb_req_fail_f(request, "failed",
+ "Network - set property %s to %s failed - %s",
+ key, json_object_to_json_string(jval),
+ error->message);
+ g_error_free(error);
+ return;
+ }
+ count++;
+ }
+
+ if (!count) {
+ afb_req_fail_f(request, "failed", "Network - property set empty");
+ return;
+ }
+
+ afb_req_success_f(request, NULL, "Network - %s %d propert%s set",
+ access_type, count, count > 1 ? "ies" : "y");
+}
+
+static void network_agent_response(struct afb_req request)
+{
+ struct network_state *ns = global_ns;
+ json_object *jobj = afb_req_json(request);
+ json_object *jfields;
+ const char *id_str;
+ int id;
+ const struct property_info *pi, *pi_sub;
+ GError *error = NULL;
+ gboolean ret;
+ struct call_work *cw;
+ GVariant *parameters = NULL, *item;
+ GVariantBuilder builder, builder2;
+ gboolean is_config;
+ gchar *dbus_name;
+
+ /* printf("%s\n", json_object_to_json_string(jobj)); */
+
+ /* either or both may be NULL */
+ id_str = afb_req_value(request, "id");
+ id = id_str ? (int)strtol(id_str, NULL, 10) : -1;
+ if (id <= 0) {
+ afb_req_fail_f(request, "failed",
+ "Network - agent response missing arguments");
+ return;
+ }
+
+ call_work_lock(ns);
+ cw = call_work_lookup_by_id_unlocked(ns, id);
+ if (!cw || !cw->invocation) {
+ call_work_unlock(ns);
+ afb_req_fail_f(request, "failed",
+ "Network - can't find request with id=%d", id);
+ return;
+ }
+
+ ret = FALSE;
+ parameters = NULL;
+ if (!g_strcmp0(cw->agent_method, "RequestInput") &&
+ json_object_object_get_ex(jobj, "fields", &jfields)) {
+
+ /* AFB_INFO("request-input response fields=%s",
+ json_object_to_json_string(jfields)); */
+
+ pi = agent_request_input_out_props;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
+ json_object_object_foreach(jfields, key_o, jval_o) {
+ pi_sub = property_by_json_name(pi, key_o, &is_config);
+ if (!pi_sub) {
+ AFB_ERROR("no property %s", key_o);
+ continue;
+ }
+
+ g_clear_error(&error);
+ item = property_json_to_gvariant(pi_sub, NULL, NULL,
+ jval_o, &error);
+ if (!item) {
+ AFB_ERROR("on %s", key_o);
+ continue;
+ }
+
+ dbus_name = property_get_name(pi, key_o);
+ g_assert(dbus_name); /* can't fail; but check */
+
+ g_variant_builder_add(&builder, "{sv}", dbus_name, item);
+
+ g_free(dbus_name);
+ }
+
+ /* dbus requires response to be wrapped as a tuple */
+ g_variant_builder_init(&builder2, G_VARIANT_TYPE_TUPLE);
+ g_variant_builder_add_value(&builder2,
+ g_variant_builder_end(&builder));
+
+ parameters = g_variant_builder_end(&builder2);
+ ret = TRUE;
+ }
+
+
+ if (!ret) {
+ afb_req_fail_f(request, "failed",
+ "Network - unhandled agent method %s",
+ cw->agent_method);
+ g_dbus_method_invocation_return_dbus_error(cw->invocation,
+ "org.freedesktop.DBus.Error.UnknownMethod",
+ "Uknown method");
+ cw->invocation = NULL;
+ call_work_unlock(ns);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value(cw->invocation, parameters);
+ cw->invocation = NULL;
+
+ call_work_unlock(ns);
+
+ afb_req_success_f(request, NULL, "Network - agent response sent");
+}
+
+static const struct afb_verb_v2 network_verbs[] = {
+ {
+ .verb = "ping",
+ .session = AFB_SESSION_NONE,
+ .callback = network_ping,
+ .info ="Check if binding is alive"
+ }, {
+ .verb = "subscribe",
+ .session = AFB_SESSION_NONE,
+ .callback = network_subscribe,
+ .info ="Subscribe to the event of 'value'",
+ }, {
+ .verb = "unsubscribe",
+ .session = AFB_SESSION_NONE,
+ .callback = network_unsubscribe,
+ .info ="Unsubscribe to the event of 'value'",
+ }, {
+ .verb = "state",
+ .session = AFB_SESSION_NONE,
+ .callback = network_state,
+ .info ="Return global network state"
+ }, {
+ .verb = "offline",
+ .session = AFB_SESSION_NONE,
+ .callback = network_offline,
+ .info ="Return global network state"
+ }, {
+ .verb = "technologies",
+ .session = AFB_SESSION_NONE,
+ .callback = network_technologies,
+ .info ="Return list of network technologies"
+ }, {
+ .verb = "services",
+ .session = AFB_SESSION_NONE,
+ .callback = network_services,
+ .info ="Return list of network services"
+ }, {
+ .verb = "enable_technology",
+ .session = AFB_SESSION_NONE,
+ .callback = network_enable_technology,
+ .info ="Enable a technology"
+ }, {
+ .verb = "disable_technology",
+ .session = AFB_SESSION_NONE,
+ .callback = network_disable_technology,
+ .info ="Disable a technology"
+ }, {
+ .verb = "scan_services",
+ .session = AFB_SESSION_NONE,
+ .callback = network_scan_services,
+ .info ="Scan services"
+ }, {
+ .verb = "move_service",
+ .session = AFB_SESSION_NONE,
+ .callback = network_move_service,
+ .info ="Arrange service order"
+ }, {
+ .verb = "remove_service",
+ .session = AFB_SESSION_NONE,
+ .callback = network_remove_service,
+ .info ="Remove service"
+ }, {
+ .verb = "reset_counters",
+ .session = AFB_SESSION_NONE,
+ .callback = network_reset_counters,
+ .info ="Reset service counters"
+ }, {
+ .verb = "connect_service",
+ .session = AFB_SESSION_NONE,
+ .callback = network_connect_service,
+ .info ="Connect service"
+ }, {
+ .verb = "disconnect_service",
+ .session = AFB_SESSION_NONE,
+ .callback = network_disconnect_service,
+ .info ="Disconnect service"
+ }, {
+ .verb = "get_property",
+ .session = AFB_SESSION_NONE,
+ .callback = network_get_property,
+ .info ="Get property"
+ }, {
+ .verb = "set_property",
+ .session = AFB_SESSION_NONE,
+ .callback = network_set_property,
+ .info ="Set property"
+ }, {
+ .verb = "agent_response",
+ .session = AFB_SESSION_NONE,
+ .callback = network_agent_response,
+ .info ="Agent response"
+ },
+
+ { } /* marker for end of the array */
+};
+
+/*
+ * description of the binding for afb-daemon
+ */
+const struct afb_binding_v2 afbBindingV2 = {
+ .api = "network-manager", /* the API name (or binding name or prefix) */
+ .specification = "networking API", /* short description of of the binding */
+ .verbs = network_verbs, /* the array describing the verbs of the API */
+ .init = init,
+};