aboutsummaryrefslogtreecommitdiffstats
path: root/binding/network-connman.c
diff options
context:
space:
mode:
authorPantelis Antoniou <pantelis.antoniou@konsulko.com>2018-03-08 20:39:36 +0200
committerMatt Porter <mporter@konsulko.com>2018-07-10 08:32:25 -0400
commit916843c373bc653bc472e6353631134b42d490f2 (patch)
tree35e89581b8dcdf82d93c064b42b6c7ec9eab3bb3 /binding/network-connman.c
parent8f3e1a6cd811b6b2ccecf4d192249800c521466a (diff)
initial network service binding
The AGL network service binding exposes connman apis via the AGL application framework. All network connectivity technologies are supported via the binding, limited only by the underlying connman daemon. Bug-AGL: SPEC-1540 Change-Id: Id73cfc98c7abe97cb655a4fc40d440422fa75803 Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com> [Cleanups to whitespace and naming to match other services] Signed-off-by: Matt Porter <mporter@konsulko.com>
Diffstat (limited to 'binding/network-connman.c')
-rw-r--r--binding/network-connman.c555
1 files changed, 555 insertions, 0 deletions
diff --git a/binding/network-connman.c b/binding/network-connman.c
new file mode 100644
index 0000000..a9e0b74
--- /dev/null
+++ b/binding/network-connman.c
@@ -0,0 +1,555 @@
+/*
+ * 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"
+
+static const struct property_info manager_props[] = {
+ { .name = "State", .fmt = "s", },
+ { .name = "OfflineMode", .fmt = "b", },
+ { .name = "SessionMode", .fmt = "b", },
+ { },
+};
+
+static const struct property_info technology_props[] = {
+ { .name = "Name", .fmt = "s", },
+ { .name = "Type", .fmt = "s", },
+ { .name = "Powered", .fmt = "b", },
+ { .name = "Connected", .fmt = "b", },
+ { .name = "Tethering", .fmt = "b", },
+ { .name = "TetheringIdentifier",.fmt = "s", },
+ { .name = "TetheringPassphrase",.fmt = "s", },
+ { },
+};
+
+static const struct property_info service_props[] = {
+ /* simple types */
+ { .name = "State", .fmt = "s", },
+ { .name = "Error", .fmt = "s", },
+ { .name = "Type", .fmt = "s", },
+ { .name = "Name", .fmt = "s", },
+ { .name = "Favorite", .fmt = "b", },
+ { .name = "Immutable", .fmt = "b", },
+ { .name = "AutoConnect", .fmt = "b", },
+ { .name = "Strength", .fmt = "y", },
+ { .name = "Security", .fmt = "as", },
+ { .name = "Roaming", .fmt = "b", },
+
+ /* complex types with configuration (but no subtype) */
+ {
+ .name = "Nameservers",
+ .fmt = "as",
+ .flags = PI_CONFIG,
+ }, {
+ .name = "Timeservers",
+ .fmt = "as",
+ .flags = PI_CONFIG,
+ }, {
+ .name = "Domains",
+ .fmt = "as",
+ .flags = PI_CONFIG,
+ }, {
+ .name = "mDNS",
+ .fmt = "b",
+ .flags = PI_CONFIG,
+ },
+
+ /* complex types with subtypes */
+ {
+ .name = "IPv4",
+ .fmt = "{sv}",
+ .flags = PI_CONFIG,
+ .sub = (const struct property_info []) {
+ { .name = "Method", .fmt = "s", },
+ { .name = "Address", .fmt = "s", },
+ { .name = "Netmask", .fmt = "s", },
+ { .name = "Gateway", .fmt = "s", },
+ { },
+ },
+ }, {
+ .name = "IPv6",
+ .fmt = "{sv}",
+ .flags = PI_CONFIG,
+ .sub = (const struct property_info []) {
+ { .name = "Method", .fmt = "s", },
+ { .name = "Address", .fmt = "s", },
+ { .name = "PrefixLength", .fmt = "y", },
+ { .name = "Gateway", .fmt = "s", },
+ { .name = "Privacy", .fmt = "s", },
+ { },
+ },
+ }, {
+ .name = "Proxy",
+ .fmt = "{sv}",
+ .flags = PI_CONFIG,
+ .sub = (const struct property_info []) {
+ { .name = "Method", .fmt = "s", },
+ { .name = "URL", .fmt = "s", },
+ { .name = "Servers", .fmt = "as", },
+ { .name = "Excludes", .fmt = "as", },
+ { },
+ },
+ }, {
+ .name = "Ethernet",
+ .fmt = "{sv}",
+ .sub = (const struct property_info []) {
+ { .name = "Method", .fmt = "s", },
+ { .name = "Interface", .fmt = "s", },
+ { .name = "Address", .fmt = "s", },
+ { .name = "MTU", .fmt = "q", },
+ { },
+ },
+ }, {
+ .name = "Provider",
+ .fmt = "{sv}",
+ .flags = PI_CONFIG,
+ .sub = (const struct property_info []) {
+ { .name = "Host", .fmt = "s", },
+ { .name = "Domain", .fmt = "s", },
+ { .name = "Name", .fmt = "s", },
+ { .name = "Type", .fmt = "s", },
+ { },
+ },
+ },
+ { }
+};
+
+const struct property_info *connman_get_property_info(
+ const char *access_type, GError **error)
+{
+ const struct property_info *pi = NULL;
+
+ if (!strcmp(access_type, CONNMAN_AT_MANAGER))
+ pi = manager_props;
+ else if (!strcmp(access_type, CONNMAN_AT_TECHNOLOGY))
+ pi = technology_props;
+ else if (!strcmp(access_type, CONNMAN_AT_SERVICE))
+ pi = service_props;
+ else
+ g_set_error(error, NB_ERROR, NB_ERROR_ILLEGAL_ARGUMENT,
+ "illegal %s argument", access_type);
+ return pi;
+}
+
+gboolean connman_property_dbus2json(const char *access_type,
+ json_object *jprop, const gchar *key, GVariant *var,
+ gboolean *is_config,
+ GError **error)
+{
+ const struct property_info *pi;
+ gboolean ret;
+
+ *is_config = FALSE;
+ pi = connman_get_property_info(access_type, error);
+ if (!pi)
+ return FALSE;
+
+ ret = root_property_dbus2json(jprop, pi, key, var, is_config);
+ if (!ret)
+ g_set_error(error, NB_ERROR, NB_ERROR_UNKNOWN_PROPERTY,
+ "unknown %s property %s",
+ access_type, key);
+
+ return ret;
+}
+
+
+void connman_decode_call_error(struct network_state *ns,
+ const char *access_type, const char *type_arg,
+ const char *method,
+ GError **error)
+{
+ if (!error || !*error)
+ return;
+
+ if (strstr((*error)->message,
+ "org.freedesktop.DBus.Error.UnknownObject")) {
+
+ if (!strcmp(method, "SetProperty") ||
+ !strcmp(method, "GetProperty") ||
+ !strcmp(method, "ClearProperty")) {
+
+ g_clear_error(error);
+ g_set_error(error, NB_ERROR,
+ NB_ERROR_UNKNOWN_PROPERTY,
+ "unknown %s property on %s",
+ access_type, type_arg);
+
+ } else if (!strcmp(method, "Connect") ||
+ !strcmp(method, "Disconnect") ||
+ !strcmp(method, "Remove") ||
+ !strcmp(method, "ResetCounters") ||
+ !strcmp(method, "MoveAfter") ||
+ !strcmp(method, "MoveBefore")) {
+
+ g_clear_error(error);
+ g_set_error(error, NB_ERROR,
+ NB_ERROR_UNKNOWN_SERVICE,
+ "unknown service %s",
+ type_arg);
+
+ } else if (!strcmp(method, "Scan")) {
+
+ g_clear_error(error);
+ g_set_error(error, NB_ERROR,
+ NB_ERROR_UNKNOWN_TECHNOLOGY,
+ "unknown technology %s",
+ type_arg);
+ }
+ }
+}
+
+GVariant *connman_call(struct network_state *ns,
+ const char *access_type, const char *type_arg,
+ const char *method, GVariant *params, GError **error)
+{
+ const char *path;
+ const char *interface;
+ GVariant *reply;
+
+ if (!type_arg && (!strcmp(access_type, CONNMAN_AT_TECHNOLOGY) ||
+ !strcmp(access_type, CONNMAN_AT_SERVICE))) {
+ g_set_error(error, NB_ERROR, NB_ERROR_MISSING_ARGUMENT,
+ "missing %s argument",
+ access_type);
+ return NULL;
+ }
+
+ if (!strcmp(access_type, CONNMAN_AT_MANAGER)) {
+ path = CONNMAN_MANAGER_PATH;
+ interface = CONNMAN_MANAGER_INTERFACE;
+ } else if (!strcmp(access_type, CONNMAN_AT_TECHNOLOGY)) {
+ path = CONNMAN_TECHNOLOGY_PATH(type_arg);
+ interface = CONNMAN_TECHNOLOGY_INTERFACE;
+ } else if (!strcmp(access_type, CONNMAN_AT_SERVICE)) {
+ path = CONNMAN_SERVICE_PATH(type_arg);
+ interface = CONNMAN_SERVICE_INTERFACE;
+ } else {
+ g_set_error(error, NB_ERROR, NB_ERROR_ILLEGAL_ARGUMENT,
+ "illegal %s argument",
+ access_type);
+ return NULL;
+ }
+
+ reply = g_dbus_connection_call_sync(ns->conn,
+ CONNMAN_SERVICE, path, interface, method, params,
+ NULL, G_DBUS_CALL_FLAGS_NONE, DBUS_REPLY_TIMEOUT,
+ NULL, error);
+ connman_decode_call_error(ns, access_type, type_arg, method,
+ error);
+ if (!reply) {
+ if (error && *error)
+ g_dbus_error_strip_remote_error(*error);
+ AFB_ERROR("Error calling %s%s%s %s method %s",
+ access_type,
+ type_arg ? "/" : "",
+ type_arg ? type_arg : "",
+ method,
+ error && *error ? (*error)->message :
+ "unspecified");
+ }
+
+ return reply;
+}
+
+static void connman_call_async_ready(GObject *source_object,
+ GAsyncResult *res, gpointer user_data)
+{
+ struct connman_pending_work *cpw = user_data;
+ struct network_state *ns = cpw->ns;
+ GVariant *result;
+ GError *error = NULL;
+
+ result = g_dbus_connection_call_finish(ns->conn, res, &error);
+
+ cpw->callback(cpw->user_data, result, &error);
+
+ g_clear_error(&error);
+ g_cancellable_reset(cpw->cancel);
+ g_free(cpw);
+}
+
+void connman_cancel_call(struct network_state *ns,
+ struct connman_pending_work *cpw)
+{
+ g_cancellable_cancel(cpw->cancel);
+}
+
+struct connman_pending_work *
+connman_call_async(struct network_state *ns,
+ const char *access_type, const char *type_arg,
+ const char *method, GVariant *params, GError **error,
+ void (*callback)(void *user_data, GVariant *result, GError **error),
+ void *user_data)
+{
+ const char *path;
+ const char *interface;
+ struct connman_pending_work *cpw;
+
+ if (!type_arg && (!strcmp(access_type, CONNMAN_AT_TECHNOLOGY) ||
+ !strcmp(access_type, CONNMAN_AT_SERVICE))) {
+ g_set_error(error, NB_ERROR, NB_ERROR_MISSING_ARGUMENT,
+ "missing %s argument",
+ access_type);
+ return NULL;
+ }
+
+ if (!strcmp(access_type, CONNMAN_AT_MANAGER)) {
+ path = CONNMAN_MANAGER_PATH;
+ interface = CONNMAN_MANAGER_INTERFACE;
+ } else if (!strcmp(access_type, CONNMAN_AT_TECHNOLOGY)) {
+ path = CONNMAN_TECHNOLOGY_PATH(type_arg);
+ interface = CONNMAN_TECHNOLOGY_INTERFACE;
+ } else if (!strcmp(access_type, CONNMAN_AT_SERVICE)) {
+ path = CONNMAN_SERVICE_PATH(type_arg);
+ interface = CONNMAN_SERVICE_INTERFACE;
+ } else {
+ g_set_error(error, NB_ERROR, NB_ERROR_ILLEGAL_ARGUMENT,
+ "illegal %s argument",
+ access_type);
+ return NULL;
+ }
+
+ cpw = g_malloc(sizeof(*cpw));
+ if (!cpw) {
+ g_set_error(error, NB_ERROR, NB_ERROR_OUT_OF_MEMORY,
+ "out of memory");
+ return NULL;
+ }
+ cpw->ns = ns;
+ cpw->user_data = user_data;
+ cpw->cancel = g_cancellable_new();
+ if (!cpw->cancel) {
+ g_free(cpw);
+ g_set_error(error, NB_ERROR, NB_ERROR_OUT_OF_MEMORY,
+ "out of memory");
+ return NULL;
+ }
+ cpw->callback = callback;
+
+ g_dbus_connection_call(ns->conn,
+ CONNMAN_SERVICE, path, interface, method, params,
+ NULL, /* reply type */
+ G_DBUS_CALL_FLAGS_NONE, DBUS_REPLY_TIMEOUT,
+ cpw->cancel, /* cancellable? */
+ connman_call_async_ready,
+ cpw);
+
+ return cpw;
+}
+
+json_object *connman_get_properties(struct network_state *ns,
+ const char *access_type, const char *type_arg,
+ GError **error)
+{
+ const struct property_info *pi = NULL;
+ const char *method = NULL;
+ GVariant *reply = NULL;
+ GVariantIter *array, *array2;
+ GVariant *var = NULL;
+ const gchar *path = NULL;
+ const gchar *key = NULL;
+ const gchar *basename;
+ json_object *jprop = NULL, *jresp = NULL, *jtype = NULL;
+ gboolean is_config;
+
+ pi = connman_get_property_info(access_type, error);
+ if (!pi)
+ return NULL;
+
+ method = NULL;
+ if (!strcmp(access_type, CONNMAN_AT_MANAGER))
+ method = "GetProperties";
+ else if (!strcmp(access_type, CONNMAN_AT_TECHNOLOGY))
+ method = "GetTechnologies";
+ else if (!strcmp(access_type, CONNMAN_AT_SERVICE))
+ method = "GetServices";
+
+ if (!method) {
+ g_set_error(error, NB_ERROR, NB_ERROR_ILLEGAL_ARGUMENT,
+ "illegal %s argument",
+ access_type);
+ return NULL;
+ }
+
+ reply = connman_call(ns, CONNMAN_AT_MANAGER, NULL,
+ method, NULL, error);
+ if (!reply)
+ return NULL;
+
+ if (!strcmp(access_type, CONNMAN_AT_MANAGER)) {
+ jprop = json_object_new_object();
+ g_variant_get(reply, "(a{sv})", &array);
+ while (g_variant_iter_loop(array, "{sv}", &key, &var)) {
+ root_property_dbus2json(jprop, pi,
+ key, var, &is_config);
+ }
+ g_variant_iter_free(array);
+ g_variant_unref(reply);
+ jresp = jprop;
+ } else {
+ if (!type_arg)
+ jresp = json_object_new_array();
+
+ g_variant_get(reply, "(a(oa{sv}))", &array);
+ while (g_variant_iter_loop(array, "(oa{sv})", &path, &array2)) {
+
+ /* a basename must exist and be at least 1 character wide */
+ basename = strrchr(path, '/');
+ if (!basename || strlen(basename) <= 1)
+ continue;
+ basename++;
+
+ if (!type_arg) {
+ jtype = json_object_new_object();
+ json_object_object_add(jtype, access_type,
+ json_object_new_string(basename));
+ } else if (g_strcmp0(basename, type_arg))
+ continue;
+
+ jprop = json_object_new_object();
+ while (g_variant_iter_loop(array2, "{sv}", &key, &var)) {
+ root_property_dbus2json(jprop, pi,
+ key, var, &is_config);
+ }
+
+ if (!type_arg) {
+ json_object_object_add(jtype, "properties", jprop);
+ json_object_array_add(jresp, jtype);
+ }
+ }
+ g_variant_iter_free(array);
+ g_variant_unref(reply);
+
+ if (type_arg && jprop)
+ jresp = jprop;
+
+ }
+
+ if (!jresp) {
+ if (type_arg)
+ g_set_error(error, NB_ERROR, NB_ERROR_ILLEGAL_ARGUMENT,
+ "Bad %s %s", access_type, type_arg);
+ else
+ g_set_error(error, NB_ERROR, NB_ERROR_ILLEGAL_ARGUMENT,
+ "No %s", access_type);
+ }
+
+ return jresp;
+}
+
+json_object *connman_get_property(struct network_state *ns,
+ const char *access_type, const char *type_arg,
+ gboolean is_json_name, const char *name, GError **error)
+{
+ const struct property_info *pi;
+ json_object *jprop, *jval;
+
+ pi = connman_get_property_info(access_type, error);
+ if (!pi)
+ return NULL;
+
+ jprop = connman_get_properties(ns, access_type, type_arg, error);
+ if (!jprop)
+ return NULL;
+
+ jval = get_named_property(pi, is_json_name, name, jprop);
+ json_object_put(jprop);
+
+ if (!jval)
+ g_set_error(error, NB_ERROR, NB_ERROR_BAD_PROPERTY,
+ "Bad property %s on %s%s%s", name,
+ access_type,
+ type_arg ? "/" : "",
+ type_arg ? type_arg : "");
+ return jval;
+}
+
+/* NOTE: jval is consumed */
+gboolean connman_set_property(struct network_state *ns,
+ const char *access_type, const char *type_arg,
+ gboolean is_json_name, const char *name, json_object *jval,
+ GError **error)
+{
+ const struct property_info *pi;
+ GVariant *reply, *arg;
+ gboolean is_config;
+ gchar *propname;
+
+ /* get start of properties */
+ pi = connman_get_property_info(access_type, error);
+ if (!pi)
+ return FALSE;
+
+ /* get actual property */
+ pi = property_by_name(pi, is_json_name, name, &is_config);
+ if (!pi) {
+ g_set_error(error, NB_ERROR, NB_ERROR_UNKNOWN_PROPERTY,
+ "unknown property with name %s", name);
+ json_object_put(jval);
+ return FALSE;
+ }
+
+ /* convert to gvariant */
+ arg = property_json_to_gvariant(pi, NULL, NULL, jval, error);
+
+ /* we don't need this anymore */
+ json_object_put(jval);
+ jval = NULL;
+
+ /* no variant? error */
+ if (!arg)
+ return FALSE;
+
+ if (!is_config)
+ propname = g_strdup(pi->name);
+ else
+ propname = configuration_dbus_name(pi->name);
+
+ reply = connman_call(ns, access_type, type_arg,
+ "SetProperty",
+ g_variant_new("(sv)", propname, arg),
+ error);
+
+ g_free(propname);
+
+ if (!reply)
+ return FALSE;
+
+ g_variant_unref(reply);
+
+ return TRUE;
+}