aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorScott Murray <scott.murray@konsulko.com>2022-01-28 16:14:59 -0500
committerScott Murray <scott.murray@konsulko.com>2022-01-28 16:19:28 -0500
commit5d094ec17f0f60489efe9df6c113be62f80b4c2a (patch)
tree266b3471abb9520afaca36fe0af491e8a72ec98b
parent4da886055d7cdc659858eec3a45e14f52fcfa643 (diff)
GLib based interface library for ConnMan, factored out of the agl-service-network binding code. See README.md for build and usage notes and the mapping of the new source files to those in the binding if that is for some reason required. Bug-AGL: SPEC-4182 Signed-off-by: Scott Murray <scott.murray@konsulko.com> Change-Id: I13c0982c790353c6e48f21bb8c4953a676840dbb
-rw-r--r--.gitreview5
-rw-r--r--LICENSE54
-rw-r--r--README.md51
-rw-r--r--include/connman-glib.h136
-rw-r--r--include/meson.build1
-rw-r--r--meson.build16
-rw-r--r--meson_options.txt1
-rw-r--r--src/api.c1062
-rw-r--r--src/call_work.c198
-rw-r--r--src/call_work.h80
-rw-r--r--src/common.h80
-rw-r--r--src/connman-agent.c239
-rw-r--r--src/connman-agent.h34
-rw-r--r--src/connman-call.c404
-rw-r--r--src/connman-call.h158
-rw-r--r--src/meson.build18
-rw-r--r--src/test.c125
17 files changed, 2662 insertions, 0 deletions
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 0000000..23df7a7
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,5 @@
+[gerrit]
+host=gerrit.automotivelinux.org
+port=29418
+project=src/connman-glib
+defaultbranch=master
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..31c692a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,54 @@
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+ You must give any other recipients of the Work or Derivative Works a copy of this License; and
+ You must cause any modified files to carry prominent notices stating that You changed the files; and
+ You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+ If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3e8cfc0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,51 @@
+GLib ConnMan interface library
+----------------------------
+Derived from AGL network binding source at
+[https://git.automotivelinux.org/apps/agl-service-network/]
+
+Source refactoring from the binding (not necessarily exhaustive):
+* network-api.c -> api.c, call_work.c
+* network-connman.c -> connman-call.c
+* network-common.h -> common.h, connman-call.h, call_work.h
+* network-util.c -> mostly redundant, required things moved to api.c, connman-call.c
+
+Copyright dates in files reflect their original creation dates and include
+revisions made in agl-service-network before December 2021.
+
+Building
+--------
+The build requirements are:
+* glib 2.0 headers and libraries (from e.g. glib2-devel on Fedora or CentOS,
+ libglib2.0-dev on Debian or Ubuntu).
+* meson
+
+To build:
+```
+meson build/
+ninja -C build/
+```
+
+Usage Notes
+-----------
+* Users only need include `connman-glib.h` and link to the library.
+* API calls generally return a gboolean, with `FALSE` indicating failure.
+* `connman_init` must be called before any other API calls except
+ `connman_set_log_level` or one of the callback registration functions
+ (e.g. `connman_add_manager_event_callback`).
+* A return code of `TRUE` from `connman_init` indicates D-Bus connection to
+ **ConnMan** has succeeded.
+* Callbacks may be registered after calling `connman_init`, but note that there
+ is a possibility that registration calls may briefly block if they occur
+ during processing of an associated event.
+* It is advised that only one primary user of the library enable agent support
+ to avoid conflicts.
+
+Contributing
+------------
+Questions can be sent to the agl-dev-community mailing list at
+<https://lists.automotivelinux.org/g/agl-dev-community>.
+
+Bugs can be filed on the AGL JIRA instance at <https://jira.automotivelinux.org>.
+
+Source contributions need to go through the AGL Gerrit instance, see
+<https://wiki.automotivelinux.org/agl-distro/contributing>.
diff --git a/include/connman-glib.h b/include/connman-glib.h
new file mode 100644
index 0000000..63412c0
--- /dev/null
+++ b/include/connman-glib.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2022 Konsulko Group
+ *
+ * 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.
+ */
+
+#ifndef CONNMAN_GLIB_H
+#define CONNMAN_GLIB_H
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef enum {
+ CONNMAN_LOG_LEVEL_ERROR,
+ CONNMAN_LOG_LEVEL_WARNING,
+ CONNMAN_LOG_LEVEL_INFO,
+ CONNMAN_LOG_LEVEL_DEBUG
+} connman_log_level_t;
+
+// Hook to allow users to override the default level
+#ifndef CONNMAN_LOG_LEVEL_DEFAULT
+#define CONNMAN_LOG_LEVEL_DEFAULT CONNMAN_LOG_LEVEL_ERROR
+#endif
+
+typedef enum {
+ CONNMAN_MANAGER_EVENT_TECHNOLOGY_ADD,
+ CONNMAN_MANAGER_EVENT_TECHNOLOGY_REMOVE,
+ CONNMAN_MANAGER_EVENT_SERVICE_CHANGE,
+ CONNMAN_MANAGER_EVENT_SERVICE_REMOVE,
+ CONNMAN_MANAGER_EVENT_PROPERTY_CHANGE
+} connman_manager_event_t;
+
+
+typedef void (*connman_manager_event_cb_t)(const gchar *path,
+ connman_manager_event_t event,
+ GVariant *properties,
+ gpointer user_data);
+
+typedef void (*connman_technology_property_event_cb_t)(const gchar *technology,
+ GVariant *properties,
+ gpointer user_data);
+
+typedef void (*connman_service_property_event_cb_t)(const gchar *service,
+ GVariant *property,
+ gpointer user_data);
+
+typedef void (*connman_agent_event_cb_t)(const gchar *service,
+ const int id,
+ GVariant *property,
+ gpointer user_data);
+
+typedef void (*connman_service_connect_cb_t)(const gchar *service,
+ gboolean status,
+ const char *error,
+ gpointer user_data);
+
+void connman_add_manager_event_callback(connman_manager_event_cb_t cb,
+ gpointer user_data);
+
+void connman_add_technology_property_event_callback(connman_technology_property_event_cb_t cb,
+ gpointer user_data);
+
+void connman_add_service_property_event_callback(connman_service_property_event_cb_t cb,
+ gpointer user_data);
+
+void connman_add_agent_event_callback(connman_agent_event_cb_t cb,
+ gpointer user_data);
+
+void connman_set_log_level(connman_log_level_t level);
+
+gboolean connman_init(gboolean register_agent);
+
+gboolean connman_manager_get_state(gchar **state);
+
+gboolean connman_manager_get_online(void);
+
+gboolean connman_manager_set_offline(gboolean state);
+
+gboolean connman_get_technologies(GVariant **reply);
+
+gboolean connman_get_services(GVariant **reply);
+
+gboolean connman_technology_enable(const gchar *technology);
+
+gboolean connman_technology_disable(const gchar *technology);
+
+gboolean connman_technology_scan_services(const gchar *technology);
+
+gboolean connman_service_move(const gchar *service,
+ const gchar *target_service,
+ gboolean after);
+
+gboolean connman_service_remove(const gchar *service);
+
+gboolean connman_service_connect(const gchar *service,
+ connman_service_connect_cb_t cb,
+ gpointer user_data);
+
+gboolean connman_service_disconnect(const gchar *service);
+
+typedef enum {
+ CONNMAN_PROPERTY_MANAGER,
+ CONNMAN_PROPERTY_TECHNOLOGY,
+ CONNMAN_PROPERTY_SERVICE
+} connman_property_type_t;
+
+GVariant *connman_get_property(connman_property_type_t prop_type,
+ const char *path,
+ const char *name);
+
+gboolean connman_set_property(connman_property_type_t prop_type,
+ const char *path,
+ const char *name,
+ GVariant *value);
+
+gboolean connman_agent_response(const int id, GVariant *parameters);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* CONNMAN_GLIB_H */
diff --git a/include/meson.build b/include/meson.build
new file mode 100644
index 0000000..078461d
--- /dev/null
+++ b/include/meson.build
@@ -0,0 +1 @@
+install_headers('connman-glib.h')
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..661255b
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,16 @@
+project('connman-glib', 'c', license : 'Apache-2.0')
+
+systemd_dep = dependency('systemd', version : '>=222')
+glib_deps = [dependency('glib-2.0'), dependency('gio-2.0'), dependency('gobject-2.0'), dependency('gio-unix-2.0')]
+
+inc = include_directories('include')
+
+subdir('include')
+subdir('src')
+
+pkg_mod = import('pkgconfig')
+pkg_mod.generate(libraries : lib,
+ version : '1.0',
+ name : 'libconnman-glib',
+ filebase : 'connman-glib',
+ description : 'GLib helper library for using ConnMan.')
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000..397f787
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1 @@
+option('build-tester', type : 'boolean', value : false)
diff --git a/src/api.c b/src/api.c
new file mode 100644
index 0000000..b9582e4
--- /dev/null
+++ b/src/api.c
@@ -0,0 +1,1062 @@
+/*
+ * 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.
+ */
+
+#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 "connman-glib.h"
+#include "common.h"
+#include "call_work.h"
+#include "connman-call.h"
+#include "connman-agent.h"
+
+typedef struct connman_signal_callback_list_entry_t {
+ gpointer callback;
+ gpointer user_data;
+} callback_list_entry_t;
+
+typedef struct {
+ GMutex mutex;
+ GSList *list;
+} callback_list_t;
+
+callback_list_t connman_manager_callbacks;
+callback_list_t connman_technology_callbacks;
+callback_list_t connman_service_callbacks;
+
+// The global handler thread and state
+static GThread *g_connman_thread;
+static struct connman_state *g_connman_state;
+
+// Global log level
+static connman_log_level_t g_connman_log_level = CONNMAN_LOG_LEVEL_DEFAULT;
+
+static const char *g_connman_log_level_names[CONNMAN_LOG_LEVEL_DEBUG + 1] = {
+ "ERROR",
+ "WARNING",
+ "INFO",
+ "DEBUG"
+};
+
+// Wrappers to hedge possible future abstractions
+static void connman_set_state(struct connman_state *ns)
+{
+ g_connman_state = ns;
+}
+
+static struct connman_state *connman_get_state(void)
+{
+ return g_connman_state;
+}
+
+EXPORT void connman_set_log_level(connman_log_level_t level)
+{
+ printf("%s: Setting log level to %d\n", __FUNCTION__, level);
+ g_connman_log_level = level;
+}
+
+void connman_log(connman_log_level_t level, const char *func, const char *format, ...)
+{
+ FILE *out = stdout;
+
+ if (level > g_connman_log_level)
+ return;
+
+ if (level == CONNMAN_LOG_LEVEL_ERROR)
+ out = stderr;
+
+ va_list args;
+ va_start(args, format);
+ fprintf(out, "%s: %s: ", g_connman_log_level_names[level], func);
+ gchar *format_line = g_strconcat(format, "\n", NULL);
+ vfprintf(out, format_line, args);
+ va_end(args);
+ fflush(out);
+ g_free(format_line);
+}
+
+static void callback_add(callback_list_t *callbacks, gpointer callback, gpointer user_data)
+{
+ callback_list_entry_t *entry = NULL;
+
+ if(!callbacks)
+ return;
+
+ g_mutex_lock(&callbacks->mutex);
+ entry = g_malloc0(sizeof(*entry));
+ entry->callback = callback;
+ entry->user_data = user_data;
+ callbacks->list = g_slist_append(callbacks->list, entry);
+ g_mutex_unlock(&callbacks->mutex);
+}
+
+static void callback_remove(callback_list_t *callbacks, gpointer callback)
+{
+ callback_list_entry_t *entry = NULL;
+ GSList *list;
+
+ if(!(callbacks && callbacks->list))
+ return;
+
+ g_mutex_lock(&callbacks->mutex);
+ for (list = callbacks->list; list; list = g_slist_next(list)) {
+ entry = list->data;
+ if (entry->callback == callback)
+ break;
+ entry = NULL;
+ }
+ if (entry) {
+ callbacks->list = g_slist_remove(callbacks->list, entry);
+ g_free(entry);
+ }
+ g_mutex_unlock(&callbacks->mutex);
+}
+
+static void run_manager_callbacks(callback_list_t *callbacks,
+ const gchar *path,
+ connman_manager_event_t event,
+ GVariant *properties)
+{
+ GSList *list;
+
+ if (!path)
+ return;
+
+ g_mutex_lock(&callbacks->mutex);
+ for (list = callbacks->list; list; list = g_slist_next(list)) {
+ callback_list_entry_t *entry = list->data;
+ if (entry->callback) {
+ connman_manager_event_cb_t cb = (connman_manager_event_cb_t) entry->callback;
+ (*cb)(path, event, properties, entry->user_data);
+ }
+ }
+ g_mutex_unlock(&callbacks->mutex);
+}
+
+static void run_property_callbacks(callback_list_t *callbacks,
+ const gchar *object,
+ GVariant *properties,
+ gboolean technology)
+{
+ GSList *list;
+
+ g_mutex_lock(&callbacks->mutex);
+ for (list = callbacks->list; list; list = g_slist_next(list)) {
+ callback_list_entry_t *entry = list->data;
+ if (entry->callback) {
+ if (technology) {
+ connman_technology_property_event_cb_t cb =
+ (connman_technology_property_event_cb_t) entry->callback;
+ (*cb)(object, properties, entry->user_data);
+ } else {
+ connman_service_property_event_cb_t cb =
+ (connman_service_property_event_cb_t) entry->callback;
+ (*cb)(object, properties, entry->user_data);
+ }
+ }
+ }
+ g_mutex_unlock(&callbacks->mutex);
+}
+
+EXPORT void connman_add_manager_event_callback(connman_manager_event_cb_t cb,
+ gpointer user_data)
+{
+ if (!cb)
+ return;
+
+ callback_add(&connman_manager_callbacks, cb, user_data);
+}
+
+EXPORT void connman_add_technology_property_event_callback(connman_technology_property_event_cb_t cb,
+ gpointer user_data)
+{
+ if (!cb)
+ return;
+
+ callback_add(&connman_technology_callbacks, cb, user_data);
+}
+
+EXPORT void connman_add_service_property_event_callback(connman_service_property_event_cb_t cb,
+ gpointer user_data)
+{
+ if (!cb)
+ return;
+
+ callback_add(&connman_service_callbacks, cb, user_data);
+}
+
+static void connman_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)
+{
+ GVariant *var = NULL;
+ const gchar *path = NULL;
+ const gchar *key = NULL;
+ const gchar *basename;
+
+#if CONNMAN_GLIB_DEBUG
+ INFO("sender=%s", sender_name);
+ INFO("object_path=%s", object_path);
+ INFO("interface=%s", interface_name);
+ INFO("signal=%s", signal_name);
+ DEBUG("parameters = %s", g_variant_print(parameters, TRUE));
+#endif
+
+ if (!g_strcmp0(signal_name, "TechnologyAdded")) {
+ g_variant_get(parameters, "(&o@a{sv})", &path, &var);
+ basename = connman_strip_path(path);
+ g_assert(basename); /* guaranteed by dbus */
+
+ run_manager_callbacks(&connman_manager_callbacks,
+ basename,
+ CONNMAN_MANAGER_EVENT_TECHNOLOGY_ADD,
+ var);
+ g_variant_unref(var);
+
+ } else if (!g_strcmp0(signal_name, "TechnologyRemoved")) {
+ g_variant_get(parameters, "(&o)", &path);
+ basename = connman_strip_path(path);
+ g_assert(basename); /* guaranteed by dbus */
+
+ run_manager_callbacks(&connman_manager_callbacks,
+ basename,
+ CONNMAN_MANAGER_EVENT_TECHNOLOGY_REMOVE,
+ NULL);
+
+ } else if (!g_strcmp0(signal_name, "ServicesChanged")) {
+ GVariantIter *array1, *array2;
+ GVariantIter array3;
+
+ g_variant_get(parameters, "(a(oa{sv})ao)", &array1, &array2);
+ while (g_variant_iter_loop(array1, "(&o@a{sv})", &path, &var)) {
+ if (!g_variant_iter_init(&array3, var)) {
+ continue;
+ }
+
+ basename = connman_strip_path(path);
+ g_assert(basename); /* guaranteed by dbus */
+
+ run_manager_callbacks(&connman_manager_callbacks,
+ basename,
+ CONNMAN_MANAGER_EVENT_SERVICE_CHANGE,
+ var);
+ }
+
+ while (g_variant_iter_loop(array2, "&o", &path)) {
+ basename = connman_strip_path(path);
+ g_assert(basename); /* guaranteed by dbus */
+
+ run_manager_callbacks(&connman_manager_callbacks,
+ basename,
+ CONNMAN_MANAGER_EVENT_SERVICE_REMOVE,
+ NULL);
+ }
+
+ g_variant_iter_free(array2);
+ g_variant_iter_free(array1);
+
+ } else if (!g_strcmp0(signal_name, "PropertyChanged")) {
+ g_variant_get(parameters, "(&sv)", &key, &var);
+
+ run_manager_callbacks(&connman_manager_callbacks,
+ key,
+ CONNMAN_MANAGER_EVENT_PROPERTY_CHANGE,
+ var);
+ g_variant_unref(var);
+ }
+}
+
+static void connman_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)
+{
+#if CONNMAN_GLIB_DEBUG
+ INFO("sender=%s", sender_name);
+ INFO("object_path=%s", object_path);
+ INFO("interface=%s", interface_name);
+ INFO("signal=%s", signal_name);
+ DEBUG("parameters = %s", g_variant_print(parameters, TRUE));
+#endif
+
+ // a basename must exist and be at least 1 character wide
+ const gchar *basename = connman_strip_path(object_path);
+ g_assert(basename);
+
+ if (!g_strcmp0(signal_name, "PropertyChanged")) {
+ run_property_callbacks(&connman_technology_callbacks,
+ basename,
+ parameters,
+ TRUE);
+ }
+}
+
+static void connman_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)
+{
+#if CONNMAN_GLIB_DEBUG
+ INFO("sender=%s", sender_name);
+ INFO("object_path=%s", object_path);
+ INFO("interface=%s", interface_name);
+ INFO("signal=%s", signal_name);
+ DEBUG("parameters = %s", g_variant_print(parameters, TRUE));
+#endif
+
+ // a basename must exist and be at least 1 character wide
+ const gchar *basename = connman_strip_path(object_path);
+ g_assert(basename);
+
+ if (!g_strcmp0(signal_name, "PropertyChanged")) {
+ run_property_callbacks(&connman_service_callbacks,
+ basename,
+ parameters,
+ FALSE);
+ }
+}
+
+static struct connman_state *connman_dbus_init(GMainLoop *loop)
+{
+ struct connman_state *ns;
+ GError *error = NULL;
+
+ ns = g_try_malloc0(sizeof(*ns));
+ if (!ns) {
+ ERROR("out of memory allocating network state");
+ goto err_no_ns;
+ }
+
+ 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);
+ ERROR("Cannot connect to D-Bus, %s",
+ error ? error->message : "unspecified");
+ g_error_free(error);
+ goto err_no_conn;
+
+ }
+
+ INFO("connected to dbus");
+
+ 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,
+ connman_manager_signal_callback,
+ ns,
+ NULL);
+ if (!ns->manager_sub) {
+ 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,
+ connman_technology_signal_callback,
+ ns,
+ NULL);
+ if (!ns->technology_sub) {
+ 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,
+ connman_service_signal_callback,
+ ns,
+ NULL);
+ if (!ns->service_sub) {
+ 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:
+ g_dbus_connection_close(ns->conn, NULL, NULL, NULL);
+err_no_conn:
+ g_free(ns);
+err_no_ns:
+ return NULL;
+}
+
+static void connman_cleanup(struct connman_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, gboolean 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 connman_handler_func(gpointer ptr)
+{
+ struct init_data *id = ptr;
+ struct connman_state *ns;
+ GMainLoop *loop;
+ int rc;
+
+ loop = g_main_loop_new(NULL, FALSE);
+ if (!loop) {
+ ERROR("Unable to create main loop");
+ goto err_no_loop;
+ }
+
+ // dbus interface init
+ ns = connman_dbus_init(loop);
+ if (!ns) {
+ ERROR("connman_dbus_init() failed");
+ goto err_no_ns;
+ }
+
+ id->ns = ns;
+ if (id->register_agent) {
+ rc = connman_register_agent(id);
+ if (rc) {
+ ERROR("network_register_agent() failed");
+ goto err_no_agent;
+ }
+
+ // agent registeration will signal init done
+
+ } else {
+ signal_init_done(id, TRUE);
+ }
+
+ connman_set_state(ns);
+ g_main_loop_run(loop);
+
+ g_main_loop_unref(ns->loop);
+
+ connman_unregister_agent(ns);
+
+ connman_cleanup(ns);
+ connman_set_state(NULL);
+
+ return NULL;
+
+err_no_agent:
+ connman_cleanup(ns);
+
+err_no_ns:
+ g_main_loop_unref(loop);
+
+err_no_loop:
+ signal_init_done(id, FALSE);
+
+ return NULL;
+}
+
+// API functions
+
+EXPORT gboolean connman_init(gboolean register_agent)
+{
+ struct init_data init_data, *id = &init_data;
+ gint64 end_time;
+
+ memset(id, 0, sizeof(*id));
+ id->register_agent = register_agent;
+ id->init_done = FALSE;
+ id->init_done_cb = signal_init_done;
+ //id->rc = FALSE;
+ g_cond_init(&id->cond);
+ g_mutex_init(&id->mutex);
+
+ g_connman_thread = g_thread_new("connman_handler",
+ connman_handler_func,
+ id);
+
+ INFO("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) {
+ ERROR("init timeout");
+ return FALSE;
+ }
+
+ if (!id->rc)
+ ERROR("init thread failed");
+ else
+ INFO("connman operational");
+
+ return id->rc;
+}
+
+EXPORT gboolean connman_manager_get_state(gchar **state)
+{
+ struct connman_state *ns = connman_get_state();
+ GVariant *prop = NULL;
+ GError *error = NULL;
+
+ if (!ns) {
+ ERROR("No connman connection");
+ return FALSE;
+ }
+ if (!state)
+ return FALSE;
+
+ prop = connman_get_property_internal(ns,
+ CONNMAN_AT_MANAGER,
+ NULL,
+ "State",
+ &error);
+ if (error) {
+ ERROR("property %s error %s", "State", error->message);
+ g_error_free(error);
+ return FALSE;
+ }
+
+ const gchar *val = g_variant_get_string(prop, NULL);
+ if (!val) {
+ ERROR("Invalid state property");
+ g_variant_unref(prop);
+ }
+ *state = g_strdup(val);
+ g_variant_unref(prop);
+
+ return TRUE;
+}
+
+EXPORT gboolean connman_manager_get_online(void)
+{
+ gboolean rc = FALSE;
+ gchar *state = NULL;
+
+ if(connman_manager_get_state(&state)) {
+ rc = g_strcmp0(state, "online") == 0;
+ g_free(state);
+ }
+ return rc;
+}
+
+EXPORT gboolean connman_manager_set_offline(gboolean state)
+{
+ struct connman_state *ns = connman_get_state();
+ GError *error = NULL;
+
+ GVariant *var = g_variant_new_boolean(state);
+ if (!var) {
+ ERROR("Could not create new value variant");
+ return TRUE;
+ }
+ if(!connman_set_property_internal(ns,
+ CONNMAN_AT_MANAGER,
+ NULL,
+ "OfflineMode",
+ var,
+ &error)) {
+ ERROR("Setting offline mode to %s failed - %s",
+ state ? "true" : "false", error->message);
+ g_error_free(error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+EXPORT gboolean connman_get_technologies(GVariant **reply)
+{
+ struct connman_state *ns = connman_get_state();
+ GVariant *properties = NULL;
+ GError *error = NULL;
+
+ if (!ns) {
+ ERROR("No connman connection");
+ return FALSE;
+ }
+ if (!reply)
+ return FALSE;
+
+ properties = connman_get_properties(ns, CONNMAN_AT_TECHNOLOGY, NULL, &error);
+ if (error) {
+ ERROR("technology properties error %s", error->message);
+ g_error_free(error);
+ return FALSE;
+ }
+
+ *reply = properties;
+
+ return TRUE;
+}
+
+EXPORT gboolean connman_get_services(GVariant **reply)
+{
+ struct connman_state *ns = connman_get_state();
+ GVariant *properties = NULL;
+ GError *error = NULL;
+
+ if (!ns) {
+ ERROR("No connman connection");
+ return FALSE;
+ }
+ if (!reply)
+ return FALSE;
+
+ properties = connman_get_properties(ns, CONNMAN_AT_SERVICE, NULL, &error);
+ if (error) {
+ ERROR("service properties error %s", error->message);
+ g_error_free(error);
+ return FALSE;
+ }
+
+ *reply = properties;
+
+ return TRUE;
+}
+
+// helper
+static gboolean connman_technology_set_powered(const gchar *technology, gboolean powered)
+{
+ struct connman_state *ns = connman_get_state();
+ GError *error = NULL;
+
+ GVariant *var = connman_get_property_internal(ns,
+ CONNMAN_AT_TECHNOLOGY,
+ technology,
+ "Powered",
+ &error);
+ if (!var) {
+ ERROR("Failed to get current Powered state - %s",
+ error->message);
+ g_error_free(error);
+ return FALSE;
+ }
+ gboolean current_powered = g_variant_get_boolean(var);
+ g_variant_unref(var);
+ var = NULL;
+
+ if (current_powered == powered) {
+ INFO("Technology %s already %s",
+ technology, powered ? "enabled" : "disabled");
+ return TRUE;
+ }
+
+
+ var = g_variant_new_boolean(powered);
+ if (!var) {
+ ERROR("Could not create new value variant");
+ return TRUE;
+ }
+ if(!connman_set_property_internal(ns,
+ CONNMAN_AT_TECHNOLOGY,
+ technology,
+ "Powered",
+ var,
+ &error)) {
+ ERROR("Failed to set Powered state - %s",
+ error->message);
+ g_error_free(error);
+ return FALSE;
+ }
+
+ INFO("Technology %s %s",
+ technology, powered ? "enabled" : "disabled");
+
+ return TRUE;
+}
+
+EXPORT gboolean connman_technology_enable(const gchar *technology)
+{
+ return connman_technology_set_powered(technology, TRUE);
+}
+
+EXPORT gboolean connman_technology_disable(const gchar *technology)
+{
+ return connman_technology_set_powered(technology, FALSE);
+}
+
+EXPORT gboolean connman_technology_scan_services(const gchar *technology)
+{
+ struct connman_state *ns = connman_get_state();
+ GVariant *reply = NULL;
+ GError *error = NULL;
+
+ if (!technology) {
+ ERROR("No technology given");
+ return FALSE;
+ }
+
+ reply = connman_call(ns, CONNMAN_AT_TECHNOLOGY, technology,
+ "Scan", NULL, &error);
+ if (!reply) {
+ ERROR("technology %s method %s error %s",
+ technology, "Scan", error->message);
+ g_error_free(error);
+ return FALSE;
+ }
+ g_variant_unref(reply);
+
+ return TRUE;
+}
+
+EXPORT gboolean connman_service_move(const gchar *service,
+ const gchar *target_service,
+ gboolean after)
+{
+ struct connman_state *ns = connman_get_state();
+ GVariant *reply = NULL;
+ GError *error = NULL;
+
+ if (!target_service) {
+ ERROR("No other service given for move");
+ return FALSE;
+ }
+
+ reply = connman_call(ns, CONNMAN_AT_SERVICE, service,
+ after ? "MoveAfter" : "MoveBefore",
+ g_variant_new("o", CONNMAN_SERVICE_PATH(target_service)),
+ &error);
+ if (!reply) {
+ ERROR("%s error %s",
+ after ? "MoveAfter" : "MoveBefore",
+ error ? error->message : "unspecified");
+ g_error_free(error);
+ return FALSE;
+ }
+ g_variant_unref(reply);
+
+ return TRUE;
+}
+
+EXPORT gboolean connman_service_remove(const gchar *service)
+{
+ struct connman_state *ns = connman_get_state();
+ GVariant *reply = NULL;
+ GError *error = NULL;
+
+ if (!service) {
+ ERROR("No service");
+ return FALSE;
+ }
+
+ reply = connman_call(ns, CONNMAN_AT_SERVICE, service,
+ "Remove", NULL, &error);
+ if (!reply) {
+ ERROR("Remove error %s",
+ error ? error->message : "unspecified");
+ g_error_free(error);
+ return FALSE;
+ }
+ g_variant_unref(reply);
+
+ return TRUE;
+}
+
+static void connect_service_callback(void *user_data,
+ GVariant *result,
+ GError **error)
+{
+ struct call_work *cw = user_data;
+ struct connman_state *ns = cw->ns;
+ GError *sub_error = NULL;
+ gboolean status = TRUE;
+ gchar *error_string = NULL;
+
+ connman_decode_call_error(ns,
+ cw->access_type, cw->type_arg, cw->connman_method,
+ error);
+ if (error && *error) {
+ status = FALSE;
+
+ /* Read the Error property (if available to be specific) */
+ GVariant *err = connman_get_property_internal(ns,
+ CONNMAN_AT_SERVICE,
+ cw->type_arg,
+ "Error",
+ &sub_error);
+ g_clear_error(&sub_error);
+ if (err) {
+ /* clear property error */
+ connman_call(ns,
+ CONNMAN_AT_SERVICE,
+ cw->type_arg,
+ "ClearProperty",
+ NULL,
+ &sub_error);
+ g_clear_error(&sub_error);
+
+ error_string = g_strdup(g_variant_get_string(err, NULL));
+ ERROR("Connect error: %s", error_string);
+ g_variant_unref(err);
+ } else {
+ error_string = g_strdup((*error)->message);
+ ERROR("Connect error: %s", error_string);
+ }
+ }
+
+ if (result)
+ g_variant_unref(result);
+
+ // Run callback
+ if (cw->request_cb) {
+ connman_service_connect_cb_t cb = (connman_service_connect_cb_t) cw->request_cb;
+ gchar *service = g_strdup(cw->type_arg);
+ (*cb)(service, status, error_string, cw->request_user_data);
+ if (error_string)
+ g_free(error_string);
+ }
+
+ DEBUG("Service %s %s", cw->type_arg, status ? "connected" : "error");
+
+ call_work_destroy(cw);
+}
+
+EXPORT gboolean connman_service_connect(const gchar *service,
+ connman_service_connect_cb_t cb,
+ gpointer user_data)
+{
+ struct connman_state *ns = connman_get_state();
+ GError *error = NULL;
+ struct call_work *cw;
+
+ if (!service) {
+ ERROR("No service given");
+ return FALSE;
+ }
+
+ cw = call_work_create(ns, "service", service,
+ "connect_service", "Connect", &error);
+ if (!cw) {
+ ERROR("can't queue work %s", error->message);
+ g_error_free(error);
+ return FALSE;
+ }
+
+ // Set callback hook
+ cw->request_cb = cb;
+ cw->request_user_data = user_data;
+
+ cw->cpw = connman_call_async(ns, "service", service,
+ "Connect", NULL, &error,
+ connect_service_callback, cw);
+ if (!cw->cpw) {
+ ERROR("connection error %s", error->message);
+ call_work_destroy(cw);
+ g_error_free(error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+EXPORT gboolean connman_service_disconnect(const gchar *service)
+{
+ struct connman_state *ns = connman_get_state();
+ GVariant *reply = NULL;
+ GError *error = NULL;
+
+ if (!service) {
+ ERROR("No service given to move");
+ return FALSE;
+ }
+
+ reply = connman_call(ns, CONNMAN_AT_SERVICE, service,
+ "Disconnect", NULL, &error);
+ if (!reply) {
+ ERROR("Disconnect error %s",
+ error ? error->message : "unspecified");
+ g_error_free(error);
+ return FALSE;
+ }
+
+ g_variant_unref(reply);
+
+ return TRUE;
+}
+
+EXPORT GVariant *connman_get_property(connman_property_type_t prop_type,
+ const char *path,
+ const char *name)
+{
+ struct connman_state *ns = connman_get_state();
+ const char *access_type;
+ const char *type_arg;
+ GError *error = NULL;
+
+ if (!name)
+ return FALSE;
+
+ type_arg = path;
+ switch (prop_type) {
+ case CONNMAN_PROPERTY_MANAGER:
+ access_type = CONNMAN_AT_MANAGER;
+ type_arg = NULL;
+ break;
+ case CONNMAN_PROPERTY_TECHNOLOGY:
+ access_type = CONNMAN_AT_TECHNOLOGY;
+ break;
+ case CONNMAN_PROPERTY_SERVICE:
+ access_type = CONNMAN_AT_SERVICE;
+ break;
+ default:
+ return NULL;
+ break;
+ }
+
+ GVariant *val = connman_get_property_internal(ns,
+ access_type,
+ type_arg,
+ name,
+ &error);
+ if (!val) {
+ ERROR("%s property error %s",
+ access_type, error->message);
+ g_error_free(error);
+ }
+ return val;
+}
+
+EXPORT gboolean connman_set_property(connman_property_type_t prop_type,
+ const char *path,
+ const char *name,
+ GVariant *value)
+{
+ struct connman_state *ns = connman_get_state();
+ const char *access_type;
+ const char *type_arg;
+ GError *error = NULL;
+ gboolean ret;
+
+ if (!(name && value))
+ return FALSE;
+
+ type_arg = path;
+ switch (prop_type) {
+ case CONNMAN_PROPERTY_MANAGER:
+ access_type = CONNMAN_AT_MANAGER;
+ type_arg = NULL;
+ break;
+ case CONNMAN_PROPERTY_TECHNOLOGY:
+ access_type = CONNMAN_AT_TECHNOLOGY;
+ break;
+ case CONNMAN_PROPERTY_SERVICE:
+ access_type = CONNMAN_AT_SERVICE;
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+
+ ret = connman_set_property_internal(ns,
+ access_type,
+ type_arg,
+ name,
+ value,
+ &error);
+ if (!ret) {
+ ERROR("Set property %s failed - %s", name, error->message);
+ g_error_free(error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+EXPORT gboolean connman_agent_response(const int id, GVariant *parameters)
+{
+ struct connman_state *ns = connman_get_state();
+ struct call_work *cw;
+
+ call_work_lock(ns);
+ cw = call_work_lookup_by_id_unlocked(ns, id);
+ if (!cw || !cw->invocation) {
+ call_work_unlock(ns);
+ ERROR("Cannot find request with id %d", id);
+ return FALSE;
+ }
+
+ if (g_strcmp0(cw->agent_method, "RequestInput") != 0) {
+ ERROR("Unhandled agent method %s", cw->agent_method);
+ g_dbus_method_invocation_return_dbus_error(cw->invocation,
+ "org.freedesktop.DBus.Error.UnknownMethod",
+ "Unknown method");
+ cw->invocation = NULL;
+ call_work_unlock(ns);
+ return FALSE;
+ }
+
+ g_dbus_method_invocation_return_value(cw->invocation, parameters);
+ cw->invocation = NULL;
+
+ call_work_unlock(ns);
+
+ INFO("Agent response sent");
+ return TRUE;
+}
diff --git a/src/call_work.c b/src/call_work.c
new file mode 100644
index 0000000..47cfdcd
--- /dev/null
+++ b/src/call_work.c
@@ -0,0 +1,198 @@
+/*
+ * 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 <glib-object.h>
+
+#include "common.h"
+#include "call_work.h"
+#include "connman-call.h"
+
+
+void call_work_lock(struct connman_state *ns)
+{
+ g_mutex_lock(&ns->cw_mutex);
+}
+
+void call_work_unlock(struct connman_state *ns)
+{
+ g_mutex_unlock(&ns->cw_mutex);
+}
+
+struct call_work *call_work_lookup_unlocked(struct connman_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 connman_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 connman_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 connman_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 connman_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 connman_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, CONNMAN_ERROR, CONNMAN_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 connman_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 connman_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) {
+ 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 connman_state *ns = cw->ns;
+
+ g_mutex_lock(&ns->cw_mutex);
+ call_work_destroy_unlocked(cw);
+ g_mutex_unlock(&ns->cw_mutex);
+}
diff --git a/src/call_work.h b/src/call_work.h
new file mode 100644
index 0000000..904fa7b
--- /dev/null
+++ b/src/call_work.h
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#ifndef CONNMAN_CALL_WORK_H
+#define CONNMAN_CALL_WORK_H
+
+#include <glib.h>
+
+struct call_work {
+ struct connman_state *ns;
+ int id;
+ gchar *access_type;
+ gchar *type_arg;
+ gchar *method;
+ gchar *connman_method;
+ struct connman_pending_work *cpw;
+ gpointer request_cb;
+ gpointer request_user_data;
+ gchar *agent_method;
+ GDBusMethodInvocation *invocation;
+};
+
+void call_work_lock(struct connman_state *ns);
+
+void call_work_unlock(struct connman_state *ns);
+
+struct call_work *call_work_lookup_unlocked(struct connman_state *ns,
+ const char *access_type,
+ const char *type_arg,
+ const char *method);
+
+struct call_work *call_work_lookup(struct connman_state *ns,
+ const char *access_type,
+ const char *type_arg,
+ const char *method);
+
+
+int call_work_pending_id(struct connman_state *ns,
+ const char *access_type,
+ const char *type_arg,
+ const char *method);
+
+
+struct call_work *call_work_lookup_by_id_unlocked(struct connman_state *ns, int id);
+
+struct call_work *call_work_lookup_by_id(struct connman_state *ns, int id);
+
+struct call_work *call_work_create_unlocked(struct connman_state *ns,
+ const char *access_type,
+ const char *type_arg,
+ const char *method,
+ const char *connman_method,
+ GError **error);
+
+struct call_work *call_work_create(struct connman_state *ns,
+ const char *access_type,
+ const char *type_arg,
+ const char *method,
+ const char *connman_method,
+ GError **error);
+
+void call_work_destroy_unlocked(struct call_work *cw);
+
+void call_work_destroy(struct call_work *cw);
+
+#endif // CONNMAN_CALL_WORK_H
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 0000000..d05d022
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#ifndef CONNMAN_COMMON_H
+#define CONNMAN_COMMON_H
+
+#include <glib.h>
+#include <stdlib.h>
+#include <gio/gio.h>
+#include <glib-object.h>
+
+#include "connman-glib.h"
+
+// Marker for exposed API functions
+#define EXPORT __attribute__ ((visibility("default")))
+
+struct call_work;
+
+struct connman_state {
+ GMainLoop *loop;
+ GDBusConnection *conn;
+ guint manager_sub;
+ guint technology_sub;
+ guint service_sub;
+
+ /* NOTE: single connection allowed for now */
+ /* NOTE: needs locking and a list */
+ GMutex cw_mutex;
+ int next_cw_id;
+ GSList *cw_pending;
+ struct call_work *cw;
+
+ /* agent */
+ GDBusNodeInfo *introspection_data;
+ guint agent_id;
+ guint registration_id;
+ gchar *agent_path;
+ gboolean agent_registered;
+};
+
+struct init_data {
+ GCond cond;
+ GMutex mutex;
+ gboolean register_agent;
+ gboolean init_done;
+ struct connman_state *ns; /* before setting afb_api_set_userdata() */
+ gboolean rc;
+ void (*init_done_cb)(struct init_data *id, gboolean rc);
+};
+
+extern void connman_log(connman_log_level_t level, const char *func, const char *format, ...)
+ __attribute__ ((format (printf, 3, 4)));
+
+#define ERROR(format, ...) \
+ connman_log(CONNMAN_LOG_LEVEL_ERROR, __FUNCTION__, format, ##__VA_ARGS__)
+
+#define WARNING(format, ...) \
+ connman_log(CONNMAN_LOG_LEVEL_WARNING, __FUNCTION__, format, ##__VA_ARGS__)
+
+#define INFO(format, ...) \
+ connman_log(CONNMAN_LOG_LEVEL_INFO, __FUNCTION__, format, ##__VA_ARGS__)
+
+#define DEBUG(format, ...) \
+ connman_log(CONNMAN_LOG_LEVEL_DEBUG, __FUNCTION__, format, ##__VA_ARGS__)
+
+#endif // CONNMAN_COMMON_H
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);
+}
diff --git a/src/connman-agent.h b/src/connman-agent.h
new file mode 100644
index 0000000..c2acc66
--- /dev/null
+++ b/src/connman-agent.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2022 Konsulko Group
+ *
+ * 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.
+ */
+
+#ifndef CONNMAN_AGENT_H
+#define CONNMAN_AGENT_H
+
+#define _GNU_SOURCE
+#include <glib.h>
+/*
+#include <stdlib.h>
+#include <gio/gio.h>
+#include <glib-object.h>
+*/
+
+#include "common.h"
+
+int connman_register_agent(struct init_data *id);
+
+void connman_unregister_agent(struct connman_state *ns);
+
+#endif /* CONNMAN_AGENT_H */
diff --git a/src/connman-call.c b/src/connman-call.c
new file mode 100644
index 0000000..0479646
--- /dev/null
+++ b/src/connman-call.c
@@ -0,0 +1,404 @@
+/*
+ * 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 "connman-call.h"
+#include "common.h"
+
+G_DEFINE_QUARK(connman-error-quark, connman_error)
+
+
+void connman_decode_call_error(struct connman_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, CONNMAN_ERROR,
+ CONNMAN_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, CONNMAN_ERROR,
+ CONNMAN_ERROR_UNKNOWN_SERVICE,
+ "unknown service %s",
+ type_arg);
+
+ } else if (!strcmp(method, "Scan")) {
+
+ g_clear_error(error);
+ g_set_error(error, CONNMAN_ERROR,
+ CONNMAN_ERROR_UNKNOWN_TECHNOLOGY,
+ "unknown technology %s",
+ type_arg);
+ }
+ }
+}
+
+GVariant *connman_call(struct connman_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, CONNMAN_ERROR, CONNMAN_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, CONNMAN_ERROR, CONNMAN_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);
+ 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 connman_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 connman_state *ns,
+ struct connman_pending_work *cpw)
+{
+ g_cancellable_cancel(cpw->cancel);
+}
+
+struct connman_pending_work *
+connman_call_async(struct connman_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, CONNMAN_ERROR, CONNMAN_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, CONNMAN_ERROR, CONNMAN_ERROR_ILLEGAL_ARGUMENT,
+ "illegal %s argument",
+ access_type);
+ return NULL;
+ }
+
+ cpw = g_malloc(sizeof(*cpw));
+ if (!cpw) {
+ g_set_error(error, CONNMAN_ERROR, CONNMAN_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, CONNMAN_ERROR, CONNMAN_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;
+}
+
+GVariant *connman_get_properties(struct connman_state *ns,
+ const char *access_type,
+ const char *type_arg,
+ GError **error)
+{
+ const char *method = NULL;
+ GVariant *reply = 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, CONNMAN_ERROR, CONNMAN_ERROR_ILLEGAL_ARGUMENT,
+ "illegal %s argument",
+ access_type);
+ return NULL;
+ }
+
+ reply = connman_call(ns, CONNMAN_AT_MANAGER, type_arg, method, NULL, error);
+ if (!reply) {
+ if (type_arg)
+ g_set_error(error, CONNMAN_ERROR, CONNMAN_ERROR_ILLEGAL_ARGUMENT,
+ "Bad %s %s", access_type, type_arg);
+ else
+ g_set_error(error, CONNMAN_ERROR, CONNMAN_ERROR_ILLEGAL_ARGUMENT,
+ "No %s", access_type);
+ }
+
+ DEBUG("properties: %s", g_variant_print(reply, TRUE));
+
+ return reply;
+}
+
+static GVariant *find_manager_property(GVariant *properties,
+ const char *access_type,
+ const char *type_arg,
+ const char *name,
+ GError **error)
+{
+ GVariantIter *array = NULL;
+ g_variant_get(properties, "(a{sv})", &array);
+ if (!array) {
+ g_set_error(error, CONNMAN_ERROR, CONNMAN_ERROR_BAD_PROPERTY,
+ "Unexpected reply querying property '%s' on %s%s%s",
+ name,
+ access_type,
+ type_arg ? "/" : "",
+ type_arg ? type_arg : "");
+ return NULL;
+ }
+
+ // Look for property by name
+ gchar *key = NULL;
+ GVariant *val = NULL;
+ while (g_variant_iter_loop(array, "{&sv}", &key, &val)) {
+ if (!g_strcmp0(name, key))
+ break;
+ }
+ g_variant_iter_free(array);
+ return val;
+}
+
+static GVariant *find_property(GVariant *properties,
+ const char *access_type,
+ const char *type_arg,
+ const char *name,
+ GError **error)
+{
+ gchar *target_path = NULL;
+ if (!g_strcmp0(access_type, CONNMAN_AT_TECHNOLOGY)) {
+ target_path = CONNMAN_TECHNOLOGY_PATH(type_arg);
+ } else if (!g_strcmp0(access_type, CONNMAN_AT_SERVICE)) {
+ target_path = CONNMAN_SERVICE_PATH(type_arg);
+ } else {
+ ERROR("Bad access_type");
+ return NULL;
+ }
+
+ GVariantIter *array = NULL;
+ g_variant_get(properties, "(a(oa{sv}))", &array);
+ if (!array) {
+ g_set_error(error, CONNMAN_ERROR, CONNMAN_ERROR_BAD_PROPERTY,
+ "Unexpected reply querying property '%s' on %s%s%s",
+ name,
+ access_type,
+ type_arg ? "/" : "",
+ type_arg ? type_arg : "");
+ return NULL;
+ }
+
+ // Look for property by name
+ gchar *path = NULL;
+ GVariantIter *array2 = NULL;
+ gchar *key = NULL;
+ GVariant *val = NULL;
+ gboolean found = FALSE;
+ while (g_variant_iter_loop(array, "(&oa{sv})", &path, &array2)) {
+ if (g_strcmp0(path, target_path) != 0) {
+ continue;
+ }
+
+ // Look for property in technology
+ while (g_variant_iter_loop(array2, "{&sv}", &key, &val)) {
+ if (!g_strcmp0(name, key)) {
+ found = TRUE;
+ break;
+ }
+ }
+ break;
+ }
+ if (found)
+ g_variant_iter_free(array2);
+ g_variant_iter_free(array);
+ return val;
+}
+
+GVariant *connman_get_property_internal(struct connman_state *ns,
+ const char *access_type,
+ const char *type_arg,
+ const char *name,
+ GError **error)
+{
+ GError *get_error = NULL;
+ GVariant *reply = connman_get_properties(ns, access_type, type_arg, &get_error);
+ if (get_error || !reply) {
+ if (!get_error)
+ g_set_error(error, CONNMAN_ERROR, CONNMAN_ERROR_BAD_PROPERTY,
+ "Unexpected error querying properties %s%s%s",
+ access_type,
+ type_arg ? "/" : "",
+ type_arg ? type_arg : "");
+ else if (error)
+ *error = get_error;
+ else
+ g_error_free(get_error);
+ return NULL;
+ }
+
+ GVariant *val = NULL;
+ if (!g_strcmp0(access_type, CONNMAN_AT_MANAGER)) {
+ val = find_manager_property(reply, access_type, type_arg, name, error);
+ } else {
+ val = find_property(reply, access_type, type_arg, name, error);
+ }
+
+ g_variant_unref(reply);
+
+ if (!val)
+ g_set_error(error, CONNMAN_ERROR, CONNMAN_ERROR_BAD_PROPERTY,
+ "Bad property '%s' on %s%s%s",
+ name,
+ access_type,
+ type_arg ? "/" : "",
+ type_arg ? type_arg : "");
+ return val;
+}
+
+gboolean connman_set_property_internal(struct connman_state *ns,
+ const char *access_type,
+ const char *type_arg,
+ const char *name,
+ GVariant *value,
+ GError **error)
+{
+ if (!(ns && access_type && type_arg && name && value))
+ return FALSE;
+
+ GVariant *var = g_variant_new("(sv)", name, value);
+ if (!var)
+ return FALSE;
+
+ GVariant *reply = connman_call(ns,
+ access_type,
+ type_arg,
+ "SetProperty",
+ var,
+ error);
+ if (!reply)
+ return FALSE;
+
+ g_variant_unref(reply);
+
+ return TRUE;
+}
diff --git a/src/connman-call.h b/src/connman-call.h
new file mode 100644
index 0000000..e11ae09
--- /dev/null
+++ b/src/connman-call.h
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+#ifndef CONNMAN_CALL_H
+#define CONNMAN_CALL_H
+
+#include <stdio.h>
+#include <glib.h>
+#include <gio/gio.h>
+
+typedef enum {
+ CONNMAN_ERROR_BAD_TECHNOLOGY,
+ CONNMAN_ERROR_BAD_SERVICE,
+ CONNMAN_ERROR_OUT_OF_MEMORY,
+ CONNMAN_ERROR_NO_TECHNOLOGIES,
+ CONNMAN_ERROR_NO_SERVICES,
+ CONNMAN_ERROR_BAD_PROPERTY,
+ CONNMAN_ERROR_UNIMPLEMENTED,
+ CONNMAN_ERROR_UNKNOWN_PROPERTY,
+ CONNMAN_ERROR_UNKNOWN_TECHNOLOGY,
+ CONNMAN_ERROR_UNKNOWN_SERVICE,
+ CONNMAN_ERROR_MISSING_ARGUMENT,
+ CONNMAN_ERROR_ILLEGAL_ARGUMENT,
+ CONNMAN_ERROR_CALL_IN_PROGRESS,
+} NBError;
+
+#define CONNMAN_ERROR (connman_error_quark())
+
+extern GQuark connman_error_quark(void);
+
+#define CONNMAN_SERVICE "net.connman"
+#define CONNMAN_MANAGER_INTERFACE CONNMAN_SERVICE ".Manager"
+#define CONNMAN_TECHNOLOGY_INTERFACE CONNMAN_SERVICE ".Technology"
+#define CONNMAN_SERVICE_INTERFACE CONNMAN_SERVICE ".Service"
+#define CONNMAN_PROFILE_INTERFACE CONNMAN_SERVICE ".Profile"
+#define CONNMAN_COUNTER_INTERFACE CONNMAN_SERVICE ".Counter"
+#define CONNMAN_ERROR_INTERFACE CONNMAN_SERVICE ".Error"
+#define CONNMAN_AGENT_INTERFACE CONNMAN_SERVICE ".Agent"
+
+#define CONNMAN_MANAGER_PATH "/"
+#define CONNMAN_PATH "/net/connman"
+#define CONNMAN_TECHNOLOGY_PREFIX CONNMAN_PATH "/technology"
+#define CONNMAN_SERVICE_PREFIX CONNMAN_PATH "/service"
+
+#define CONNMAN_TECHNOLOGY_PATH(_t) \
+ ({ \
+ const char *__t = (_t); \
+ size_t __len = strlen(CONNMAN_TECHNOLOGY_PREFIX) + 1 + \
+ strlen(__t) + 1; \
+ char *__tpath; \
+ __tpath = alloca(__len + 1 + 1); \
+ snprintf(__tpath, __len + 1, \
+ CONNMAN_TECHNOLOGY_PREFIX "/%s", __t); \
+ __tpath; \
+ })
+
+#define CONNMAN_SERVICE_PATH(_s) \
+ ({ \
+ const char *__s = (_s); \
+ size_t __len = strlen(CONNMAN_SERVICE_PREFIX) + 1 + \
+ strlen(__s) + 1; \
+ char *__spath; \
+ __spath = alloca(__len + 1 + 1); \
+ snprintf(__spath, __len + 1, \
+ CONNMAN_SERVICE_PREFIX "/%s", __s); \
+ __spath; \
+ })
+
+#define AGENT_PATH "/net/connman/Agent"
+#define AGENT_SERVICE "org.agent"
+
+#define DBUS_REPLY_TIMEOUT (120 * 1000)
+#define DBUS_REPLY_TIMEOUT_SHORT (10 * 1000)
+
+#define CONNMAN_AT_MANAGER "manager"
+#define CONNMAN_AT_TECHNOLOGY "technology"
+#define CONNMAN_AT_SERVICE "service"
+
+struct connman_state;
+
+static inline const char *connman_strip_path(const char *path)
+{
+ const char *basename;
+
+ basename = strrchr(path, '/');
+ if (!basename)
+ return NULL;
+ basename++;
+ /* at least one character */
+ return *basename ? basename : NULL;
+}
+
+GVariant *connman_call(struct connman_state *ns,
+ const char *access_type,
+ const char *type_arg,
+ const char *method,
+ GVariant *params,
+ GError **error);
+
+GVariant *connman_get_properties(struct connman_state *ns,
+ const char *access_type,
+ const char *type_arg,
+ GError **error);
+
+GVariant *connman_get_property_internal(struct connman_state *ns,
+ const char *access_type,
+ const char *type_arg,
+ const char *name,
+ GError **error);
+
+gboolean connman_set_property_internal(struct connman_state *ns,
+ const char *access_type,
+ const char *type_arg,
+ const char *name,
+ GVariant *value,
+ GError **error);
+
+struct connman_pending_work {
+ struct connman_state *ns;
+ void *user_data;
+ GCancellable *cancel;
+ void (*callback)(void *user_data, GVariant *result, GError **error);
+};
+
+void connman_cancel_call(struct connman_state *ns,
+ struct connman_pending_work *cpw);
+
+struct connman_pending_work *
+connman_call_async(struct connman_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);
+
+void connman_decode_call_error(struct connman_state *ns,
+ const char *access_type,
+ const char *type_arg,
+ const char *method,
+ GError **error);
+
+#endif /* CONNMAN_CALL_H */
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 0000000..8542d12
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,18 @@
+add_project_arguments('-fvisibility=hidden', language : 'c')
+
+src = ['api.c', 'connman-agent.c', 'connman-call.c', 'call_work.c']
+lib = shared_library('connman-glib',
+ sources: src,
+ version: '1.0.0',
+ soversion: '0',
+ include_directories: inc,
+ dependencies: [systemd_dep, glib_deps],
+ install: true)
+
+if get_option('build-tester')
+ lib_dep = declare_dependency(link_with: lib)
+ executable('connman-glib-test',
+ 'test.c',
+ include_directories: inc,
+ dependencies: [systemd_dep, glib_deps, lib_dep])
+endif
diff --git a/src/test.c b/src/test.c
new file mode 100644
index 0000000..0f82cc4
--- /dev/null
+++ b/src/test.c
@@ -0,0 +1,125 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "connman-glib.h"
+
+void manager_event_cb(const gchar *path,
+ connman_manager_event_t event,
+ GVariant *properties,
+ gpointer user_data)
+{
+ printf("%s - enter\n", __FUNCTION__);
+ switch(event) {
+ case CONNMAN_MANAGER_EVENT_TECHNOLOGY_ADD:
+ printf("technology %s add\n", path);
+ break;
+ case CONNMAN_MANAGER_EVENT_TECHNOLOGY_REMOVE:
+ printf("technology %s remove\n", path);
+ break;
+ case CONNMAN_MANAGER_EVENT_SERVICE_CHANGE:
+ printf("service %s change: ...\n", path);
+ break;
+ case CONNMAN_MANAGER_EVENT_SERVICE_REMOVE:
+ printf("service %s remove: ...\n", path);
+ break;
+ case CONNMAN_MANAGER_EVENT_PROPERTY_CHANGE:
+ printf("property %s change: %s\n",
+ path,
+ properties ? g_variant_print(properties, TRUE) : "(null)");
+ break;
+ default:
+ break;
+ }
+ printf("%s - exit\n\n", __FUNCTION__);
+}
+
+void technology_property_event_cb(const gchar *technology,
+ GVariant *properties,
+ gpointer user_data)
+{
+ printf("%s - enter\n", __FUNCTION__);
+ printf("technology %s properties: %s\n",
+ technology,
+ properties ? g_variant_print(properties, TRUE) : "(null)");
+ printf("%s - exit\n\n", __FUNCTION__);
+}
+
+void service_property_event_cb(const gchar *service,
+ GVariant *properties,
+ gpointer user_data)
+{
+ printf("%s - enter\n", __FUNCTION__);
+ printf("service %s properties: %s\n",
+ service,
+ properties ? g_variant_print(properties, TRUE) : "(null)");
+ printf("%s - exit\n\n", __FUNCTION__);
+}
+
+
+int main(int argc, char *argv[])
+{
+ gboolean rc;
+
+ connman_add_manager_event_callback(manager_event_cb, NULL);
+ connman_add_technology_property_event_callback(technology_property_event_cb, NULL);
+ connman_add_service_property_event_callback(service_property_event_cb, NULL);
+
+ // FIXME: should pass callback here and wait for it to report success
+ rc = connman_init(TRUE);
+ printf("connman_init rc = %d\n", rc);
+
+ GVariant *reply = NULL;
+ rc = connman_get_technologies(&reply);
+ if(rc) {
+ //printf("technologies: %s\n\n", reply ? g_variant_print(reply, TRUE) : "(null)");
+
+ GVariantIter *array = NULL;
+ g_variant_get(reply, "(a(oa{sv}))", &array);
+ const gchar *path = NULL;
+ GVariant *var = NULL;
+ printf("technologies:\n");
+ while (g_variant_iter_next(array, "(o@a{sv})", &path, &var)) {
+ printf("%s: %s\n", path, g_variant_print(var, TRUE));
+ g_variant_unref(var);
+ }
+ g_variant_iter_free(array);
+ g_variant_unref(reply);
+ }
+
+ reply = NULL;
+ rc = connman_get_services(&reply);
+ if(rc) {
+ printf("services: %s\n", reply ? g_variant_print(reply, TRUE) : "(null)");
+ g_variant_unref(reply);
+ }
+
+ gchar *state = NULL;
+ if(connman_manager_get_state(&state)) {
+ printf("\nconnman manager state = %s\n", state);
+ g_free(state);
+
+ }
+
+ rc = connman_technology_enable("wifi");
+ sleep(5);
+
+ rc = connman_technology_scan_services("wifi");
+ if(!rc) {
+ printf("wifi scan failed!\n");
+ exit(1);
+ }
+
+ sleep(20);
+
+ reply = NULL;
+ rc = connman_get_services(&reply);
+ if(rc) {
+ printf("services: %s\n", reply ? g_variant_print(reply, TRUE) : "(null)");
+ g_variant_unref(reply);
+ }
+
+ rc = connman_technology_disable("wifi");
+
+ return 0;
+}