diff --git a/gdbus/client.c b/gdbus/client.c deleted file mode 100644 index 93c6d4e..0000000 --- a/gdbus/client.c +++ /dev/null @@ -1,2009 +0,0 @@ -/* - * Copyright (C) 2025 Xiaomi Corporation - * - * 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. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#define _GNU_SOURCE -#include -#include - -#include -#include -#include - -#include "gdbus-internal.h" - -/** method call timeout in milliseconds: - * -1 (or #DBUS_TIMEOUT_USE_DEFAULT) for default timer(25s) - * ((int) 0x7fffffff)(or #DBUS_TIMEOUT_INFINITE) for no timeout - */ -#define METHOD_CALL_TIMEOUT DBUS_TIMEOUT_INFINITE - -#ifndef DBUS_INTERFACE_OBJECT_MANAGER -#define DBUS_INTERFACE_OBJECT_MANAGER DBUS_INTERFACE_DBUS ".ObjectManager" -#endif - -struct ptr_array { - void** data; - size_t len; - size_t alloc_len; -}; - -struct prop_entry { - char* name; - int type; - DBusMessage* msg; -}; - -enum client_async_handler_type { - ASYNC_HDL_REPLY_ASYNC = 1, - ASYNC_HDL_GET_PROP, -}; - -typedef struct client_async_handler { - int handle_type; - void* data; -} client_async_handler; - -struct get_prop_handler { - GDBusProxy* proxy; - const char* name; - void* prop_value; - GDBusPropIterFunction prop_iter_cb; - pthread_cond_t cond; - pthread_mutex_t mutex; - int result; -}; - -struct pending_call_async { - DBusConnection* conn; - DBusMessage* msg; - DBusPendingCall** call; - int timeout; - DBusPendingCallNotifyFunction pending_reply; - void* user_data; - DBusFreeFunction destroy; -}; - -static gboolean get_properties_specific(GDBusProxy* proxy); - -static client_async_handler* new_client_async_handler(int handle_type, void* data) -{ - client_async_handler* async_hdl = calloc(1, sizeof(client_async_handler)); - if (async_hdl == NULL) - return NULL; - async_hdl->handle_type = handle_type; - async_hdl->data = data; - - return async_hdl; -} - -static gboolean dbus_send_msg_reply_pendingcall(DBusConnection* conn, DBusMessage* msg, - DBusPendingCall** call, int timeout, DBusPendingCallNotifyFunction pending_reply, - void* user_data, DBusFreeFunction destroy) -{ - DBusPendingCall* pending_call = NULL; - DBusPendingCall** pending_call_pp = NULL; - - if (call == NULL) { - pending_call_pp = &pending_call; - } else { - pending_call_pp = call; - } - - if (dbus_send_message_with_reply(conn, msg, pending_call_pp, timeout) == FALSE) { - destroy(user_data); - return FALSE; - } - - dbus_pending_call_set_notify(*pending_call_pp, pending_reply, user_data, destroy); - - /* call is not NULL, it will unref in pendingcall notify */ - if (call == NULL) { - dbus_pending_call_unref(*pending_call_pp); - } - - return TRUE; -} - -static void client_async_handler_reply(struct pending_call_async* handler) -{ - dbus_send_msg_reply_pendingcall(handler->conn, handler->msg, handler->call, - handler->timeout, handler->pending_reply, handler->user_data, handler->destroy); - dbus_message_unref(handler->msg); - dbus_connection_unref(handler->conn); - free(handler); -} - -static void client_async_handler_get_prop(struct get_prop_handler* handler) -{ - DBusMessageIter iter; - - handler->result = dbus_proxy_get_property(handler->proxy, handler->name, &iter); - if (handler->result == TRUE) - handler->prop_iter_cb(&iter, handler->prop_value); - - pthread_mutex_lock(&handler->mutex); - pthread_cond_signal(&handler->cond); - pthread_mutex_unlock(&handler->mutex); -} - -static void client_uv_async_queue_cb(uv_async_queue_t* async_queue, void* data) -{ - client_async_handler* async_hdl = (client_async_handler*)data; - - switch (async_hdl->handle_type) { - case ASYNC_HDL_REPLY_ASYNC: - client_async_handler_reply(async_hdl->data); - break; - case ASYNC_HDL_GET_PROP: - client_async_handler_get_prop(async_hdl->data); - break; - } - - free(async_hdl); -} - -static gboolean dbus_send_msg_reply_async(GDBusClient* client, DBusMessage* msg, - DBusPendingCall** call, int timeout, DBusPendingCallNotifyFunction pending_reply, - void* user_data, DBusFreeFunction destroy) -{ - struct pending_call_async* handler; - uv_thread_t self_tid = uv_thread_self(); - - if (uv_thread_equal(&self_tid, &client->main_thread) != 0) { - return dbus_send_msg_reply_pendingcall(client->dbus_conn, msg, call, timeout, - pending_reply, user_data, destroy); - } - - handler = calloc(1, sizeof(struct pending_call_async)); - if (handler == NULL) { - return FALSE; - } - - handler->conn = client->dbus_conn; - dbus_connection_ref(client->dbus_conn); - handler->msg = msg; - dbus_message_ref(msg); - handler->call = call; - handler->timeout = timeout; - handler->pending_reply = pending_reply; - handler->user_data = user_data; - handler->destroy = destroy; - - client_async_handler* async_hdl = new_client_async_handler(ASYNC_HDL_REPLY_ASYNC, handler); - if (uv_async_queue_send(&client->async_queue, async_hdl) != 0) { - dbus_connection_unref(client->dbus_conn); - dbus_message_unref(msg); - free(handler); - free(async_hdl); - return FALSE; - } - - return TRUE; -} - -static char* strdup0(const char* str) -{ - if (str) - return strdup(str); - - return NULL; -} - -static struct ptr_array* ptr_array_sized_new(size_t size) -{ - struct ptr_array* parray; - - parray = malloc(sizeof(struct ptr_array) + size * sizeof(void*)); - if (parray == NULL) - return NULL; - - parray->data = (void**)(parray + 1); - parray->alloc_len = size; - parray->len = 0; - return parray; -} - -static void ptr_array_add(struct ptr_array** array, void* data) -{ - struct ptr_array* tmp; - - if ((*array)->len >= (*array)->alloc_len) { - tmp = realloc(*array, sizeof(*tmp) + (*array)->alloc_len * 2 * sizeof(void*)); - if (tmp == NULL) - return; - - *array = tmp; - (*array)->alloc_len *= 2; - (*array)->data = (void**)(tmp + 1); - } - - (*array)->data[(*array)->len++] = data; -} - -static void ptr_array_free(struct ptr_array* array) -{ - int i; - for (i = 0; i < array->len; i++) { - free(array->data[i]); - } - - free(array); -} - -static void modify_match_reply(DBusPendingCall* call, void* user_data) -{ - DBusMessage* reply = dbus_pending_call_steal_reply(call); - DBusError error; - - dbus_error_init(&error); - - if (dbus_set_error_from_message(&error, reply) == TRUE) - dbus_error_free(&error); - - dbus_message_unref(reply); -} - -static gboolean modify_match(GDBusClient* client, const char* member, - const char* rule) -{ - DBusMessage* msg; - - msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, member); - if (msg == NULL) - return FALSE; - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &rule, - DBUS_TYPE_INVALID); - - if (dbus_send_msg_reply_async(client, msg, NULL, -1, modify_match_reply, NULL, NULL) - == FALSE) { - dbus_message_unref(msg); - return FALSE; - } - - dbus_message_unref(msg); - - return TRUE; -} - -static void append_variant(DBusMessageIter* iter, int type, const void* val) -{ - DBusMessageIter value; - char sig[2] = { type, '\0' }; - - dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value); - - dbus_message_iter_append_basic(&value, type, val); - - dbus_message_iter_close_container(iter, &value); -} - -static void append_array_variant(DBusMessageIter* iter, int type, void* val, - int n_elements) -{ - DBusMessageIter variant, array; - char type_sig[2] = { type, '\0' }; - char array_sig[3] = { DBUS_TYPE_ARRAY, type, '\0' }; - - dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, - array_sig, &variant); - - dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, - type_sig, &array); - - if (dbus_type_is_fixed(type) == TRUE) { - dbus_message_iter_append_fixed_array(&array, type, val, - n_elements); - } else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) { - const char*** str_array = val; - int i; - - for (i = 0; i < n_elements; i++) - dbus_message_iter_append_basic(&array, type, - &((*str_array)[i])); - } - - dbus_message_iter_close_container(&variant, &array); - - dbus_message_iter_close_container(iter, &variant); -} - -static void dict_append_basic(DBusMessageIter* dict, int key_type, - const void* key, int type, void* val) -{ - DBusMessageIter entry; - - if (type == DBUS_TYPE_STRING) { - const char* str = *((const char**)val); - if (str == NULL) - return; - } - - dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, - NULL, &entry); - - dbus_message_iter_append_basic(&entry, key_type, key); - - append_variant(&entry, type, val); - - dbus_message_iter_close_container(dict, &entry); -} - -void dbus_dict_append_entry(DBusMessageIter* dict, - const char* key, int type, void* val) -{ - dict_append_basic(dict, DBUS_TYPE_STRING, &key, type, val); -} - -void dbus_dict_append_basic_array(DBusMessageIter* dict, int key_type, - const void* key, int type, void* val, - int n_elements) -{ - DBusMessageIter entry; - - dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, - NULL, &entry); - - dbus_message_iter_append_basic(&entry, key_type, key); - - append_array_variant(&entry, type, val, n_elements); - - dbus_message_iter_close_container(dict, &entry); -} - -void dbus_dict_append_array(DBusMessageIter* dict, - const char* key, int type, void* val, - int n_elements) -{ - dbus_dict_append_basic_array(dict, DBUS_TYPE_STRING, &key, type, val, - n_elements); -} - -static void iter_append_iter(DBusMessageIter* base, DBusMessageIter* iter) -{ - int type; - - type = dbus_message_iter_get_arg_type(iter); - - if (dbus_type_is_basic(type)) { - DBusBasicValue value; - - dbus_message_iter_get_basic(iter, &value); - dbus_message_iter_append_basic(base, type, &value); - } else if (dbus_type_is_container(type)) { - DBusMessageIter iter_sub, base_sub; - char* sig; - - dbus_message_iter_recurse(iter, &iter_sub); - - switch (type) { - case DBUS_TYPE_ARRAY: - case DBUS_TYPE_VARIANT: - sig = dbus_message_iter_get_signature(&iter_sub); - break; - default: - sig = NULL; - break; - } - - dbus_message_iter_open_container(base, type, sig, &base_sub); - - if (sig != NULL) - dbus_free(sig); - - while (dbus_message_iter_get_arg_type(&iter_sub) != DBUS_TYPE_INVALID) { - iter_append_iter(&base_sub, &iter_sub); - dbus_message_iter_next(&iter_sub); - } - - dbus_message_iter_close_container(base, &base_sub); - } -} - -static void prop_entry_update(struct prop_entry* prop, DBusMessageIter* iter) -{ - DBusMessage* msg; - DBusMessageIter base; - - msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); - if (msg == NULL) - return; - - dbus_message_iter_init_append(msg, &base); - iter_append_iter(&base, iter); - - if (prop->msg != NULL) - dbus_message_unref(prop->msg); - - prop->msg = dbus_message_copy(msg); - dbus_message_unref(msg); -} - -static struct prop_entry* prop_entry_new(const char* name, - DBusMessageIter* iter) -{ - struct prop_entry* prop; - - prop = calloc(1, sizeof(struct prop_entry)); - if (prop == NULL) - return NULL; - - prop->name = strdup0(name); - prop->type = dbus_message_iter_get_arg_type(iter); - - prop_entry_update(prop, iter); - - return prop; -} - -static void prop_entry_free(gpointer data) -{ - struct prop_entry* prop = data; - - if (prop == NULL) - return; - - if (prop->msg != NULL) - dbus_message_unref(prop->msg); - - free(prop->name); - - free(prop); -} - -static void add_property(GDBusProxy* proxy, const char* name, - DBusMessageIter* iter, gboolean send_changed, gboolean standard) -{ - GDBusClient* client = proxy->client; - DBusMessageIter value; - struct prop_entry* prop; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) - return; - - dbus_message_iter_recurse(iter, &value); - - client->standard = standard; - prop = _dbus_hash_table_lookup_string(proxy->prop_list, name); - if (prop != NULL) { - prop_entry_update(prop, &value); - goto done; - } - - prop = prop_entry_new(name, &value); - if (prop == NULL) - return; - - _dbus_hash_table_insert_string(proxy->prop_list, prop->name, prop); - -done: - if (proxy->prop_func) - proxy->prop_func(proxy, name, &value, proxy->prop_data); - - if (send_changed == FALSE) - return; - - if (client->property_changed) - client->property_changed(proxy, name, &value, - client->user_data); -} - -static void update_properties(GDBusProxy* proxy, DBusMessageIter* iter, - gboolean send_changed, gboolean standard) -{ - DBusMessageIter dict; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) - return; - - dbus_message_iter_recurse(iter, &dict); - - while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { - DBusMessageIter entry; - const char* name; - - dbus_message_iter_recurse(&dict, &entry); - - if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) - break; - - dbus_message_iter_get_basic(&entry, &name); - dbus_message_iter_next(&entry); - - add_property(proxy, name, &entry, send_changed, standard); - - dbus_message_iter_next(&dict); - } -} - -static void proxy_added(GDBusClient* client, GDBusProxy* proxy) -{ - if (!proxy->pending) - return; - - if (client->proxy_added) - client->proxy_added(proxy, client->user_data); - - proxy->pending = FALSE; - proxy->filter_first = TRUE; -} - -static void get_all_properties_reply(DBusPendingCall* call, void* user_data) -{ - GDBusProxy* proxy = user_data; - GDBusClient* client = proxy->client; - DBusMessage* reply = dbus_pending_call_steal_reply(call); - DBusMessageIter iter; - DBusError error; - - dbus_client_ref(client); - - dbus_error_init(&error); - - if (dbus_set_error_from_message(&error, reply) == TRUE) { - dbus_error_free(&error); - goto done; - } - - if (!dbus_message_iter_init(reply, &iter)) - goto done; - - update_properties(proxy, &iter, FALSE, TRUE); - -done: - if (_dbus_hash_table_get_n_entries(proxy->prop_list) != 0) - proxy_added(client, proxy); - - dbus_message_unref(reply); - - proxy->getting_all_prop = FALSE; - dbus_pending_call_unref(proxy->get_all_call); - proxy->get_all_call = NULL; - - dbus_client_unref(client); -} - -static void get_all_properties(GDBusProxy* proxy) -{ - GDBusClient* client = proxy->client; - const char* service_name = client->service_name; - DBusMessage* msg; - - if (proxy->getting_all_prop) - return; - - msg = dbus_message_new_method_call(service_name, proxy->obj_path, - DBUS_INTERFACE_PROPERTIES, "GetAll"); - if (msg == NULL) - return; - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &proxy->interface, - DBUS_TYPE_INVALID); - - if (dbus_send_msg_reply_async(client, msg, &proxy->get_all_call, - -1, get_all_properties_reply, proxy, NULL) - == FALSE) { - dbus_message_unref(msg); - return; - } - proxy->getting_all_prop = TRUE; - dbus_message_unref(msg); -} - -GDBusProxy* dbus_proxy_lookup(void* list, int* index, const char* path, - const char* interface) -{ - DBusList* list_ = list; - int n = index ? *index : 0; - DBusList* l; - - if (!interface) - return NULL; - - for (l = _dbus_list_get_first_link(&list_); l; - l = _dbus_list_get_next_link(&list_, l)) { - if (n-- > 0) { - continue; - } - - GDBusProxy* proxy = l->data; - const char* proxy_iface = dbus_proxy_get_interface(proxy); - const char* proxy_path = dbus_proxy_get_path(proxy); - - if (index) - (*index)++; - - if (strcmp(proxy_iface, interface) == 0 && strcmp(proxy_path, path) == 0) - return proxy; - } - - return NULL; -} - -char* dbus_proxy_path_lookup(void* list, int* index, const char* path) -{ - DBusList* list_ = list; - int len = strlen(path); - int n = index ? *index : 0; - DBusList* l; - - for (l = _dbus_list_get_first_link(&list_); l; - l = _dbus_list_get_next_link(&list_, l)) { - if (n-- > 0) { - continue; - } - - GDBusProxy* proxy = l->data; - const char* proxy_path = dbus_proxy_get_path(proxy); - - if (index) - (*index)++; - - if (!strncasecmp(proxy_path, path, len)) - return strdup0(proxy_path); - } - - return NULL; -} - -static gboolean properties_changed(DBusConnection* conn, DBusMessage* msg, - void* user_data) -{ - GDBusProxy* proxy = user_data; - GDBusClient* client = proxy->client; - DBusMessageIter iter, entry; - const char* interface; - - if (dbus_message_iter_init(msg, &iter) == FALSE) - return TRUE; - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) - return TRUE; - - dbus_message_iter_get_basic(&iter, &interface); - dbus_message_iter_next(&iter); - - update_properties(proxy, &iter, TRUE, TRUE); - - dbus_message_iter_next(&iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) - return TRUE; - - dbus_message_iter_recurse(&iter, &entry); - - while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { - const char* name; - - dbus_message_iter_get_basic(&entry, &name); - - _dbus_hash_table_remove_string(proxy->prop_list, name); - - if (proxy->prop_func) - proxy->prop_func(proxy, name, NULL, proxy->prop_data); - - if (client->property_changed) - client->property_changed(proxy, name, NULL, - client->user_data); - - dbus_message_iter_next(&entry); - } - - return TRUE; -} - -static gboolean properties_changed_non_standard(DBusConnection* conn, DBusMessage* msg, - void* user_data) -{ - GDBusProxy* proxy = user_data; - DBusMessageIter iter; - const char* name; - - if (dbus_message_iter_init(msg, &iter) == FALSE) - return TRUE; - - dbus_message_iter_get_basic(&iter, &name); - - dbus_message_iter_next(&iter); - add_property(proxy, name, &iter, TRUE, FALSE); - - return TRUE; -} - -static GDBusProxy* proxy_new(GDBusClient* client, const char* path, - const char* interface) -{ - GDBusProxy* proxy; - - if (client->proxy_filter && client->proxy_filter(path, interface, client->user_data)) { - return NULL; - } - - proxy = calloc(1, sizeof(GDBusProxy)); - if (proxy == NULL) - return NULL; - - proxy->client = client; - proxy->obj_path = strdup0(path); - proxy->interface = strdup0(interface); - - proxy->prop_list = _dbus_hash_table_new(DBUS_HASH_STRING, - NULL, prop_entry_free); - proxy->watch = dbus_add_properties_watch(client->watcher, - client->service_name, - proxy->obj_path, - proxy->interface, - properties_changed, - proxy, NULL); - - proxy->watch_non_standard = dbus_add_signal_watch(client->watcher, - client->service_name, - proxy->obj_path, - proxy->interface, - "PropertyChanged", - properties_changed_non_standard, - proxy, NULL); - - proxy->pending = TRUE; - proxy->filter_first = FALSE; - - _dbus_list_append(&client->proxy_list, proxy); - - return dbus_proxy_ref(proxy); -} - -static void proxy_free(gpointer data) -{ - GDBusProxy* proxy = data; - - if (proxy->client) { - GDBusClient* client = proxy->client; - - proxy->getting_all_prop = FALSE; - if (proxy->get_all_call != NULL) { - dbus_pending_call_cancel(proxy->get_all_call); - dbus_pending_call_unref(proxy->get_all_call); - proxy->get_all_call = NULL; - } - - if (client->proxy_removed) - client->proxy_removed(proxy, client->user_data); - - dbus_remove_watch(client->watcher, proxy->watch); - dbus_remove_watch(client->watcher, proxy->watch_non_standard); - - _dbus_hash_table_remove_all(proxy->prop_list); - - proxy->client = NULL; - } - - if (proxy->removed_func) - proxy->removed_func(proxy, proxy->removed_data); - - dbus_proxy_unref(proxy); -} - -static void proxy_remove(GDBusClient* client, const char* path, - const char* interface) -{ - DBusList* list; - - for (list = _dbus_list_get_first_link(&client->proxy_list); list; - list = _dbus_list_get_next_link(&client->proxy_list, list)) { - GDBusProxy* proxy = list->data; - - if (strcmp(proxy->interface, interface) == 0 && strcmp(proxy->obj_path, path) == 0) { - _dbus_list_remove_link(&client->proxy_list, list); - proxy_free(proxy); - break; - } - } -} - -static void start_service(GDBusProxy* proxy) -{ - GDBusClient* client = proxy->client; - const char* service_name = client->service_name; - dbus_uint32_t flags = 0; - DBusMessage* msg; - - msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - "StartServiceByName"); - if (msg == NULL) - return; - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &service_name, - DBUS_TYPE_UINT32, &flags, - DBUS_TYPE_INVALID); - - dbus_send_message(client->dbus_conn, msg); - return; -} - -GDBusProxy* dbus_proxy_new(GDBusClient* client, const char* path, - const char* interface) -{ - GDBusProxy* proxy; - - if (client == NULL) - return NULL; - - proxy = dbus_proxy_lookup(client->proxy_list, NULL, - path, interface); - if (proxy) - return dbus_proxy_ref(proxy); - - proxy = proxy_new(client, path, interface); - if (proxy == NULL) - return NULL; - - if (!client->connected) { - /* Force service to start */ - start_service(proxy); - return dbus_proxy_ref(proxy); - } - - if (!client->getting_object_call) - get_all_properties(proxy); - - return dbus_proxy_ref(proxy); -} - -GDBusProxy* dbus_proxy_ref(GDBusProxy* proxy) -{ - if (proxy == NULL) - return NULL; - - __sync_fetch_and_add(&proxy->ref_count, 1); - - return proxy; -} - -void dbus_proxy_unref(GDBusProxy* proxy) -{ - if (proxy == NULL) - return; - - if (__sync_sub_and_fetch(&proxy->ref_count, 1) > 0) - return; - - proxy->getting_all_prop = FALSE; - if (proxy->get_all_call != NULL) { - dbus_pending_call_cancel(proxy->get_all_call); - dbus_pending_call_unref(proxy->get_all_call); - } - - _dbus_hash_table_unref(proxy->prop_list); - - free(proxy->obj_path); - free(proxy->interface); - - free(proxy); -} - -const char* dbus_proxy_get_path(const GDBusProxy* proxy) -{ - if (proxy == NULL) - return NULL; - - return proxy->obj_path; -} - -const char* dbus_proxy_get_interface(GDBusProxy* proxy) -{ - if (proxy == NULL) - return NULL; - - return proxy->interface; -} - -gboolean dbus_proxy_get_property(GDBusProxy* proxy, const char* name, - DBusMessageIter* iter) -{ - struct prop_entry* prop; - - if (proxy == NULL || name == NULL) - return FALSE; - - prop = _dbus_hash_table_lookup_string(proxy->prop_list, name); - if (prop != NULL - && prop->msg != NULL - && dbus_message_iter_init(prop->msg, iter) == TRUE) - return TRUE; - - return FALSE; -} - -gboolean dbus_proxy_get_property_basic(GDBusProxy* proxy, const char* name, - void* value) -{ - return dbus_proxy_get_property_iter_cb(proxy, name, value, - dbus_message_iter_get_basic); -} - -gboolean dbus_proxy_get_property_iter_cb(GDBusProxy* proxy, const char* name, - void* value, GDBusPropIterFunction iter_cb) -{ - DBusMessageIter iter; - uv_thread_t self_tid = uv_thread_self(); - GDBusClient* client = NULL; - gboolean ret = FALSE; - - if (proxy == NULL || name == NULL) - return FALSE; - - client = proxy->client; - if (client == NULL) - return FALSE; - - if (uv_thread_equal(&self_tid, &client->main_thread) != 0) { - if (dbus_proxy_get_property(proxy, name, &iter) == FALSE) - return FALSE; - - iter_cb(&iter, value); - return TRUE; - } - - struct get_prop_handler* prop_hdl = calloc(1, sizeof(struct get_prop_handler)); - if (prop_hdl == NULL) - return FALSE; - prop_hdl->proxy = proxy; - prop_hdl->name = name; - pthread_mutex_init(&prop_hdl->mutex, NULL); - pthread_cond_init(&prop_hdl->cond, NULL); - prop_hdl->prop_value = value; - prop_hdl->prop_iter_cb = iter_cb; - prop_hdl->result = -1; - - client_async_handler* async_hdl = new_client_async_handler(ASYNC_HDL_GET_PROP, prop_hdl); - pthread_mutex_lock(&prop_hdl->mutex); - if (uv_async_queue_send(&client->async_queue, async_hdl) != 0) { - free(prop_hdl); - free(async_hdl); - return FALSE; - } - - while (prop_hdl->result == -1) - pthread_cond_wait(&prop_hdl->cond, &prop_hdl->mutex); - - if (prop_hdl->result == TRUE) - ret = TRUE; - - pthread_mutex_unlock(&prop_hdl->mutex); - free(prop_hdl); - return ret; -} - -struct refresh_property_data { - GDBusProxy* proxy; - char* name; -}; - -static void refresh_property_free(gpointer user_data) -{ - struct refresh_property_data* data = user_data; - - free(data->name); - free(data); -} - -static void refresh_property_reply(DBusPendingCall* call, void* user_data) -{ - struct refresh_property_data* data = user_data; - DBusMessage* reply = dbus_pending_call_steal_reply(call); - DBusError error; - - dbus_error_init(&error); - - if (dbus_set_error_from_message(&error, reply) == FALSE) { - DBusMessageIter iter; - - if (!dbus_message_iter_init(reply, &iter)) - return; - - add_property(data->proxy, data->name, &iter, TRUE, TRUE); - } else - dbus_error_free(&error); - - dbus_message_unref(reply); -} - -static void refresh_properties_reply_not_standard(DBusMessage* message, - void* user_data) -{ - GDBusProxy* proxy = user_data; - DBusMessageIter array; - DBusError error; - - dbus_proxy_ref(proxy); - dbus_error_init(&error); - - if (dbus_set_error_from_message(&error, message)) { - dbus_error_free(&error); - dbus_proxy_unref(proxy); - return; - } - - if (!dbus_message_iter_init(message, &array)) { - dbus_proxy_unref(proxy); - return; - } - - update_properties(proxy, &array, TRUE, FALSE); - - dbus_proxy_unref(proxy); -} - -gboolean dbus_proxy_refresh_property(GDBusProxy* proxy, const char* name) -{ - struct refresh_property_data* data; - GDBusClient* client; - DBusMessage* msg; - DBusMessageIter iter; - - if (proxy == NULL || name == NULL) - return FALSE; - - client = proxy->client; - if (client == NULL) - return FALSE; - - if (!client->standard) { - return dbus_proxy_method_call(proxy, "GetProperties", - NULL, refresh_properties_reply_not_standard, - proxy, NULL); - } - - data = calloc(1, sizeof(struct refresh_property_data)); - if (data == NULL) - return FALSE; - - data->proxy = proxy; - data->name = strdup0(name); - - msg = dbus_message_new_method_call(client->service_name, - proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Get"); - - if (msg == NULL) { - refresh_property_free(data); - return FALSE; - } - - dbus_message_iter_init_append(msg, &iter); - dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, - &proxy->interface); - dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); - - if (dbus_send_msg_reply_async(client, msg, NULL, -1, refresh_property_reply, - data, refresh_property_free) - == FALSE) { - dbus_message_unref(msg); - refresh_property_free(data); - return FALSE; - } - - dbus_message_unref(msg); - - return TRUE; -} - -struct set_property_data { - GDBusResultFunction function; - void* user_data; - GDBusDestroyFunction destroy; -}; - -static void set_property_reply(DBusPendingCall* call, void* user_data) -{ - struct set_property_data* data = user_data; - DBusMessage* reply = dbus_pending_call_steal_reply(call); - DBusError error; - - dbus_error_init(&error); - - dbus_set_error_from_message(&error, reply); - - if (data->function) - data->function(&error, data->user_data); - - if (data->destroy) - data->destroy(data->user_data); - - dbus_error_free(&error); - - dbus_message_unref(reply); -} - -gboolean dbus_proxy_set_property_basic(GDBusProxy* proxy, - const char* name, int type, const void* value, - GDBusResultFunction function, void* user_data, - GDBusDestroyFunction destroy) -{ - struct set_property_data* data; - GDBusClient* client; - DBusMessage* msg; - DBusMessageIter iter; - - if (proxy == NULL || name == NULL || value == NULL) - return FALSE; - - if (dbus_type_is_basic(type) == FALSE) - return FALSE; - - client = proxy->client; - if (client == NULL) - return FALSE; - - data = calloc(1, sizeof(struct set_property_data)); - if (data == NULL) - return FALSE; - - data->function = function; - data->user_data = user_data; - data->destroy = destroy; - - if (client->standard) { - msg = dbus_message_new_method_call(client->service_name, - proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set"); - } else { - msg = dbus_message_new_method_call(client->service_name, - proxy->obj_path, proxy->interface, "SetProperty"); - } - - if (msg == NULL) { - free(data); - return FALSE; - } - - dbus_message_iter_init_append(msg, &iter); - - if (client->standard) - dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, - &proxy->interface); - - dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); - - append_variant(&iter, type, value); - - if (dbus_send_msg_reply_async(client, msg, NULL, -1, set_property_reply, data, free) - == FALSE) { - dbus_message_unref(msg); - free(data); - return FALSE; - } - - dbus_message_unref(msg); - - return TRUE; -} - -gboolean dbus_proxy_set_property_array(GDBusProxy* proxy, - const char* name, int type, const void* value, - size_t size, GDBusResultFunction function, - void* user_data, GDBusDestroyFunction destroy) -{ - struct set_property_data* data; - GDBusClient* client; - DBusMessage* msg; - DBusMessageIter iter; - - if (!proxy || !name || !value) - return FALSE; - - if (!dbus_type_is_basic(type)) - return FALSE; - - client = proxy->client; - if (!client || !client->standard) - return FALSE; - - data = calloc(1, sizeof(struct set_property_data)); - if (!data) - return FALSE; - - data->function = function; - data->user_data = user_data; - data->destroy = destroy; - - msg = dbus_message_new_method_call(client->service_name, - proxy->obj_path, - DBUS_INTERFACE_PROPERTIES, - "Set"); - if (!msg) { - free(data); - return FALSE; - } - - dbus_message_iter_init_append(msg, &iter); - dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, - &proxy->interface); - dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); - - append_array_variant(&iter, type, &value, size); - - if (dbus_send_msg_reply_async(client, msg, NULL, -1, set_property_reply, data, free) - == FALSE) { - dbus_message_unref(msg); - free(data); - return FALSE; - } - - dbus_message_unref(msg); - - return TRUE; -} - -struct method_call_data { - GDBusReturnFunction function; - void* user_data; - GDBusDestroyFunction destroy; -}; - -static void method_call_reply(DBusPendingCall* call, void* user_data) -{ - struct method_call_data* data = user_data; - DBusMessage* reply = dbus_pending_call_steal_reply(call); - - if (data->function) - data->function(reply, data->user_data); - - if (data->destroy) - data->destroy(data->user_data); - - dbus_message_unref(reply); -} - -gboolean dbus_proxy_method_call(GDBusProxy* proxy, const char* method, - GDBusSetupFunction setup, - GDBusReturnFunction function, void* user_data, - GDBusDestroyFunction destroy) -{ - struct method_call_data* data; - GDBusClient* client; - DBusMessage* msg; - - if (proxy == NULL || method == NULL) - return FALSE; - - client = proxy->client; - if (client == NULL) - return FALSE; - - msg = dbus_message_new_method_call(client->service_name, - proxy->obj_path, proxy->interface, method); - if (msg == NULL) - return FALSE; - - if (setup) { - DBusMessageIter iter; - - dbus_message_iter_init_append(msg, &iter); - setup(&iter, user_data); - } - - if (!function) - return dbus_send_message(client->dbus_conn, msg); - - data = calloc(1, sizeof(struct method_call_data)); - if (data == NULL) - return FALSE; - - data->function = function; - data->user_data = user_data; - data->destroy = destroy; - - if (dbus_send_msg_reply_async(client, msg, NULL, METHOD_CALL_TIMEOUT, - method_call_reply, data, free) - == FALSE) { - dbus_message_unref(msg); - free(data); - return FALSE; - } - - dbus_message_unref(msg); - - return TRUE; -} - -gboolean dbus_proxy_set_property_watch(GDBusProxy* proxy, - GDBusPropertyFunction function, void* user_data) -{ - if (proxy == NULL) - return FALSE; - - proxy->prop_func = function; - proxy->prop_data = user_data; - - return TRUE; -} - -gboolean dbus_proxy_remove_property_watch(GDBusProxy* proxy, - GDBusDestroyFunction destroy) -{ - if (proxy == NULL) - return FALSE; - - proxy->prop_func = NULL; - if (destroy) - destroy(proxy->prop_data); - proxy->prop_data = NULL; - - return TRUE; -} - -gboolean dbus_proxy_set_removed_watch(GDBusProxy* proxy, - GDBusProxyFunction function, void* user_data) -{ - if (proxy == NULL) - return FALSE; - - proxy->removed_func = function; - proxy->removed_data = user_data; - - return TRUE; -} - -static void refresh_properties(DBusList* list) -{ - DBusList* l; - - for (l = _dbus_list_get_first_link(&list); l; - l = _dbus_list_get_next_link(&list, l)) { - GDBusProxy* proxy = l->data; - - if (proxy->pending) - get_all_properties(proxy); - } -} - -static void parse_properties(GDBusClient* client, const char* path, - const char* interface, DBusMessageIter* iter) -{ - GDBusProxy* proxy; - - if (strcmp(interface, DBUS_INTERFACE_INTROSPECTABLE) == 0) - return; - - if (strcmp(interface, DBUS_INTERFACE_PROPERTIES) == 0) - return; - - proxy = dbus_proxy_lookup(client->proxy_list, NULL, - path, interface); - if (proxy && !proxy->pending) { - update_properties(proxy, iter, FALSE, TRUE); - return; - } - - if (!proxy) { - proxy = proxy_new(client, path, interface); - if (proxy == NULL) - return; - - get_properties_specific(proxy); - return; - } - - update_properties(proxy, iter, FALSE, TRUE); - - if (_dbus_hash_table_get_n_entries(proxy->prop_list) != 0) - proxy_added(client, proxy); -} - -static void parse_interfaces(GDBusClient* client, const char* path, - DBusMessageIter* iter) -{ - DBusMessageIter dict; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) - return; - - dbus_message_iter_recurse(iter, &dict); - - while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { - DBusMessageIter entry; - const char* interface; - - dbus_message_iter_recurse(&dict, &entry); - - if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) - break; - - dbus_message_iter_get_basic(&entry, &interface); - dbus_message_iter_next(&entry); - - parse_properties(client, path, interface, &entry); - - dbus_message_iter_next(&dict); - } -} - -static void get_properties_reply_not_standard(DBusPendingCall* call, void* user_data) -{ - GDBusProxy* proxy = user_data; - GDBusClient* client = proxy->client; - DBusMessage* message = dbus_pending_call_steal_reply(call); - DBusMessageIter array; - DBusError error; - - if (!proxy->getting_all_prop) { - /** - * not in getting prop process, do nothing for reply. - * maybe proxy already freed. - */ - return; - } - - dbus_error_init(&error); - - if (dbus_set_error_from_message(&error, message)) { - goto out; - } - - if (!dbus_message_iter_init(message, &array)) { - goto out; - } - - update_properties(proxy, &array, FALSE, FALSE); - -out: - proxy_added(client, proxy); - - if (client->ready_called == FALSE - && proxy == _dbus_list_get_last(&client->proxy_list) - && client->ready && !client->standard) { - client->ready_called = TRUE; - client->ready(client, client->ready_data); - } - - dbus_error_free(&error); - dbus_message_unref(message); - proxy->getting_all_prop = FALSE; - dbus_pending_call_unref(proxy->get_all_call); - proxy->get_all_call = NULL; -} - -static gboolean get_properties_non_standard(GDBusClient* client) -{ - DBusList* list; - - for (list = _dbus_list_get_first_link(&client->proxy_list); list; - list = _dbus_list_get_next_link(&client->proxy_list, list)) { - GDBusProxy* proxy; - DBusMessage* msg; - - proxy = list->data; - if (proxy->getting_all_prop) - continue; - - client = proxy->client; - if (client->proxy_property_filter && client->proxy_property_filter(proxy, client->user_data)) { - if (!proxy->filter_first) - proxy_added(client, proxy); - continue; - } - - msg = dbus_message_new_method_call(client->service_name, - proxy->obj_path, proxy->interface, "GetProperties"); - if (msg == NULL) - return FALSE; - - if (dbus_send_msg_reply_async(client, msg, &proxy->get_all_call, -1, - get_properties_reply_not_standard, proxy, NULL) - == FALSE) { - dbus_message_unref(msg); - return FALSE; - } - proxy->getting_all_prop = TRUE; - dbus_message_unref(msg); - } - - return TRUE; -} - -static gboolean get_properties_specific(GDBusProxy* proxy) -{ - DBusMessage* msg; - GDBusClient* client; - - client = proxy->client; - - if (client->proxy_property_filter && !client->proxy_property_filter(proxy, client->user_data)) { - if (proxy->getting_all_prop) { - return FALSE; - } - - msg = dbus_message_new_method_call(client->service_name, - proxy->obj_path, proxy->interface, "GetProperties"); - if (msg == NULL) - return FALSE; - - if (dbus_send_msg_reply_async(client, msg, &proxy->get_all_call, -1, - get_properties_reply_not_standard, proxy, NULL) - == FALSE) { - dbus_message_unref(msg); - return FALSE; - } - - proxy->getting_all_prop = TRUE; - dbus_message_unref(msg); - } - - return TRUE; -} - -static gboolean interfaces_added(DBusConnection* conn, DBusMessage* msg, - void* user_data) -{ - GDBusClient* client = user_data; - DBusMessageIter iter; - const char* path; - - if (dbus_message_iter_init(msg, &iter) == FALSE) - return TRUE; - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) - return TRUE; - - dbus_message_iter_get_basic(&iter, &path); - dbus_message_iter_next(&iter); - - dbus_client_ref(client); - - parse_interfaces(client, path, &iter); - - dbus_client_unref(client); - - return TRUE; -} - -static gboolean interfaces_removed(DBusConnection* conn, DBusMessage* msg, - void* user_data) -{ - GDBusClient* client = user_data; - DBusMessageIter iter, entry; - const char* path; - - if (dbus_message_iter_init(msg, &iter) == FALSE) - return TRUE; - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) - return TRUE; - - dbus_message_iter_get_basic(&iter, &path); - dbus_message_iter_next(&iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) - return TRUE; - - dbus_message_iter_recurse(&iter, &entry); - - dbus_client_ref(client); - - while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { - const char* interface; - - dbus_message_iter_get_basic(&entry, &interface); - proxy_remove(client, path, interface); - dbus_message_iter_next(&entry); - } - - dbus_client_unref(client); - - return TRUE; -} - -static void parse_managed_objects(GDBusClient* client, DBusMessage* msg) -{ - DBusMessageIter iter, dict; - - if (dbus_message_iter_init(msg, &iter) == FALSE) - return; - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) - return; - - dbus_message_iter_recurse(&iter, &dict); - - while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { - DBusMessageIter entry; - const char* path; - - dbus_message_iter_recurse(&dict, &entry); - - if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_OBJECT_PATH) - break; - - dbus_message_iter_get_basic(&entry, &path); - dbus_message_iter_next(&entry); - - parse_interfaces(client, path, &entry); - - dbus_message_iter_next(&dict); - } - - get_properties_non_standard(client); -} - -static void get_managed_objects_reply(DBusPendingCall* call, void* user_data) -{ - GDBusClient* client = user_data; - DBusMessage* reply = dbus_pending_call_steal_reply(call); - DBusError error; - - if (!client->getting_object_call) { - /** - * not in getting object process, do nothing for reply. - * maybe client already freed. - */ - return; - } - - dbus_client_ref(client); - - dbus_error_init(&error); - - if (dbus_set_error_from_message(&error, reply) == TRUE) { - dbus_error_free(&error); - goto done; - } - - parse_managed_objects(client, reply); - -done: - if (client->ready && client->standard) - client->ready(client, client->ready_data); - - dbus_message_unref(reply); - - client->getting_object_call = FALSE; - dbus_pending_call_unref(client->get_objects_call); - client->get_objects_call = NULL; - - refresh_properties(client->proxy_list); - - dbus_client_unref(client); -} - -static void get_managed_objects(GDBusClient* client) -{ - DBusMessage* msg; - - if (!client->connected) - return; - - if ((!client->proxy_added && !client->proxy_removed) || !client->root_path) { - refresh_properties(client->proxy_list); - return; - } - - if (client->getting_object_call) - return; - - msg = dbus_message_new_method_call(client->service_name, - client->root_path, - DBUS_INTERFACE_OBJECT_MANAGER, - "GetManagedObjects"); - if (msg == NULL) - return; - - dbus_message_append_args(msg, DBUS_TYPE_INVALID); - - if (dbus_send_msg_reply_async(client, msg, &client->get_objects_call, - -1, get_managed_objects_reply, client, NULL) - == FALSE) { - dbus_message_unref(msg); - return; - } - client->getting_object_call = TRUE; - dbus_message_unref(msg); -} - -static void service_connect(DBusConnection* conn, void* user_data) -{ - GDBusClient* client = user_data; - - dbus_client_ref(client); - - client->connected = TRUE; - - get_managed_objects(client); - - if (client->connect_func) - client->connect_func(conn, client->connect_data); - - dbus_client_unref(client); -} - -static void service_disconnect(DBusConnection* conn, void* user_data) -{ - GDBusClient* client = user_data; - - client->connected = FALSE; - - _dbus_list_clear_full(&client->proxy_list, proxy_free); - client->proxy_list = NULL; - - if (client->disconn_func) - client->disconn_func(conn, client->disconn_data); -} - -static DBusHandlerResult message_filter(DBusConnection* connection, - DBusMessage* message, void* user_data) -{ - GDBusClient* client = user_data; - const char *sender, *path, *interface; - - if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - sender = dbus_message_get_sender(message); - if (sender == NULL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - path = dbus_message_get_path(message); - interface = dbus_message_get_interface(message); - - if (path == NULL || client->base_path == NULL - || strncmp(path, client->base_path, strlen(client->base_path)) != 0) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (strcmp(interface, DBUS_INTERFACE_PROPERTIES) == 0) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (client->signal_func) - client->signal_func(connection, message, client->signal_data); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -GDBusClient* dbus_client_new(DBusConnection* connection, - const char* service, const char* path) -{ - return dbus_client_new_full(connection, service, path, "/"); -} - -GDBusClient* dbus_client_new_full(DBusConnection* connection, - const char* service, - const char* path, - const char* root_path) -{ - GDBusClient* client; - DBusString str; - char* rule; - unsigned int i; - - if (!connection || !service) - return NULL; - - client = calloc(1, sizeof(GDBusClient)); - if (client == NULL) - return NULL; - - if (dbus_connection_add_filter(connection, message_filter, client, NULL) - == FALSE) { - free(client); - return NULL; - } - - client->dbus_conn = dbus_connection_ref(connection); - client->service_name = strdup0(service); - client->base_path = strdup0(path); - client->root_path = strdup0(root_path); - client->connected = FALSE; - client->watcher = new_dbus_watch(connection); - client->match_rules = ptr_array_sized_new(1); - - uv_async_queue_init(uv_default_loop(), &client->async_queue, client_uv_async_queue_cb); - client->async_queue.data = client; - client->main_thread = uv_thread_self(); - - client->watch = dbus_add_service_watch(client->watcher, service, - service_connect, - service_disconnect, - client, NULL); - - if (!root_path) - return dbus_client_ref(client); - - client->added_watch = dbus_add_signal_watch(client->watcher, service, - client->root_path, - DBUS_INTERFACE_OBJECT_MANAGER, - "InterfacesAdded", - interfaces_added, - client, NULL); - client->removed_watch = dbus_add_signal_watch(client->watcher, service, - client->root_path, - DBUS_INTERFACE_OBJECT_MANAGER, - "InterfacesRemoved", - interfaces_removed, - client, NULL); - - if (_dbus_string_init(&str)) { - _dbus_string_append_printf(&str, "type='signal', sender='%s'," - "path_namespace='%s'", - client->service_name, client->base_path); - _dbus_string_copy_data(&str, &rule); - _dbus_string_free(&str); - ptr_array_add(&client->match_rules, rule); - } - - for (i = 0; i < client->match_rules->len; i++) { - modify_match(client, "AddMatch", - client->match_rules->data[i]); - } - - return dbus_client_ref(client); -} - -static void dbus_close_uv_async_cb(uv_handle_t* handle) -{ - uv_async_queue_t* async_queue = (uv_async_queue_t*)handle; - GDBusClient* client = (GDBusClient*)async_queue->data; - - if (client != NULL) { - free(client->service_name); - free(client->base_path); - free(client->root_path); - free(client); - } -} - -GDBusClient* dbus_client_ref(GDBusClient* client) -{ - if (client == NULL) - return NULL; - - __sync_fetch_and_add(&client->ref_count, 1); - - return client; -} - -void dbus_client_unref(GDBusClient* client) -{ - unsigned int i; - - if (client == NULL) - return; - - if (__sync_sub_and_fetch(&client->ref_count, 1) > 0) - return; - - if (client->pending_call != NULL) { - dbus_pending_call_cancel(client->pending_call); - dbus_pending_call_unref(client->pending_call); - } - - client->getting_object_call = FALSE; - if (client->get_objects_call != NULL) { - dbus_pending_call_cancel(client->get_objects_call); - dbus_pending_call_unref(client->get_objects_call); - } - - for (i = 0; i < client->match_rules->len; i++) { - modify_match(client, "RemoveMatch", - client->match_rules->data[i]); - } - - ptr_array_free(client->match_rules); - - dbus_connection_remove_filter(client->dbus_conn, - message_filter, client); - - _dbus_list_clear_full(&client->proxy_list, proxy_free); - - /* - * Don't call disconn_func twice if disconnection - * was previously reported. - */ - if (client->disconn_func && client->connected) - client->disconn_func(client->dbus_conn, client->disconn_data); - - dbus_remove_watch(client->watcher, client->watch); - dbus_remove_watch(client->watcher, client->added_watch); - dbus_remove_watch(client->watcher, client->removed_watch); - - dbus_connection_unref(client->dbus_conn); - - uv_async_queue_close(&client->async_queue, dbus_close_uv_async_cb); -} - -gboolean dbus_client_set_connect_watch(GDBusClient* client, - GDBusWatchFunction function, void* user_data) -{ - if (client == NULL) - return FALSE; - - client->connect_func = function; - client->connect_data = user_data; - - return TRUE; -} - -gboolean dbus_client_set_disconnect_watch(GDBusClient* client, - GDBusWatchFunction function, void* user_data) -{ - if (client == NULL) - return FALSE; - - client->disconn_func = function; - client->disconn_data = user_data; - - return TRUE; -} - -gboolean dbus_client_set_signal_watch(GDBusClient* client, - GDBusMessageFunction function, void* user_data) -{ - if (client == NULL) - return FALSE; - - client->signal_func = function; - client->signal_data = user_data; - - return TRUE; -} - -gboolean dbus_client_set_ready_watch(GDBusClient* client, - GDBusClientFunction ready, void* user_data) -{ - if (client == NULL) - return FALSE; - - client->ready = ready; - client->ready_data = user_data; - - return TRUE; -} - -gboolean dbus_client_set_proxy_handlers(GDBusClient* client, - GDBusProxyFunction proxy_added_, - GDBusProxyFunction proxy_removed, - GDBusProxyPropertyFilterFunction proxy_property_filter, - GDBusPropertyFunction property_changed, - void* user_data) -{ - if (client == NULL) - return FALSE; - - client->proxy_added = proxy_added_; - client->proxy_removed = proxy_removed; - client->proxy_property_filter = proxy_property_filter; - client->property_changed = property_changed; - client->user_data = user_data; - - if (proxy_added_ || proxy_removed || property_changed || proxy_property_filter) - get_managed_objects(client); - - return TRUE; -} - -gboolean dbus_client_set_proxy_filter(GDBusClient* client, - GDBusProxyFilterFunction proxy_filter, void* user_data) -{ - if (client == NULL) - return FALSE; - - client->proxy_filter = proxy_filter; - client->user_data = user_data; - - return TRUE; -} diff --git a/gdbus/gdbus-internal.h b/gdbus/gdbus-internal.h deleted file mode 100644 index 7e824a5..0000000 --- a/gdbus/gdbus-internal.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2024 Xiaomi Technologies Co., Ltd. - * All rights reserved. - * - * This file is part of the Xiaomi project. - * - * This source code is 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 - -#include -#include - -#include "syslog.h" - -#include "gdbus.h" - -#define error(fmt, ...) \ - do { \ - syslog(LOG_ERR, fmt, ##__VA_ARGS__); \ - } while (0) -#define warning(fmt, ...) \ - do { \ - syslog(LOG_WARNING, fmt, ##__VA_ARGS__); \ - } while (0) -#define info(fmt, ...) \ - do { \ - syslog(LOG_INFO, fmt, ##__VA_ARGS__); \ - } while (0) -#define debug(fmt, ...) \ - do { \ - syslog(LOG_DEBUG, fmt, ##__VA_ARGS__); \ - } while (0) - -struct GDBusClient { - int ref_count; - DBusConnection* dbus_conn; - char* service_name; - char* base_path; - char* root_path; - guint watch; - guint added_watch; - guint removed_watch; - struct ptr_array* match_rules; - DBusPendingCall* pending_call; - DBusPendingCall* get_objects_call; - GDBusWatchFunction connect_func; - void* connect_data; - GDBusWatchFunction disconn_func; - gboolean connected; - void* disconn_data; - GDBusMessageFunction signal_func; - void* signal_data; - GDBusProxyFunction proxy_added; - GDBusProxyFunction proxy_removed; - GDBusProxyPropertyFilterFunction proxy_property_filter; - GDBusProxyFilterFunction proxy_filter; - GDBusClientFunction ready; - void* ready_data; - gboolean ready_called; - GDBusPropertyFunction property_changed; - void* user_data; - DBusList* proxy_list; - gboolean standard; - gboolean getting_object_call; - uv_async_queue_t async_queue; - uv_thread_t main_thread; - GDBusWatch *watcher; -}; - -struct GDBusProxy { - int ref_count; - GDBusClient* client; - char* obj_path; - char* interface; - DBusHashTable* prop_list; - guint watch; - guint watch_non_standard; - GDBusPropertyFunction prop_func; - void* prop_data; - GDBusProxyFunction removed_func; - void* removed_data; - DBusPendingCall* get_all_call; - gboolean pending; - gboolean filter_first; - gboolean getting_all_prop; -}; - -GDBusWatch* new_dbus_watch(DBusConnection* connection); -void free_dbus_watch(GDBusWatch* watcher); -void dbus_watch_set_connection_state(GDBusWatch* watcher, gboolean closed); - -int dbus_polkit_check_authorization(DBusConnection* conn, - const char* action, gboolean allow_interaction, - void (*callback)(dbus_bool_t, void*), - void* user_data, int timeout_ms); diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h deleted file mode 100644 index 4c484e4..0000000 --- a/gdbus/gdbus.h +++ /dev/null @@ -1,964 +0,0 @@ -/* - * Copyright (c) 2024 Xiaomi Technologies Co., Ltd. - * All rights reserved. - * - * This file is part of the Xiaomi project. - * - * This source code is 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 __GDBUS_H -#define __GDBUS_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -typedef void* gpointer; -typedef int gboolean; -typedef unsigned int guint; -typedef uint32_t guint32; - -#define g_dbus_setup_bus dbus_setup_bus -#define g_dbus_setup_private dbus_setup_private -#define g_dbus_request_name dbus_request_name -#define g_dbus_set_flags dbus_set_flags -#define g_dbus_get_flags dbus_get_flags -#define g_dbus_register_interface dbus_register_interface -#define g_dbus_unregister_interface dbus_unregister_interface -#define g_dbus_register_security dbus_register_security -#define g_dbus_unregister_security dbus_unregister_security -#define g_dbus_pending_success dbus_pending_success -#define g_dbus_pending_error dbus_pending_error -#define g_dbus_pending_error_valist dbus_pending_error_valist -#define g_dbus_create_error dbus_create_error -#define g_dbus_create_error_valist dbus_create_error_valist -#define g_dbus_create_reply dbus_create_reply -#define g_dbus_create_reply_valist dbus_create_reply_valist -#define g_dbus_send_message dbus_send_message -#define g_dbus_send_message_with_reply dbus_send_message_with_reply -#define g_dbus_send_error dbus_send_error -#define g_dbus_send_error_valist dbus_send_error_valist -#define g_dbus_send_reply dbus_send_reply -#define g_dbus_send_reply_valist dbus_send_reply_valist -#define g_dbus_emit_signal dbus_emit_signal -#define g_dbus_emit_signal_valist dbus_emit_signal_valist -#define g_dbus_pending_property_success dbus_pending_property_success -#define g_dbus_pending_property_error_valist dbus_pending_property_error_valist -#define g_dbus_pending_property_error dbus_pending_property_error -#define g_dbus_emit_property_changed dbus_emit_property_changed -#define g_dbus_emit_property_changed_full dbus_emit_property_changed_full -#define g_dbus_get_properties dbus_get_properties -#define g_dbus_attach_object_manager dbus_attach_object_manager -#define g_dbus_detach_object_manager dbus_detach_object_manager -#define g_dbus_proxy_new dbus_proxy_new -#define g_dbus_proxy_ref dbus_proxy_ref -#define g_dbus_proxy_unref dbus_proxy_unref -#define g_dbus_proxy_get_path dbus_proxy_get_path -#define g_dbus_proxy_get_interface dbus_proxy_get_interface -#define g_dbus_proxy_get_property dbus_proxy_get_property -#define g_dbus_proxy_get_property_basic dbus_proxy_get_property_basic -#define g_dbus_proxy_get_property_iter_cb dbus_proxy_get_property_iter_cb -#define g_dbus_proxy_lookup dbus_proxy_lookup -#define g_dbus_proxy_path_lookup dbus_proxy_path_lookup -#define g_dbus_proxy_refresh_property dbus_proxy_refresh_property -#define g_dbus_proxy_set_property_basic dbus_proxy_set_property_basic -#define g_dbus_proxy_set_property_array dbus_proxy_set_property_array -#define g_dbus_dict_append_entry dbus_dict_append_entry -#define g_dbus_dict_append_basic_array dbus_dict_append_basic_array -#define g_dbus_dict_append_array dbus_dict_append_array -#define g_dbus_proxy_method_call dbus_proxy_method_call -#define g_dbus_proxy_set_property_watch dbus_proxy_set_property_watch -#define g_dbus_proxy_remove_property_watch dbus_proxy_remove_property_watch -#define g_dbus_proxy_set_removed_watch dbus_proxy_set_removed_watch -#define g_dbus_client_new dbus_client_new -#define g_dbus_client_new_full dbus_client_new_full -#define g_dbus_client_ref dbus_client_ref -#define g_dbus_client_unref dbus_client_unref -#define g_dbus_client_set_connect_watch dbus_client_set_connect_watch -#define g_dbus_client_set_disconnect_watch dbus_client_set_disconnect_watch -#define g_dbus_client_set_signal_watch dbus_client_set_signal_watch -#define g_dbus_client_set_ready_watch dbus_client_set_ready_watch -#define g_dbus_client_set_proxy_handlers dbus_client_set_proxy_handlers -#define g_dbus_client_set_proxy_filter dbus_client_set_proxy_filter - -typedef struct GDBusArgInfo GDBusArgInfo; -typedef struct GDBusMethodTable GDBusMethodTable; -typedef struct GDBusSignalTable GDBusSignalTable; -typedef struct GDBusPropertyTable GDBusPropertyTable; -typedef struct GDBusSecurityTable GDBusSecurityTable; - -typedef void (*GDBusWatchFunction)(DBusConnection* connection, - void* user_data); - -typedef void (*GDBusMessageFunction)(DBusConnection* connection, - DBusMessage* message, void* user_data); - -typedef gboolean (*GDBusSignalFunction)(DBusConnection* connection, - DBusMessage* message, void* user_data); - -typedef void (*GDBusPropIterFunction)(DBusMessageIter* iter, void* value); - -/** - * @brief Set and connect to the specified DBus bus type - * - * @param type the type of DBus bus, which can be DBUS_BUS_SESSION or DBUS_BUS_SYSTEM - * @param name The name of the DBus bus to connect to - * @param error A DBusError structure used to store error information - * - * @return If successful, returns a pointer to DBusConnection; if failed, returns NULL - */ -DBusConnection* dbus_setup_bus(DBusBusType type, const char* name, DBusError* error); - -/** - * @brief Set up a private DBus connection - * - * This function is used to set up a private DBus connection. It accepts a DBusBusType - * parameter that specifies the type of connection (session or system), a string parameter - * that is the name of the DBus you want to connect to, and a DBusError pointer to store - * any error information that may occur. - * If the function succeeds, it returns a pointer to a DBusConnection, otherwise it returns NULL. - * - * @param type The type of DBus connection, which can be DBUS_BUS_SESSION or DBUS_BUS_SYSTEM - * @param name The name of the DBus you want to connect to - * @param error A DBusError structure used to store error information - * @return Returns a DBusConnection pointer on success, or NULL on failure - */ -DBusConnection* dbus_setup_private(DBusBusType type, const char* name, DBusError* error); - -/** - * @brief Requests a specific D-Bus name on the specified DBus connection. - * - * @param connection Pointer to a DBusConnection object representing the connection for - * which the name is to be requested. - * @param name String of the D-Bus name to be requested. - * @param error Pointer to a DBusError object for storing possible errors that may occur - * during the name request. - * - * @return Returns a gboolean value of TRUE if the request succeeds or a gboolean value - * of FALSE if the request fails. - */ -gboolean dbus_request_name(DBusConnection* connection, const char* name, DBusError* error); - -typedef void (*GDBusDestroyFunction)(void* user_data); - -typedef DBusMessage* (*GDBusMethodFunction)(DBusConnection* connection, - DBusMessage* message, void* user_data); - -typedef gboolean (*GDBusPropertyGetter)(const GDBusPropertyTable* property, - DBusMessageIter* iter, void* data); - -typedef guint32 GDBusPendingPropertySet; - -typedef void (*GDBusPropertySetter)(const GDBusPropertyTable* property, - DBusMessageIter* value, GDBusPendingPropertySet id, - void* data); - -typedef gboolean (*GDBusPropertyExists)(const GDBusPropertyTable* property, - void* data); - -typedef guint32 GDBusPendingReply; - -typedef void (*GDBusSecurityFunction)(DBusConnection* connection, - const char* action, - gboolean interaction, - GDBusPendingReply pending); - -enum GDBusFlags { - G_DBUS_FLAG_ENABLE_EXPERIMENTAL = (1 << 0), -}; - -enum GDBusMethodFlags { - G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0), - G_DBUS_METHOD_FLAG_NOREPLY = (1 << 1), - G_DBUS_METHOD_FLAG_ASYNC = (1 << 2), - G_DBUS_METHOD_FLAG_EXPERIMENTAL = (1 << 3), -}; - -enum GDBusSignalFlags { - G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0), - G_DBUS_SIGNAL_FLAG_EXPERIMENTAL = (1 << 1), -}; - -enum GDBusPropertyFlags { - G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0), - G_DBUS_PROPERTY_FLAG_EXPERIMENTAL = (1 << 1), -}; - -enum GDBusSecurityFlags { - G_DBUS_SECURITY_FLAG_DEPRECATED = (1 << 0), - G_DBUS_SECURITY_FLAG_BUILTIN = (1 << 1), - G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2), -}; - -enum GDbusPropertyChangedFlags { - G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH = (1 << 0), -}; - -typedef enum GDBusMethodFlags GDBusMethodFlags; -typedef enum GDBusSignalFlags GDBusSignalFlags; -typedef enum GDBusPropertyFlags GDBusPropertyFlags; -typedef enum GDBusSecurityFlags GDBusSecurityFlags; -typedef enum GDbusPropertyChangedFlags GDbusPropertyChangedFlags; - -struct GDBusArgInfo { - const char* name; - const char* signature; -}; - -struct GDBusMethodTable { - const char* name; - GDBusMethodFunction function; - GDBusMethodFlags flags; - unsigned int privilege; - const GDBusArgInfo* in_args; - const GDBusArgInfo* out_args; -}; - -struct GDBusSignalTable { - const char* name; - GDBusSignalFlags flags; - const GDBusArgInfo* args; -}; - -struct GDBusPropertyTable { - const char* name; - const char* type; - GDBusPropertyGetter get; - GDBusPropertySetter set; - GDBusPropertyExists exists; - GDBusPropertyFlags flags; -}; - -struct GDBusSecurityTable { - unsigned int privilege; - const char* action; - GDBusSecurityFlags flags; - GDBusSecurityFunction function; -}; - -#define GDBUS_ARGS(args...) \ - (const GDBusArgInfo[]) \ - { \ - args, { } \ - } - -#define GDBUS_METHOD(_name, _in_args, _out_args, _function) \ - .name = _name, \ - .in_args = _in_args, \ - .out_args = _out_args, \ - .function = _function - -#define GDBUS_ASYNC_METHOD(_name, _in_args, _out_args, _function) \ - .name = _name, \ - .in_args = _in_args, \ - .out_args = _out_args, \ - .function = _function, \ - .flags = G_DBUS_METHOD_FLAG_ASYNC - -#define GDBUS_DEPRECATED_METHOD(_name, _in_args, _out_args, _function) \ - .name = _name, \ - .in_args = _in_args, \ - .out_args = _out_args, \ - .function = _function, \ - .flags = G_DBUS_METHOD_FLAG_DEPRECATED - -#define GDBUS_DEPRECATED_ASYNC_METHOD(_name, _in_args, _out_args, _function) \ - .name = _name, \ - .in_args = _in_args, \ - .out_args = _out_args, \ - .function = _function, \ - .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_DEPRECATED - -#define GDBUS_EXPERIMENTAL_METHOD(_name, _in_args, _out_args, _function) \ - .name = _name, \ - .in_args = _in_args, \ - .out_args = _out_args, \ - .function = _function, \ - .flags = G_DBUS_METHOD_FLAG_EXPERIMENTAL - -#define GDBUS_EXPERIMENTAL_ASYNC_METHOD(_name, _in_args, _out_args, _function) \ - .name = _name, \ - .in_args = _in_args, \ - .out_args = _out_args, \ - .function = _function, \ - .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_EXPERIMENTAL - -#define GDBUS_NOREPLY_METHOD(_name, _in_args, _out_args, _function) \ - .name = _name, \ - .in_args = _in_args, \ - .out_args = _out_args, \ - .function = _function, \ - .flags = G_DBUS_METHOD_FLAG_NOREPLY - -#define GDBUS_SIGNAL(_name, _args) \ - .name = _name, \ - .args = _args - -#define GDBUS_DEPRECATED_SIGNAL(_name, _args) \ - .name = _name, \ - .args = _args, \ - .flags = G_DBUS_SIGNAL_FLAG_DEPRECATED - -#define GDBUS_EXPERIMENTAL_SIGNAL(_name, _args) \ - .name = _name, \ - .args = _args, \ - .flags = G_DBUS_SIGNAL_FLAG_EXPERIMENTAL - -void dbus_set_flags(int flags); -int dbus_get_flags(void); - -/** - * @brief Register an interface on the specified DBus connection - * - * This function is used to register an interface on the specified DBus connection. - * It requires the following parameters: - * - connection: DBus connection object - * - path: path of DBus interface - * - name: name of DBus interface - * - methods: a GDBusMethodTable object containing methods of interface - * - signals: a GDBusSignalTable object containing signals of interface - * - properties: a GDBusPropertyTable object containing properties of interface - * - user_data: user data, which can be passed to callback function - * - destroy: a GDBusDestroyFunction, which is called when the interface is deregistered - * to clean up user data - * - * @param connection DBus connection object - * @param path path of DBus interface - * @param name name of DBus interface - * @param methods GDBusMethodTable object containing interface methods - * @param signals GDBusSignalTable object containing interface signals - * @param properties GDBusPropertyTable object containing interface properties - * @param user_data user data - * @param destroy GDBusDestroyFunction for cleaning up user data - * @return gboolean Returns TRUE if registration is successful, and FALSE if fails - */ -gboolean dbus_register_interface(DBusConnection* connection, - const char* path, const char* name, - const GDBusMethodTable* methods, - const GDBusSignalTable* signals, - const GDBusPropertyTable* properties, - void* user_data, - GDBusDestroyFunction destroy); - -/** - * @brief Unregister the specified interface from the specified DBus connection - * - * @param connection Pointer to DBusConnection, indicating the DBus connection of the - * interface to be unregistered - * @param path Path of the interface - * @param name Name of the interface - * @return If the interface is successfully unregistered, return gboolean TRUE, otherwise - * return FALSE - */ -gboolean dbus_unregister_interface(DBusConnection* connection, - const char* path, const char* name); - -gboolean dbus_register_security(const GDBusSecurityTable* security); -gboolean dbus_unregister_security(const GDBusSecurityTable* security); - -void dbus_pending_success(DBusConnection* connection, - GDBusPendingReply pending); -void dbus_pending_error(DBusConnection* connection, - GDBusPendingReply pending, - const char* name, const char* format, ...) - __attribute__((format(printf, 4, 5))); -void dbus_pending_error_valist(DBusConnection* connection, - GDBusPendingReply pending, const char* name, - const char* format, va_list args); - -DBusMessage* dbus_create_error(DBusMessage* message, const char* name, - const char* format, ...) - __attribute__((format(printf, 3, 4))); -DBusMessage* dbus_create_error_valist(DBusMessage* message, const char* name, - const char* format, va_list args); -DBusMessage* dbus_create_reply(DBusMessage* message, int type, ...); -DBusMessage* dbus_create_reply_valist(DBusMessage* message, - int type, va_list args); - -/** - * @brief Send a message to the specified DBus connection - * - * @param connection Pointer to the DBusConnection object, indicating the DBus connection - * to send the message - * @param message Pointer to the DBusMessage object, indicating the message to send - * - * @return If the sending is successful, return the gboolean type with a value of TRUE; - * if the sending fails, return the gboolean type with a value of FALSE - */ -gboolean dbus_send_message(DBusConnection* connection, DBusMessage* message); - -/** - * @brief Send a message to the specified DBus connection and wait for a reply, - * non-blocking call - * - * @param connection Pointer to the DBusConnection object, indicating the DBus connection - * @param message Pointer to the DBusMessage object, indicating the message to be sent - * @param call Pointer to the pointer to the DBusPendingCall object, used to store the - * handle of the asynchronous call - * @param timeout Timeout for waiting for a reply after sending a message, in milliseconds - * - * @return If the message is successfully sent and the DBusPendingCall object instance is - * replied, the return value of the gboolean type is TRUE; otherwise, it returns FALSE - */ -gboolean dbus_send_message_with_reply(DBusConnection* connection, DBusMessage* message, - DBusPendingCall** call, int timeout); - -gboolean dbus_send_error(DBusConnection* connection, DBusMessage* message, - const char* name, const char* format, ...) - __attribute__((format(printf, 4, 5))); -gboolean dbus_send_error_valist(DBusConnection* connection, - DBusMessage* message, const char* name, - const char* format, va_list args); -gboolean dbus_send_reply(DBusConnection* connection, - DBusMessage* message, int type, ...); -gboolean dbus_send_reply_valist(DBusConnection* connection, - DBusMessage* message, int type, va_list args); - -gboolean dbus_emit_signal(DBusConnection* connection, - const char* path, const char* interface, - const char* name, int type, ...); -gboolean dbus_emit_signal_valist(DBusConnection* connection, - const char* path, const char* interface, - const char* name, int type, va_list args); - -typedef struct GDBusWatch GDBusWatch; - -/** - * Adds a service watch to monitor the specified D-Bus service name - * - * @param watcher The GDBusWatch instance to add the watch to - * @param name The D-Bus service name to watch (e.g. "org.freedesktop.DBus") - * @param connect Callback function when service connects - * @param disconnect Callback function when service disconnects - * @param user_data User data passed to callbacks - * @param destroy Destroy notification callback for user_data - * @return Watch ID that can be used to remove the watch - */ -guint dbus_add_service_watch(GDBusWatch* watcher, const char* name, - GDBusWatchFunction connect, GDBusWatchFunction disconnect, - void* user_data, GDBusDestroyFunction destroy); - -/** - * Adds a disconnect watch for the specified D-Bus name - * - * @param watcher The GDBusWatch instance to add the watch to - * @param name The D-Bus name to monitor for disconnection - * @param function Callback function when name disconnects - * @param user_data User data passed to callback - * @param destroy Destroy notification callback for user_data - * @return Watch ID that can be used to remove the watch - */ -guint dbus_add_service_disconnect_watch(GDBusWatch* watcher, const char* name, - GDBusWatchFunction function, void* user_data, GDBusDestroyFunction destroy); - -/** - * @brief Add a signal monitor to the DBus connection - * - * @param connection: Pointer to DBusConnection, indicating the DBus connection to which - * the signal monitor is to be added - * @param sender: The name of the DBus client that sends the signal. If it is NULL, the - * signals of all clients are monitored - * @param path: The path of the signal. If it is NULL, the signals of all paths are - * monitored - * @param interface: The interface of the signal. If it is NULL, the signals of all - * interfaces are monitored - * @param member: The member of the signal, that is, the specific name of the signal. - * If it is NULL, the signals of all members are monitored - * @param function: The function called when the signal is received receives the - * following parameters: - * - DBusConnection*: DBus connection - * - const char*: The sender of the signal - * - const char*: The path of the signal - * - const char*: The interface of the signal - * - const char*: The member of the signal - * - void*: User data - * @param user_data: User data, which will be passed to the function when calling the - * function - * @param destroy: The function called when the signal monitor is removed to destroy - * user_data. If it is NULL, no action is performed. - * @return: Returns an unsigned integer representing the ID of the newly created signal - * monitor. - */ -guint dbus_add_signal_watch(GDBusWatch* watcher, - const char* sender, const char* path, - const char* interface, const char* member, - GDBusSignalFunction function, void* user_data, - GDBusDestroyFunction destroy); - -guint dbus_add_properties_watch(GDBusWatch* watcher, - const char* sender, const char* path, - const char* interface, - GDBusSignalFunction function, void* user_data, - GDBusDestroyFunction destroy); - -/** - * @brief Remove a specific watch from the specified DBus connection - * - * @param connection Pointer to DBusConnection, indicating the connection from which the - * watch is to be removed - * @param tag Indicates the unique identifier of the watch to be removed - * - * @return gboolean The function returns TRUE if executed successfully, otherwise returns - * FALSE - */ -gboolean dbus_remove_watch(GDBusWatch* watcher, guint tag); - -void dbus_remove_all_watches(GDBusWatch* watcher); - -void dbus_pending_property_success(GDBusPendingPropertySet id); -void dbus_pending_property_error_valist(GDBusPendingReply id, - const char* name, const char* format, va_list args); -void dbus_pending_property_error(GDBusPendingReply id, const char* name, - const char* format, ...); - -/* - * Note that when multiple properties for a given object path are changed - * in the same mainloop iteration, they will be grouped with the last - * property changed. If this behaviour is undesired, use - * dbus_emit_property_changed_full() with the - * G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH flag, causing the signal to ignore - * any grouping. - */ -void dbus_emit_property_changed(DBusConnection* connection, - const char* path, const char* interface, - const char* name); -void dbus_emit_property_changed_full(DBusConnection* connection, - const char* path, const char* interface, - const char* name, - GDbusPropertyChangedFlags flags); -gboolean dbus_get_properties(DBusConnection* connection, const char* path, - const char* interface, DBusMessageIter* iter); - -gboolean dbus_attach_object_manager(DBusConnection* connection); -gboolean dbus_detach_object_manager(DBusConnection* connection); - -typedef struct GDBusClient GDBusClient; -typedef struct GDBusProxy GDBusProxy; - -/** - * @brief Create a new D-Bus proxy object - * - * This function is used to create a new D-Bus proxy object. - * - * @param client Pointer to the GDBusClient object, which is an instance of the - * D-Bus client. - * @param path The path of the D-Bus proxy. - * @param interface The name of the interface to be implemented by the D-Bus proxy. - * - * @return Returns a pointer to the GDBusProxy object if it was created successfully, - * otherwise returns NULL. - */ -GDBusProxy* dbus_proxy_new(GDBusClient* client, const char* path, - const char* interface); - -/** - * @brief Increase the reference count of a D-Bus proxy - * - * This function increases the reference count of a given D-Bus proxy. - * - * @param proxy Pointer to the D-Bus proxy whose reference count is to be increased. - * @return Returns the pointer to the D-Bus proxy after the reference count is increased. - */ -GDBusProxy* dbus_proxy_ref(GDBusProxy* proxy); - -/** - * @brief Decrement the reference count of a D-Bus proxy - * - * This function decrements the reference count of a given D-Bus proxy. If the reference - * count reaches 0, the proxy will be released. - * - * @param proxy Pointer to the D-Bus proxy whose reference count is to be decremented. - */ -void dbus_proxy_unref(GDBusProxy* proxy); - -/** - * @brief Get the path of the D-Bus proxy - * - * @param proxy Pointer to GDBusProxy - * @return Return the path of the proxy - */ -const char* dbus_proxy_get_path(const GDBusProxy* proxy); - -/** - * @brief Get the interface of the D-Bus proxy - * - * @param proxy Pointer to GDBusProxy - * @return Return the interface of the proxy - */ -const char* dbus_proxy_get_interface(GDBusProxy* proxy); - -/** - * @brief Gets a property value from a D-Bus proxy object, should run - * in uv default loop. - * - * @param proxy The D-Bus proxy object to query - * @param name The name of the property to get - * @param iter Pointer to a DBusMessageIter to store the property value - * @return gboolean TRUE if successful, FALSE otherwise - * - * This function synchronously gets a property value from a D-Bus proxy object - * and stores it in the provided DBusMessageIter. The caller is responsible - * for properly handling the DBusMessageIter contents. - */ -gboolean dbus_proxy_get_property(GDBusProxy* proxy, const char* name, - DBusMessageIter* iter); - -/** - * @brief Gets a basic property value from a D-Bus proxy object (thread-safe) - * - * @param proxy The D-Bus proxy object to query - * @param name The name of the property to get - * @param value Pointer to store the property value (must match property type) - * @return gboolean TRUE if successful, FALSE otherwise - * - * This function synchronously gets a basic type property value from a D-Bus - * proxy object in a thread-safe manner. For complex struct properties, - * please use dbus_proxy_get_property_iter_cb instead. The value parameter - * must point to storage of the correct type for the property being retrieved. - */ -gboolean dbus_proxy_get_property_basic(GDBusProxy* proxy, const char* name, - void* value); - -/** - * Asynchronously gets a D-Bus proxy property with thread safety - * - * @param proxy The D-Bus proxy object to query - * @param name Name of the property to retrieve - * @param value Pointer to store the property value - * @param iter_cb Callback function to handle property iteration - * @return TRUE if property was successfully retrieved, FALSE on error - * - * @note This function handles thread synchronization automatically. - * It can be called from any thread but will block if not in default loop. - */ -gboolean dbus_proxy_get_property_iter_cb(GDBusProxy* proxy, const char* name, - void* value, GDBusPropIterFunction iter_cb); - -GDBusProxy* dbus_proxy_lookup(void* list, int* index, const char* path, - const char* interface); -char* dbus_proxy_path_lookup(void* list, int* index, const char* path); - -gboolean dbus_proxy_refresh_property(GDBusProxy* proxy, const char* name); - -typedef void (*GDBusResultFunction)(const DBusError* error, void* user_data); - -/** - * @brief This function is used to set the properties of a D-Bus proxy. - * - * @param proxy Pointer to GDBusProxy, indicating the proxy to set the properties. - * @param name The name of the property, which should be a string type. - * @param type The type of the property, which should be an integer, indicating the data - * type of the property. - * @param value Pointer to the value of the property, which should be a void type, - * indicating the value of the property. - * @param function Pointer to GDBusResultFunction, indicating the function to be executed - * after setting the property. - * @param user_data Pointer to user data, which will be passed when executing - * the @param function function. - * @param destroy Pointer to GDBusDestroyFunction, indicating how to destroy the data - * pointed to by value when the set property is no longer needed. - * - * @return Returns a true value of type gboolean if the property is successfully set; - * otherwise, returns a false value. - */ -gboolean dbus_proxy_set_property_basic(GDBusProxy* proxy, - const char* name, int type, const void* value, - GDBusResultFunction function, void* user_data, - GDBusDestroyFunction destroy); - -gboolean dbus_proxy_set_property_array(GDBusProxy* proxy, - const char* name, int type, const void* value, - size_t size, GDBusResultFunction function, - void* user_data, GDBusDestroyFunction destroy); - -void dbus_dict_append_entry(DBusMessageIter* dict, - const char* key, int type, void* val); -void dbus_dict_append_basic_array(DBusMessageIter* dict, int key_type, - const void* key, int type, void* val, - int n_elements); -void dbus_dict_append_array(DBusMessageIter* dict, - const char* key, int type, void* val, - int n_elements); - -typedef void (*GDBusSetupFunction)(DBusMessageIter* iter, void* user_data); -typedef void (*GDBusReturnFunction)(DBusMessage* message, void* user_data); - -/** - * @brief Calls the specified @method method on the given D-Bus proxy - * - * @param proxy Pointer to GDBusProxy, indicating the D-Bus proxy to be called - * @param method The name of the method to be called - * @param setup A GDBusSetupFunction, used to set the parameters of the method call - * @param function A GDBusReturnFunction, called after the method call succeeds, - * returns the return value of the method - * @param user_data User data, passed to GDBusReturnFunction - * @param destroy A GDBusDestroyFunction, called after the method call is completed, - * used to clean up resources - * - * @return Returns gboolean if the method call succeeds, otherwise returns gboolean - */ -gboolean dbus_proxy_method_call(GDBusProxy* proxy, const char* method, - GDBusSetupFunction setup, - GDBusReturnFunction function, void* user_data, - GDBusDestroyFunction destroy); - -typedef void (*GDBusClientFunction)(GDBusClient* client, void* user_data); -typedef void (*GDBusProxyFunction)(GDBusProxy* proxy, void* user_data); -typedef gboolean (*GDBusProxyPropertyFilterFunction)(GDBusProxy* proxy, void* user_data); -typedef void (*GDBusPropertyFunction)(GDBusProxy* proxy, const char* name, - DBusMessageIter* iter, void* user_data); -typedef gboolean (*GDBusProxyFilterFunction)(const char* path, - const char* interface, void* user_data); - -/** - * @brief Sets a property monitor for the D-Bus proxy. - * - * @param proxy the GDBusProxy object which the property monitor is to be set. - * @param function Pointer to the callback function that handles property changes. - * @param user_data User data, which will be passed to the callback function. - * @return Returns TRUE if the monitor was successfully set, otherwise returns FALSE. - */ -gboolean dbus_proxy_set_property_watch(GDBusProxy* proxy, - GDBusPropertyFunction function, void* user_data); - -/** - * @brief Removes a property monitor from a D-Bus proxy. - * - * @param proxy the GDBusProxy object whose property monitor is to be removed. - * @param destroy Pointer to a destruction function that handles user data. - * @return Returns TRUE if the monitor was successfully removed, otherwise returns FALSE. - */ -gboolean dbus_proxy_remove_property_watch(GDBusProxy* proxy, - GDBusDestroyFunction destroy); - -gboolean dbus_proxy_set_removed_watch(GDBusProxy* proxy, - GDBusProxyFunction destroy, void* user_data); - -/** - * @brief Create a new DBus client instance - * - * This function is used to create a new DBus client instance. - * - * @param connection Pointer to DBus connection. - * @param service Service name, used to identify DBus service. - * @param path Path, used to identify the path of DBus object. - * - * @return Returns a pointer to the newly created GDBusClient instance. - */ -GDBusClient* dbus_client_new(DBusConnection* connection, - const char* service, const char* path); - -GDBusClient* dbus_client_new_full(DBusConnection* connection, - const char* service, - const char* path, - const char* root_path); - -/** - * @brief Increase the reference count of the GDBusClient object. - * - * @param client The GDBusClient object whose reference count is to be increased. - * @return The GDBusClient object after the reference count is increased. - */ -GDBusClient* dbus_client_ref(GDBusClient* client); - -/** - * @brief Decrement the reference count of a GDBusClient object. - * - * @param client The GDBusClient object whose reference count is to be decremented. - */ -void dbus_client_unref(GDBusClient* client); - -/** - * @brief Set the connection monitoring function of the D-Bus client. - * - * @param client Pointer to the GDBusClient instance. - * @param function Pointer to the function of type GDBusWatchFunction, which will be - * called when there is a new D-Bus connection. - * @param user_data caller data, which will be passed to the caller when callback - * @return If the setting is successful, return a true value of type gboolean; otherwise, - * return a false value. - */ -gboolean dbus_client_set_connect_watch(GDBusClient* client, GDBusWatchFunction function, - void* user_data); - -/** - * @brief Set the disconnection monitoring function of the D-Bus client. - * - * @param client Pointer to the GDBusClient instance. - * @param function Pointer to the function of type GDBusWatchFunction, which will be - * called when the D-Bus connection is disconnected. - * @param user_data caller data will be passed to the caller when calling the function. - * @return If the setting is successful, return a true value of type gboolean; otherwise, - * return a false value. - */ -gboolean dbus_client_set_disconnect_watch(GDBusClient* client, - GDBusWatchFunction function, void* user_data); - -/** - * @brief Set the signal monitoring function of the D-Bus client - * - * This function is used to set the signal monitoring function of the D-Bus client. - * When the client receives a D-Bus signal, the function will be called. - * - * @param client Pointer to the D-Bus client - * @param function Points to the function that processes the D-Bus signal - * @param user_data caller data will be passed to the caller when calling the function - * - * @return If the setting is successful, return gboolean as TRUE, otherwise return FALSE - */ -gboolean dbus_client_set_signal_watch(GDBusClient* client, - GDBusMessageFunction function, void* user_data); - -/** - * @brief Set the preparation function and user data of the DBus client - * - * This function is used to set the preparation function and user data of the DBus client. - * The preparation function will be called when the DBus client is ready, and the user - * data can be passed to this function. - * - * @param client Pointer to the DBus client - * @param ready Preparation function, which will be called when the DBus client is ready - * @param user_data User data, which can be passed to the preparation function - * - * @return gboolean If the setting is successful, it returns TRUE, otherwise it returns FALSE - */ -gboolean dbus_client_set_ready_watch(GDBusClient* client, - GDBusClientFunction ready, void* user_data); - -/** - * @brief Set the proxy handler function - * - * This function is used to set the proxy handler function of the D-Bus client. - * - * @param client Pointer to the GDBusClient instance. - * @param proxy_added Callback function when the proxy is added. - * @param proxy_removed Callback function when the proxy is removed. - * @param proxy_property_filter Callback function for proxy property filtering. - * @param property_changed Callback function when the property is changed. - * @param user_data caller data, which will be used in the callback function. - * @return If the setting is successful, the return gboolean is TRUE, otherwise - * it returns FALSE. - */ -gboolean dbus_client_set_proxy_handlers(GDBusClient* client, - GDBusProxyFunction proxy_added, - GDBusProxyFunction proxy_removed, - GDBusProxyPropertyFilterFunction proxy_property_filter, - GDBusPropertyFunction property_changed, - void* user_data); - -/** - * @brief Set the proxy filter function - * - * This function is used to set the proxy filter function of the D-Bus client. - * - * @param client Pointer to the GDBusClient instance. - * @param proxy_filter Callback function of the proxy filter. - * @param user_data caller data, which will be used in the callback function. - * @return If the setting is successful, the return gboolean is TRUE, otherwise - * it returns FALSE. - */ -gboolean dbus_client_set_proxy_filter(GDBusClient* client, - GDBusProxyFilterFunction proxy_filter, - void* user_data); - -/** - * Adds a service watch for the specified name. - * @param client The DBus client instance - * @param name The service name to watch - * @param connect Callback when service connects - * @param disconnect Callback when service disconnects - * @param user_data User data passed to callbacks - * @param destroy Destroy notification callback - * @return Watch ID - */ -guint dbus_client_add_service_watch(GDBusClient* client, const char* name, - GDBusWatchFunction connect, GDBusWatchFunction disconnect, - void* user_data, GDBusDestroyFunction destroy); - -/** - * Adds a watch for service disconnection only. - * @param client The DBus client instance - * @param name The service name to watch - * @param function Callback when service disconnects - * @param user_data User data passed to callback - * @param destroy Destroy notification callback - * @return Watch ID - */ -guint dbus_client_add_service_disconnect_watch(GDBusClient* client, const char* name, - GDBusWatchFunction function, void* user_data, GDBusDestroyFunction destroy); - -/** - * Adds a signal watch with detailed matching criteria. - * @param client The DBus client instance - * @param sender The sender name to match - * @param path The object path to match - * @param interface The interface name to match - * @param member The member name to match - * @param function Callback when signal is received - * @param user_data User data passed to callback - * @param destroy Destroy notification callback - * @return Watch ID - */ -guint dbus_client_add_signal_watch(GDBusClient* client,const char* sender, - const char* path, const char* interface, const char* member, - GDBusSignalFunction function, void* user_data, GDBusDestroyFunction destroy); - -/** - * Adds a properties change watch. - * @param client The DBus client instance - * @param sender The sender name to match - * @param path The object path to match - * @param interface The interface name to match - * @param function Callback when properties change - * @param user_data User data passed to callback - * @param destroy Destroy notification callback - * @return Watch ID - */ -guint dbus_client_add_properties_watch(GDBusClient* client, - const char* sender, const char* path, const char* interface, - GDBusSignalFunction function, void* user_data, GDBusDestroyFunction destroy); - -/** - * Removes a previously added watch. - * @param client The DBus client instance - * @param tag The watch ID to remove - * @return TRUE if watch was found and removed - */ -gboolean dbus_client_remove_watch(GDBusClient* client, guint tag); - -/** - * Removes all watches from the client. - * @param client The DBus client instance - */ -void dbus_client_remove_all_watches(GDBusClient* client); - -/** - * Adds a watch for client disconnection. - * @param client The DBus client instance - * @param function Callback when client disconnects - * @param user_data User data passed to callback - * @param destroy Destroy notification callback - * @return Watch ID - */ -gboolean dbus_client_add_disconnect_watch(GDBusClient* client, - GDBusWatchFunction function, void* user_data, DBusFreeFunction destroy); - - -#ifdef __cplusplus -} -#endif - -#endif /* __GDBUS_H */ diff --git a/gdbus/mainloop.c b/gdbus/mainloop.c deleted file mode 100644 index 6c53102..0000000 --- a/gdbus/mainloop.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright (c) 2024 Xiaomi Technologies Co., Ltd. - * All rights reserved. - * - * This file is part of the Xiaomi project. - * - * This source code is 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 -#include -#include -#include - -#include "gdbus-internal.h" - -struct idle_handler { - uv_idle_t handle; - DBusConnection* conn; -}; - -struct timeout_handler { - uv_timer_t handle; - DBusTimeout* timeout; -}; - -struct watch_info { - uv_poll_t handle; - DBusWatch* watch; - DBusConnection* conn; -}; - -static void close_cb(uv_handle_t* handle) -{ - free(handle->data); -} - -static void message_dispatch(uv_idle_t* handle) -{ - struct idle_handler* handler = handle->data; - DBusConnection* conn = handler->conn; - - /* Dispatch messages */ - while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS) - ; - - dbus_connection_unref(conn); - - uv_close((uv_handle_t*)handle, close_cb); -} - -static inline void queue_dispatch(DBusConnection* conn, - DBusDispatchStatus status) -{ - if (status == DBUS_DISPATCH_DATA_REMAINS) { - struct idle_handler* handler; - - handler = calloc(1, sizeof(struct idle_handler)); - if (handler == NULL) { - return; - } - - if (uv_idle_init(uv_default_loop(), &handler->handle) != 0) { - free(handler); - return; - } - - handler->conn = dbus_connection_ref(conn); - handler->handle.data = handler; - if (uv_idle_start(&handler->handle, message_dispatch) != 0) { - dbus_connection_unref(conn); - uv_close((uv_handle_t*)&handler->handle, close_cb); - free(handler); - } - } -} - -static void watch_func(uv_poll_t* handle, int state, int events) -{ - struct watch_info* info = handle->data; - unsigned int flags = 0; - DBusDispatchStatus status; - DBusConnection* conn; - - if (events & UV_READABLE) - flags |= DBUS_WATCH_READABLE; - if (events & UV_WRITABLE) - flags |= DBUS_WATCH_WRITABLE; - if (events & UV_DISCONNECT) - flags |= DBUS_WATCH_HANGUP; - if (events & POLLERR) - flags |= DBUS_WATCH_ERROR; - - /* Protect connection from being destroyed by dbus_watch_handle */ - conn = dbus_connection_ref(info->conn); - - dbus_watch_handle(info->watch, flags); - - status = dbus_connection_get_dispatch_status(conn); - queue_dispatch(conn, status); - - dbus_connection_unref(conn); -} - -static void watch_info_free(void* data) -{ - struct watch_info* info = data; - - dbus_connection_unref(info->conn); - uv_close((uv_handle_t*)&info->handle, close_cb); -} - -static dbus_bool_t add_watch(DBusWatch* watch, void* data) -{ - DBusConnection* conn = data; - int cond = UV_DISCONNECT; - struct watch_info* info; - unsigned int flags; - int fd; - - if (!dbus_watch_get_enabled(watch)) - return TRUE; - - info = calloc(1, sizeof(struct watch_info)); - if (info == NULL) - return FALSE; - - fd = dbus_watch_get_unix_fd(watch); - - info->watch = watch; - info->conn = dbus_connection_ref(conn); - - dbus_watch_set_data(watch, info, watch_info_free); - - flags = dbus_watch_get_flags(watch); - - if (flags & DBUS_WATCH_READABLE) - cond |= UV_READABLE; - if (flags & DBUS_WATCH_WRITABLE) - cond |= UV_WRITABLE; - - if (uv_poll_init(uv_default_loop(), &info->handle, fd) != 0) { - free(info); - goto errout; - } - - info->handle.data = info; - if (uv_poll_start(&info->handle, cond, watch_func) != 0) { - uv_close((uv_handle_t*)&info->handle, close_cb); - goto errout; - } - - return TRUE; -errout: - dbus_connection_unref(conn); - return FALSE; -} - -static void remove_watch(DBusWatch* watch, void* data) -{ - if (dbus_watch_get_enabled(watch)) - return; - - /* will trigger watch_info_free() */ - dbus_watch_set_data(watch, NULL, NULL); -} - -static void watch_toggled(DBusWatch* watch, void* data) -{ - /* Because we just exit on OOM, enable/disable is - * no different from add/remove */ - if (dbus_watch_get_enabled(watch)) - add_watch(watch, data); - else - remove_watch(watch, data); -} - -static void timeout_handler_dispatch(uv_timer_t* handle) -{ - struct timeout_handler* handler = handle->data; - - /* if not enabled should not be polled by the main loop */ - if (dbus_timeout_get_enabled(handler->timeout)) - dbus_timeout_handle(handler->timeout); -} - -static void timeout_handler_free(void* data) -{ - struct timeout_handler* handler = data; - - uv_close((uv_handle_t*)&handler->handle, close_cb); -} - -static dbus_bool_t add_timeout(DBusTimeout* timeout, void* data) -{ - int interval = dbus_timeout_get_interval(timeout); - struct timeout_handler* handler; - - if (!dbus_timeout_get_enabled(timeout)) - return TRUE; - - handler = calloc(1, sizeof(struct timeout_handler)); - if (handler == NULL) - return FALSE; - - handler->timeout = timeout; - - dbus_timeout_set_data(timeout, handler, timeout_handler_free); - - if (uv_timer_init(uv_default_loop(), &handler->handle) != 0) { - goto errout; - } - - handler->handle.data = handler; - if (uv_timer_start(&handler->handle, timeout_handler_dispatch, interval, 0) != 0) { - uv_close((uv_handle_t*)&handler->handle, close_cb); - goto errout; - } - - return TRUE; -errout: - free(handler); - dbus_timeout_set_data(timeout, NULL, NULL); - return FALSE; -} - -static void remove_timeout(DBusTimeout* timeout, void* data) -{ - /* will trigger timeout_handler_free() */ - dbus_timeout_set_data(timeout, NULL, NULL); -} - -static void timeout_toggled(DBusTimeout* timeout, void* data) -{ - if (dbus_timeout_get_enabled(timeout)) - add_timeout(timeout, data); - else - remove_timeout(timeout, data); -} - -static void dispatch_status(DBusConnection* conn, - DBusDispatchStatus status, void* data) -{ - if (!dbus_connection_get_is_connected(conn)) - return; - - queue_dispatch(conn, status); -} - -static inline void setup_dbus_with_main_loop(DBusConnection* conn) -{ - dbus_connection_set_watch_functions(conn, add_watch, remove_watch, - watch_toggled, conn, NULL); - - dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, - timeout_toggled, NULL, NULL); - - dbus_connection_set_dispatch_status_function(conn, dispatch_status, - NULL, NULL); -} - -static gboolean setup_bus(DBusConnection* conn, const char* name, - DBusError* error) -{ - gboolean result; - DBusDispatchStatus status; - - if (name != NULL) { - result = dbus_request_name(conn, name, error); - - if (error != NULL) { - if (dbus_error_is_set(error) == TRUE) - return FALSE; - } - - if (result == FALSE) - return FALSE; - } - - setup_dbus_with_main_loop(conn); - - status = dbus_connection_get_dispatch_status(conn); - queue_dispatch(conn, status); - - return TRUE; -} - -DBusConnection* dbus_setup_bus(DBusBusType type, const char* name, - DBusError* error) -{ - DBusConnection* conn; - - conn = dbus_bus_get(type, error); - - if (error != NULL) { - if (dbus_error_is_set(error) == TRUE) - return NULL; - } - - if (conn == NULL) - return NULL; - - if (setup_bus(conn, name, error) == FALSE) { - dbus_connection_unref(conn); - return NULL; - } - - return conn; -} - -DBusConnection* dbus_setup_private(DBusBusType type, const char* name, - DBusError* error) -{ - DBusConnection* conn; - - conn = dbus_bus_get_private(type, error); - - if (error != NULL) { - if (dbus_error_is_set(error) == TRUE) - return NULL; - } - - if (conn == NULL) - return NULL; - - if (setup_bus(conn, name, error) == FALSE) { - dbus_connection_close(conn); - dbus_connection_unref(conn); - return NULL; - } - - return conn; -} - -gboolean dbus_request_name(DBusConnection* connection, const char* name, - DBusError* error) -{ - int result; - - result = dbus_bus_request_name(connection, name, - DBUS_NAME_FLAG_DO_NOT_QUEUE, error); - - if (error != NULL) { - if (dbus_error_is_set(error) == TRUE) - return FALSE; - } - - if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - if (error != NULL) - dbus_set_error(error, name, "Name already in use"); - - return FALSE; - } - - return TRUE; -} - -struct disconnect_data { - GDBusWatchFunction function; - void* user_data; - GDBusWatch* watcher; -}; - -static gboolean disconnected_signal(DBusConnection* conn, - DBusMessage* msg, void* data) -{ - struct disconnect_data* dc_data = data; - - info("Got disconnected from the system message bus"); - - dc_data->function(conn, dc_data->user_data); - - dbus_connection_unref(conn); - - dbus_watch_set_connection_state(dc_data->watcher, TRUE); - - return FALSE; -} - -gboolean dbus_client_add_disconnect_watch(GDBusClient* client, - GDBusWatchFunction function, void* user_data, DBusFreeFunction destroy) -{ - struct disconnect_data* dc_data; - - dbus_connection_set_exit_on_disconnect(client->dbus_conn, FALSE); - - dc_data = calloc(1, sizeof(struct disconnect_data)); - if (dc_data == NULL) - return FALSE; - - dc_data->function = function; - dc_data->user_data = user_data; - dc_data->watcher = client->watcher; - - if (dbus_add_signal_watch(client->watcher, NULL, NULL, - DBUS_INTERFACE_LOCAL, "Disconnected", - disconnected_signal, dc_data, free) - == 0) { - error("Failed to add watch for D-Bus Disconnected signal"); - free(dc_data); - return FALSE; - } - - dbus_connection_ref(client->dbus_conn); - return TRUE; -} diff --git a/gdbus/object.c b/gdbus/object.c deleted file mode 100644 index c3b6f0e..0000000 --- a/gdbus/object.c +++ /dev/null @@ -1,2055 +0,0 @@ -/* - * Copyright (C) 2025 Xiaomi Corporation - * - * 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 -#include - -#include -#include -#include -#include - -#ifdef __NuttX__ -#include -#include -#endif - -#include "gdbus-internal.h" - -#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager" - -#ifndef DBUS_ERROR_UNKNOWN_PROPERTY -#define DBUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty" -#endif - -#ifndef DBUS_ERROR_PROPERTY_READ_ONLY -#define DBUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly" -#endif - -#define DBUS_ERROR_OOM "No memory" - -#define G_DBUS_ANNOTATE(name_, value_) \ - "" - -#define G_DBUS_ANNOTATE_DEPRECATED \ - G_DBUS_ANNOTATE("Deprecated", "true") - -#define G_DBUS_ANNOTATE_NOREPLY \ - G_DBUS_ANNOTATE("Method.NoReply", "true") - -struct generic_data { - unsigned int refcount; - DBusConnection* conn; - char* path; - DBusList* interfaces; - DBusList* objects; - DBusList* added; - DBusList* removed; - gboolean pending_prop; - char* introspect; - struct generic_data* parent; - uv_idle_t handle; -}; - -struct interface_data { - char* name; - const GDBusMethodTable* methods; - const GDBusSignalTable* signals; - const GDBusPropertyTable* properties; - DBusList* pending_prop; - void* user_data; - GDBusDestroyFunction destroy; -}; - -struct security_data { - GDBusPendingReply pending; - DBusMessage* message; - const GDBusMethodTable* method; - void* iface_user_data; -}; - -struct property_data { - DBusConnection* conn; - GDBusPendingPropertySet id; - DBusMessage* message; -}; - -typedef struct { - int global_flags; - struct generic_data* root; - DBusList* generic_pending; - - GDBusPendingReply next_pending; - DBusList* pending_security; - - const GDBusSecurityTable* security_table; - GDBusPendingPropertySet next_pending_property; - DBusList* pending_property_set; -} gdbus_object_globals; - -static void process_changes(uv_idle_t* handle); -static void process_properties_from_interface(struct generic_data* data, - struct interface_data* iface); -static void process_property_changes(struct generic_data* data); - -#if defined(CONFIG_BUILD_FLAT) || defined(CONFIG_BUILD_PROTECTED) -/* TLS index for gdbus_object_t */ -static int gdbus_object_tls_index; - -/* Init once only by uv_once */ -static void gdbus_object_index_alloc(void) -{ - gdbus_object_tls_index = task_tls_alloc(dbus_free); - - ASSERT(gdbus_object_tls_index >= 0); -} - -static gdbus_object_globals* gdbus_object_get(void) -{ - static pthread_once_t once_guard = PTHREAD_ONCE_INIT; - gdbus_object_globals* gdbus_object = NULL; - - pthread_once(&once_guard, gdbus_object_index_alloc); - - gdbus_object = (gdbus_object_globals*)task_tls_get_value(gdbus_object_tls_index); - if (gdbus_object == NULL) { - gdbus_object = calloc(1, sizeof(gdbus_object_globals)); - if (gdbus_object != NULL) { - gdbus_object->global_flags = 0; - gdbus_object->root = NULL; - gdbus_object->generic_pending = NULL; - gdbus_object->next_pending = 1; - gdbus_object->pending_security = NULL; - gdbus_object->security_table = NULL; - gdbus_object->next_pending_property = 1; - gdbus_object->pending_property_set = NULL; - task_tls_set_value(gdbus_object_tls_index, (uintptr_t)gdbus_object); - } - } - - _dbus_assert(gdbus_object != NULL); - return gdbus_object; -} -#else -/* Kernel build should use gdbus_object */ -static gdbus_object_globals* gdbus_object_get(void) -{ - static gdbus_object_globals gdbus_object = { - .global_flags = 0, - .root = NULL, - .generic_pending = NULL, - .next_pending = 1, - .pending_security = NULL, - .security_table = NULL, - .next_pending_property = 1, - .pending_property_set = NULL, - }; - - return &gdbus_object; -} -#endif - -#define global_flags gdbus_object_get()->global_flags -#define root gdbus_object_get()->root -#define generic_pending gdbus_object_get()->generic_pending -#define next_pending gdbus_object_get()->next_pending -#define pending_security gdbus_object_get()->pending_security -#define security_table gdbus_object_get()->security_table -#define next_pending_property gdbus_object_get()->next_pending_property -#define pending_property_set gdbus_object_get()->pending_property_set - -static int strcmp0(const char* str1, const char* str2) -{ - if (str1 == NULL) - return -(str1 != str2); - - if (str2 == NULL) - return -(str1 != str2); - - return strcmp(str1, str2); -} - -static char* strdup0(const char* str) -{ - if (str) - return strdup(str); - - return NULL; -} - -static void append_arguments_printf(DBusString* str, const GDBusArgInfo* args, - const char* direction) -{ - while (args && args->name) { - _dbus_string_append_printf(str, - "name, args->signature); - - if (direction) - _dbus_string_append_printf(str, - " direction=\"%s\"/>\n", direction); - else - _dbus_string_append_printf(str, "/>\n"); - - args++; - } -} - -static gboolean check_experimental(int flags, int flag) -{ - if (!(flags & flag)) - return FALSE; - - return !(global_flags & G_DBUS_FLAG_ENABLE_EXPERIMENTAL); -} - -static void append_annotation(DBusString* str, int flags, - int flag_type, const char* append_annotate) -{ - if (flags & flag_type) { - _dbus_string_append(str, append_annotate); - } -} - -static void generate_method_xml(DBusString* str, const GDBusMethodTable* method) -{ - if (check_experimental(method->flags, G_DBUS_METHOD_FLAG_EXPERIMENTAL)) - return; - - _dbus_string_append_printf(str, "", method->name); - append_arguments_printf(str, method->in_args, "in"); - append_arguments_printf(str, method->out_args, "out"); - - append_annotation(str, method->flags, G_DBUS_METHOD_FLAG_DEPRECATED, - G_DBUS_ANNOTATE_DEPRECATED); - append_annotation(str, method->flags, G_DBUS_METHOD_FLAG_NOREPLY, - G_DBUS_ANNOTATE_NOREPLY); - - _dbus_string_append_printf(str, ""); -} - -static void generate_signal_xml(DBusString* str, const GDBusSignalTable* signal) -{ - if (check_experimental(signal->flags, G_DBUS_SIGNAL_FLAG_EXPERIMENTAL)) - return; - - _dbus_string_append_printf(str, "", signal->name); - append_arguments_printf(str, signal->args, NULL); - - append_annotation(str, signal->flags, G_DBUS_SIGNAL_FLAG_DEPRECATED, - G_DBUS_ANNOTATE_DEPRECATED); - _dbus_string_append_printf(str, "\n"); -} - -static void generate_property_xml(DBusString* str, const GDBusPropertyTable* property) -{ - if (check_experimental(property->flags, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) - return; - - _dbus_string_append_printf(str, "", - property->name, property->type, - property->get ? "read" : "", - property->set ? "write" : ""); - - append_annotation(str, property->flags, G_DBUS_PROPERTY_FLAG_DEPRECATED, - G_DBUS_ANNOTATE_DEPRECATED); - _dbus_string_append_printf(str, ""); -} - -static void generate_interface_xml(DBusString* str, struct interface_data* iface) -{ - const GDBusMethodTable* method = iface->methods; - while (method && method->name) { - generate_method_xml(str, method); - method++; - } - - const GDBusSignalTable* signal = iface->signals; - while (signal && signal->name) { - generate_signal_xml(str, signal); - signal++; - } - - const GDBusPropertyTable* property = iface->properties; - while (property && property->name) { - generate_property_xml(str, property); - property++; - } -} - -static void append_xml_header_node_begin(DBusString* str) -{ - _dbus_string_append_printf(str, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); - _dbus_string_append_printf(str, ""); -} - -static void append_xml_node_end(DBusString* str) -{ - _dbus_string_append_printf(str, ""); -} - -static void append_interfaces_xml(DBusString* str, DBusList* interfaces) -{ - DBusList* list = _dbus_list_get_first_link(&interfaces); - while (list != NULL) { - struct interface_data* iface = list->data; - _dbus_string_append_printf(str, "", iface->name); - generate_interface_xml(str, iface); - _dbus_string_append_printf(str, ""); - list = _dbus_list_get_next_link(&interfaces, list); - } -} - -static void append_child_nodes_xml(DBusString* str, DBusConnection* conn, const char* path) -{ - char** children = NULL; - if (dbus_connection_list_registered(conn, path, &children)) { - for (int i = 0; children[i]; i++) { - _dbus_string_append_printf(str, "", children[i]); - } - dbus_free_string_array(children); - } -} - -static void generate_introspection_xml(DBusConnection* conn, - struct generic_data* data, const char* path) -{ - DBusString str; - - free(data->introspect); - data->introspect = NULL; - - if (!_dbus_string_init(&str)) { - return; - } - - append_xml_header_node_begin(&str); - - append_interfaces_xml(&str, data->interfaces); - append_child_nodes_xml(&str, conn, path); - - append_xml_node_end(&str); - - _dbus_string_copy_data(&str, &data->introspect); - _dbus_string_free(&str); -} - -static DBusMessage* introspect(DBusConnection* connection, - DBusMessage* message, void* user_data) -{ - struct generic_data* data = user_data; - DBusMessage* reply; - - if (data->introspect == NULL) - generate_introspection_xml(connection, data, - dbus_message_get_path(message)); - - reply = dbus_message_new_method_return(message); - if (reply == NULL) - return NULL; - - dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect, - DBUS_TYPE_INVALID); - - return reply; -} - -static DBusHandlerResult process_message(DBusConnection* connection, - DBusMessage* message, const GDBusMethodTable* method, - void* iface_user_data) -{ - DBusMessage* reply; - - reply = method->function(connection, message, iface_user_data); - - if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY || dbus_message_get_no_reply(message)) { - if (reply != NULL) - dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; - } - - if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) { - if (reply == NULL) - return DBUS_HANDLER_RESULT_HANDLED; - } - - if (reply == NULL) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - dbus_send_message(connection, reply); - - return DBUS_HANDLER_RESULT_HANDLED; -} - -void dbus_pending_success(DBusConnection* connection, - GDBusPendingReply pending_reply) -{ - DBusList* list = _dbus_list_get_first_link(&pending_security); - - while (list) { - struct security_data* secdata = list->data; - list = _dbus_list_get_next_link(&pending_security, list); - - if (secdata->pending != pending_reply) - continue; - - _dbus_list_remove(&pending_security, secdata); - - process_message(connection, secdata->message, - secdata->method, secdata->iface_user_data); - - dbus_message_unref(secdata->message); - free(secdata); - return; - } -} - -void dbus_pending_error_valist(DBusConnection* connection, - GDBusPendingReply pending_reply, const char* name, - const char* format, va_list args) -{ - DBusList* list = _dbus_list_get_first_link(&pending_security); - - while (list != NULL) { - struct security_data* secdata = list->data; - DBusList* next = _dbus_list_get_next_link(&pending_security, list); - - if (secdata->pending == pending_reply) { - _dbus_list_remove(&pending_security, secdata); - dbus_send_error_valist(connection, secdata->message, name, format, args); - dbus_message_unref(secdata->message); - free(secdata); - return; - } - - list = next; - } -} - -void dbus_pending_error(DBusConnection* connection, - GDBusPendingReply pending_reply, - const char* name, const char* format, ...) -{ - va_list args; - - va_start(args, format); - - dbus_pending_error_valist(connection, pending_reply, name, format, args); - - va_end(args); -} - -struct builtin_security_data { - DBusConnection* conn; - GDBusPendingReply pending; -}; - -static void builtin_security_result(dbus_bool_t authorized, void* user_data) -{ - struct builtin_security_data* data = user_data; - - if (authorized == TRUE) - dbus_pending_success(data->conn, data->pending); - else - dbus_pending_error(data->conn, data->pending, - DBUS_ERROR_AUTH_FAILED, NULL); - - free(data); -} - -static void builtin_security_function(DBusConnection* conn, - const char* action, - gboolean interaction, - GDBusPendingReply pending_reply) -{ - struct builtin_security_data* data; - - data = calloc(1, sizeof(struct builtin_security_data)); - if (data == NULL) { - dbus_pending_error(conn, pending_reply, DBUS_ERROR_NO_MEMORY, - "Failed to allocate memory for security data"); - return; - } - data->conn = conn; - data->pending = pending_reply; - - if (dbus_polkit_check_authorization(conn, action, interaction, - builtin_security_result, data, 30000) - < 0) - dbus_pending_error(conn, pending_reply, NULL, NULL); -} - -static gboolean check_privilege(DBusConnection* conn, DBusMessage* msg, - const GDBusMethodTable* method, void* iface_user_data) -{ - gboolean interaction = FALSE; - const GDBusSecurityTable* security = security_table; - - while (security && security->privilege) { - if (security->privilege == method->privilege) { - struct security_data* secdata = calloc(1, sizeof(struct security_data)); - if (secdata == NULL) { - error("Failed to allocate memory for security data"); - return FALSE; - } - secdata->pending = next_pending++; - secdata->message = dbus_message_ref(msg); - secdata->method = method; - secdata->iface_user_data = iface_user_data; - - _dbus_list_prepend(&pending_security, secdata); - - interaction = (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION) - ? TRUE - : FALSE; - - if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) && security->function) { - security->function(conn, security->action, interaction, secdata->pending); - } else { - builtin_security_function(conn, security->action, interaction, - secdata->pending); - } - - return TRUE; - } - security++; - } - - return FALSE; -} - -static struct property_data* remove_pending_property_data(GDBusPendingPropertySet id) -{ - DBusList* l = _dbus_list_get_first_link(&pending_property_set); - - while (l != NULL) { - struct property_data* propdata = l->data; - if (propdata->id == id) { - _dbus_list_remove_link(&pending_property_set, l); - return propdata; - } - l = _dbus_list_get_next_link(&pending_property_set, l); - } - - return NULL; -} - -void dbus_pending_property_success(GDBusPendingPropertySet id) -{ - struct property_data* propdata; - - propdata = remove_pending_property_data(id); - if (propdata == NULL) - return; - - dbus_send_reply(propdata->conn, propdata->message, - DBUS_TYPE_INVALID); - dbus_message_unref(propdata->message); - free(propdata); -} - -void dbus_pending_property_error_valist(GDBusPendingReply id, - const char* name, const char* format, - va_list args) -{ - struct property_data* propdata; - - propdata = remove_pending_property_data(id); - if (propdata == NULL) - return; - - dbus_send_error_valist(propdata->conn, propdata->message, name, - format, args); - - dbus_message_unref(propdata->message); - free(propdata); -} - -void dbus_pending_property_error(GDBusPendingReply id, const char* name, - const char* format, ...) -{ - va_list args; - - va_start(args, format); - - dbus_pending_property_error_valist(id, name, format, args); - - va_end(args); -} - -static void reset_parent(gpointer data, gpointer user_data) -{ - struct generic_data* child = data; - struct generic_data* parent = user_data; - - child->parent = parent; -} - -static void append_property(struct interface_data* iface, - const GDBusPropertyTable* p, DBusMessageIter* dict) -{ - DBusMessageIter entry, value; - - dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, - &entry); - dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &p->name); - dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, p->type, - &value); - - p->get(p, &value, iface->user_data); - - dbus_message_iter_close_container(&entry, &value); - dbus_message_iter_close_container(dict, &entry); -} - -static void append_properties(struct interface_data* data, DBusMessageIter* iter) -{ - DBusMessageIter dict; - const GDBusPropertyTable* p = data->properties; - - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING - DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, - &dict); - - while (p && p->name) { - if (!check_experimental(p->flags, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL) - && p->get != NULL - && (p->exists == NULL || p->exists(p, data->user_data))) { - append_property(data, p, &dict); - } - p++; - } - - dbus_message_iter_close_container(iter, &dict); -} - -static void append_interface(gpointer data, gpointer user_data) -{ - struct interface_data* iface = data; - DBusMessageIter* array = user_data; - DBusMessageIter entry; - - dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL, - &entry); - dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &iface->name); - append_properties(data, &entry); - dbus_message_iter_close_container(array, &entry); -} - -static void emit_interfaces_added(struct generic_data* data) -{ - DBusMessage* signal; - DBusMessageIter iter, array; - - if (root == NULL || data == root) - return; - - /* Emit InterfacesAdded on the parent first so it appears first on the - * bus as child objects may point to it. - */ - if (data->parent && data->parent->added) - emit_interfaces_added(data->parent); - - signal = dbus_message_new_signal(root->path, - DBUS_INTERFACE_OBJECT_MANAGER, - "InterfacesAdded"); - if (signal == NULL) - return; - - dbus_message_iter_init_append(signal, &iter); - dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, - &data->path); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING - DBUS_TYPE_ARRAY_AS_STRING - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING - DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, - &array); - - _dbus_list_foreach(&data->added, append_interface, &array); - _dbus_list_clear(&data->added); - - dbus_message_iter_close_container(&iter, &array); - - /* Use dbus_connection_send to avoid recursive calls to dbus_flush */ - dbus_connection_send(data->conn, signal, NULL); - dbus_message_unref(signal); -} - -static struct interface_data* find_interface(DBusList* interfaces, - const char* name) -{ - DBusList* list = _dbus_list_get_first_link(&interfaces); - - if (name == NULL) - return NULL; - - while (list != NULL) { - struct interface_data* iface = list->data; - if (!strcmp0(name, iface->name)) - return iface; - - list = _dbus_list_get_next_link(&interfaces, list); - } - - return NULL; -} - -static gboolean dbus_args_have_signature(const GDBusArgInfo* args, - DBusMessage* message) -{ - const char* sig = dbus_message_get_signature(message); - const char* p = NULL; - - while (args && args->signature && *sig) { - p = args->signature; - - while (*sig && *p) { - if (*p++ != *sig++) { - return FALSE; - } - } - args++; - } - - return !(*sig || (p && *p) || (args && args->signature)); -} - -static void add_pending(struct generic_data* data) -{ - if (uv_is_active((const uv_handle_t*)&data->handle) != 0) - return; - - if (uv_idle_start(&data->handle, process_changes) == 0) { - return; - } - - _dbus_list_append(&generic_pending, data); -} - -static gboolean remove_interface(struct generic_data* data, const char* name) -{ - struct interface_data* iface; - - iface = find_interface(data->interfaces, name); - if (iface == NULL) - return FALSE; - - process_properties_from_interface(data, iface); - - _dbus_list_remove(&data->interfaces, iface); - - if (iface->destroy) { - iface->destroy(iface->user_data); - iface->user_data = NULL; - } - - /* - * Interface being removed was just added, on the same mainloop - * iteration? Don't send any signal - */ - if (_dbus_list_find_last(&data->added, iface)) { - _dbus_list_remove(&data->added, iface); - free(iface->name); - free(iface); - return TRUE; - } - - if (data->parent == NULL) { - free(iface->name); - free(iface); - return TRUE; - } - - _dbus_list_prepend(&data->removed, iface->name); - free(iface); - - add_pending(data); - - return TRUE; -} - -static struct generic_data* invalidate_parent_data(DBusConnection* conn, - const char* child_path) -{ - struct generic_data *data = NULL, *child = NULL, *parent = NULL; - char *parent_path, *slash; - - parent_path = strdup0(child_path); - slash = strrchr(parent_path, '/'); - if (slash == NULL) - goto done; - - if (slash == parent_path && parent_path[1] != '\0') - parent_path[1] = '\0'; - else - *slash = '\0'; - - if (!strlen(parent_path)) - goto done; - - if (dbus_connection_get_object_path_data(conn, parent_path, (void*)&data) == FALSE) { - goto done; - } - - parent = invalidate_parent_data(conn, parent_path); - - if (data == NULL) { - data = parent; - if (data == NULL) - goto done; - } - - free(data->introspect); - data->introspect = NULL; - - if (!dbus_connection_get_object_path_data(conn, child_path, (void*)&child)) - goto done; - - if (child == NULL || _dbus_list_find_last(&data->objects, child) != NULL) - goto done; - - _dbus_list_prepend(&data->objects, child); - child->parent = data; - -done: - free(parent_path); - return data; -} - -static inline const GDBusPropertyTable* find_property(const GDBusPropertyTable* properties, - const char* name) -{ - const GDBusPropertyTable* p; - - for (p = properties; p && p->name; p++) { - if (strcmp0(name, p->name) != 0) - continue; - - if (check_experimental(p->flags, - G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) - break; - - return p; - } - - return NULL; -} - -static DBusMessage* properties_get(DBusConnection* connection, - DBusMessage* message, void* user_data) -{ - struct generic_data* data = user_data; - struct interface_data* iface; - const GDBusPropertyTable* property; - const char *interface, *name; - DBusMessageIter iter, value; - DBusMessage* reply; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) - return NULL; - - iface = find_interface(data->interfaces, interface); - if (iface == NULL) - return dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, - "No such interface '%s'", interface); - - property = find_property(iface->properties, name); - if (property == NULL) - return dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, - "No such property '%s'", name); - - if (property->exists != NULL && !property->exists(property, iface->user_data)) - return dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, - "No such property '%s'", name); - - if (property->get == NULL) - return dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, - "Property '%s' is not readable", name); - - reply = dbus_message_new_method_return(message); - if (reply == NULL) - return NULL; - - dbus_message_iter_init_append(reply, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, - property->type, &value); - - if (!property->get(property, &value, iface->user_data)) { - dbus_message_unref(reply); - return NULL; - } - - dbus_message_iter_close_container(&iter, &value); - - return reply; -} - -static DBusMessage* properties_get_all(DBusConnection* connection, - DBusMessage* message, void* user_data) -{ - struct generic_data* data = user_data; - struct interface_data* iface; - const char* interface; - DBusMessageIter iter; - DBusMessage* reply; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_INVALID)) - return NULL; - - iface = find_interface(data->interfaces, interface); - if (iface == NULL) - return dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, - "No such interface '%s'", interface); - - reply = dbus_message_new_method_return(message); - if (reply == NULL) - return NULL; - - dbus_message_iter_init_append(reply, &iter); - - append_properties(iface, &iter); - - return reply; -} - -static char* validate_arguments(DBusMessage* message, DBusMessageIter* iter, - struct generic_data* data, struct interface_data** iface_out, const char** name) -{ - const char* interface = NULL; - char* error_msg = NULL; - - do { - if (!dbus_message_iter_init(message, iter)) { - if (asprintf(&error_msg, "No arguments given") < 0) - error_msg = DBUS_ERROR_OOM; - break; - } - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) { - if (asprintf(&error_msg, "Invalid argument type: '%c'", - dbus_message_iter_get_arg_type(iter)) - < 0) - error_msg = DBUS_ERROR_OOM; - break; - } - - dbus_message_iter_get_basic(iter, &interface); - dbus_message_iter_next(iter); - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) { - if (asprintf(&error_msg, "Invalid argument type: '%c'", - dbus_message_iter_get_arg_type(iter)) - < 0) - error_msg = DBUS_ERROR_OOM; - break; - } - - dbus_message_iter_get_basic(iter, name); - dbus_message_iter_next(iter); - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) { - if (asprintf(&error_msg, "Invalid argument type: '%c'", - dbus_message_iter_get_arg_type(iter)) - < 0) - error_msg = DBUS_ERROR_OOM; - break; - } - - *iface_out = find_interface(data->interfaces, interface); - if (*iface_out == NULL) { - if (asprintf(&error_msg, "No such interface '%s'", interface) < 0) - error_msg = DBUS_ERROR_OOM; - break; - } - - } while (0); - - return error_msg; -} - -static DBusMessage* properties_set(DBusConnection* connection, - DBusMessage* message, void* user_data) -{ - struct generic_data* data = user_data; - DBusMessageIter iter, sub; - struct interface_data* iface = NULL; - const GDBusPropertyTable* property; - const char* name = NULL; - struct property_data* propdata; - gboolean valid_signature; - char* signature; - - char* err_str = validate_arguments(message, &iter, data, &iface, &name); - if (err_str) { - DBusMessage* reply = dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, - "%s", err_str); - free(err_str); - return reply; - } - - property = find_property(iface->properties, name); - if (property == NULL) - return dbus_create_error(message, - DBUS_ERROR_UNKNOWN_PROPERTY, - "No such property '%s'", name); - - if (property->set == NULL) - return dbus_create_error(message, - DBUS_ERROR_PROPERTY_READ_ONLY, - "Property '%s' is not writable", name); - - if (property->exists != NULL && !property->exists(property, iface->user_data)) - return dbus_create_error(message, - DBUS_ERROR_UNKNOWN_PROPERTY, - "No such property '%s'", name); - - dbus_message_iter_recurse(&iter, &sub); - signature = dbus_message_iter_get_signature(&sub); - valid_signature = strcmp0(signature, property->type) ? FALSE : TRUE; - dbus_free(signature); - if (!valid_signature) - return dbus_create_error(message, - DBUS_ERROR_INVALID_SIGNATURE, - "Invalid signature for '%s'", name); - - propdata = malloc(sizeof(struct property_data)); - if (propdata == NULL) { - return dbus_create_error(message, DBUS_ERROR_NO_MEMORY, - "Failed to allocate memory for property data"); - } - - propdata->id = next_pending_property++; - propdata->message = dbus_message_ref(message); - propdata->conn = connection; - _dbus_list_prepend(&pending_property_set, propdata); - - property->set(property, &sub, propdata->id, iface->user_data); - - return NULL; -} - -static const GDBusMethodTable properties_methods[] = { - { GDBUS_METHOD("Get", - GDBUS_ARGS({ "interface", "s" }, { "name", "s" }), - GDBUS_ARGS({ "value", "v" }), - properties_get) }, - { GDBUS_ASYNC_METHOD("Set", - GDBUS_ARGS({ "interface", "s" }, { "name", "s" }, - { "value", "v" }), - NULL, - properties_set) }, - { GDBUS_METHOD("GetAll", - GDBUS_ARGS({ "interface", "s" }), - GDBUS_ARGS({ "properties", "a{sv}" }), - properties_get_all) }, - {} -}; - -static const GDBusSignalTable properties_signals[] = { - { GDBUS_SIGNAL("PropertiesChanged", - GDBUS_ARGS({ "interface", "s" }, - { "changed_properties", "a{sv}" }, - { "invalidated_properties", "as" })) }, - {} -}; - -static void append_name(gpointer data, gpointer user_data) -{ - char* name = data; - DBusMessageIter* iter = user_data; - - dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name); -} - -static void emit_interfaces_removed(struct generic_data* data) -{ - DBusMessage* signal; - DBusMessageIter iter, array; - - if (root == NULL || data == root) - return; - - signal = dbus_message_new_signal(root->path, - DBUS_INTERFACE_OBJECT_MANAGER, - "InterfacesRemoved"); - if (signal == NULL) - return; - - dbus_message_iter_init_append(signal, &iter); - dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, - &data->path); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_STRING_AS_STRING, &array); - - _dbus_list_foreach(&data->removed, append_name, &array); - _dbus_list_clear_full(&data->removed, free); - - dbus_message_iter_close_container(&iter, &array); - - /* Use dbus_connection_send to avoid recursive calls to dbus_flush */ - dbus_connection_send(data->conn, signal, NULL); - dbus_message_unref(signal); -} - -static void remove_pending(struct generic_data* data) -{ - uv_idle_stop(&data->handle); - - _dbus_list_remove(&generic_pending, data); -} - -static void process_changes(uv_idle_t* handle) -{ - struct generic_data* data = handle->data; - - remove_pending(data); - - if (data->added != NULL) - emit_interfaces_added(data); - - /* Flush pending properties */ - if (data->pending_prop == TRUE) - process_property_changes(data); - - if (data->removed != NULL) - emit_interfaces_removed(data); -} - -static void generic_unregister(DBusConnection* connection, void* user_data) -{ - struct generic_data* data = user_data; - struct generic_data* parent = data->parent; - - if (parent != NULL) - _dbus_list_remove(&parent->objects, data); - - process_changes(&data->handle); - uv_close((uv_handle_t*)&data->handle, NULL); - - _dbus_list_foreach(&data->objects, reset_parent, data->parent); - _dbus_list_clear(&data->objects); - - dbus_connection_unref(data->conn); - free(data->introspect); - free(data->path); - free(data); -} - -static DBusHandlerResult generic_message(DBusConnection* connection, - DBusMessage* message, void* user_data) -{ - struct generic_data* data = user_data; - struct interface_data* iface; - const GDBusMethodTable* method; - const char* interface; - - interface = dbus_message_get_interface(message); - - iface = find_interface(data->interfaces, interface); - if (iface == NULL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - for (method = iface->methods; method && method->name && method->function; method++) { - - if (dbus_message_is_method_call(message, iface->name, method->name) == FALSE) - continue; - - if (check_experimental(method->flags, - G_DBUS_METHOD_FLAG_EXPERIMENTAL)) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (dbus_args_have_signature(method->in_args, message) == FALSE) - continue; - - if (check_privilege(connection, message, method, iface->user_data) == TRUE) - return DBUS_HANDLER_RESULT_HANDLED; - - return process_message(connection, message, method, iface->user_data); - } - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static DBusObjectPathVTable generic_table = { - .unregister_function = generic_unregister, - .message_function = generic_message, -}; - -static const GDBusMethodTable introspect_methods[] = { - { GDBUS_METHOD("Introspect", NULL, - GDBUS_ARGS({ "xml", "s" }), introspect) }, - {} -}; - -static void append_interfaces(struct generic_data* data, DBusMessageIter* iter) -{ - DBusMessageIter array; - - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING - DBUS_TYPE_ARRAY_AS_STRING - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING - DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, - &array); - - _dbus_list_foreach(&data->interfaces, append_interface, &array); - - dbus_message_iter_close_container(iter, &array); -} - -static void append_object(gpointer data, gpointer user_data) -{ - struct generic_data* child = data; - DBusMessageIter* array = user_data; - DBusMessageIter entry; - - dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL, - &entry); - dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, - &child->path); - append_interfaces(child, &entry); - dbus_message_iter_close_container(array, &entry); - - _dbus_list_foreach(&child->objects, append_object, user_data); -} - -static DBusMessage* get_objects(DBusConnection* connection, - DBusMessage* message, void* user_data) -{ - struct generic_data* data = user_data; - DBusMessage* reply; - DBusMessageIter iter; - DBusMessageIter array; - - reply = dbus_message_new_method_return(message); - if (reply == NULL) - return NULL; - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_OBJECT_PATH_AS_STRING - DBUS_TYPE_ARRAY_AS_STRING - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING - DBUS_TYPE_ARRAY_AS_STRING - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING - DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, - &array); - - _dbus_list_foreach(&data->objects, append_object, &array); - - dbus_message_iter_close_container(&iter, &array); - - return reply; -} - -static const GDBusMethodTable manager_methods[] = { - { GDBUS_METHOD("GetManagedObjects", NULL, - GDBUS_ARGS({ "objects", "a{oa{sa{sv}}}" }), get_objects) }, - {} -}; - -static const GDBusSignalTable manager_signals[] = { - { GDBUS_SIGNAL("InterfacesAdded", - GDBUS_ARGS({ "object", "o" }, - { "interfaces", "a{sa{sv}}" })) }, - { GDBUS_SIGNAL("InterfacesRemoved", - GDBUS_ARGS({ "object", "o" }, { "interfaces", "as" })) }, - {} -}; - -static gboolean check_methods_experimental(const GDBusMethodTable* methods) -{ - const GDBusMethodTable* method = methods; - while (method && method->name) { - if (!check_experimental(method->flags, G_DBUS_METHOD_FLAG_EXPERIMENTAL)) - return FALSE; - method++; - } - return TRUE; -} - -static gboolean check_signals_experimental(const GDBusSignalTable* signals) -{ - const GDBusSignalTable* signal = signals; - while (signal && signal->name) { - if (!check_experimental(signal->flags, G_DBUS_SIGNAL_FLAG_EXPERIMENTAL)) - return FALSE; - signal++; - } - return TRUE; -} - -static gboolean check_properties_experimental(const GDBusPropertyTable* properties) -{ - const GDBusPropertyTable* property = properties; - while (property && property->name) { - if (!check_experimental(property->flags, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) - return FALSE; - property++; - } - return TRUE; -} - -static struct interface_data* create_interface_data(const char* name, - const GDBusMethodTable* methods, - const GDBusSignalTable* signals, - const GDBusPropertyTable* properties, - void* user_data, - GDBusDestroyFunction destroy) -{ - struct interface_data* iface = calloc(1, sizeof(struct interface_data)); - if (iface == NULL) { - error("Failed to allocate memory for interface data"); - return NULL; - } - - iface->name = strdup0(name); - iface->methods = methods; - iface->signals = signals; - iface->properties = properties; - iface->user_data = user_data; - iface->destroy = destroy; - - return iface; -} - -static gboolean add_interface(struct generic_data* data, - const char* name, - const GDBusMethodTable* methods, - const GDBusSignalTable* signals, - const GDBusPropertyTable* properties, - void* user_data, - GDBusDestroyFunction destroy) -{ - struct interface_data* iface; - - if (check_methods_experimental(methods) - && check_signals_experimental(signals) - && check_properties_experimental(properties)) { - info("Interface %s is experimental, Nothing to register", name); - return FALSE; - } - - iface = create_interface_data(name, methods, signals, properties, user_data, destroy); - if (iface == NULL) - return FALSE; - - _dbus_list_append(&data->interfaces, iface); - - if (data->parent) { - _dbus_list_append(&data->added, iface); - add_pending(data); - } - - return TRUE; -} - -static struct generic_data* object_path_ref(DBusConnection* connection, - const char* path) -{ - struct generic_data* data; - - if (dbus_connection_get_object_path_data(connection, path, (void*)&data) - == TRUE) { - if (data != NULL) { - data->refcount++; - return data; - } - } - - data = calloc(1, sizeof(struct generic_data)); - if (data == NULL) { - error("Failed to allocate memory for object path data"); - return NULL; - } - data->conn = dbus_connection_ref(connection); - data->path = strdup0(path); - data->refcount = 1; - if (uv_idle_init(uv_default_loop(), &data->handle) != 0) { - dbus_connection_unref(data->conn); - free(data->path); - free(data); - return NULL; - } - - data->handle.data = data; - - data->introspect = strdup0(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE ""); - - if (!dbus_connection_register_object_path(connection, path, - &generic_table, data)) { - dbus_connection_unref(data->conn); - uv_close((uv_handle_t*)&data->handle, NULL); - free(data->path); - free(data->introspect); - free(data); - return NULL; - } - - invalidate_parent_data(connection, path); - - add_interface(data, DBUS_INTERFACE_INTROSPECTABLE, introspect_methods, - NULL, NULL, data, NULL); - - return data; -} - -static void object_path_unref(DBusConnection* connection, const char* path) -{ - struct generic_data* data = NULL; - - if (dbus_connection_get_object_path_data(connection, path, (void*)&data) - == FALSE) - return; - - if (data == NULL) - return; - - data->refcount--; - - if (data->refcount > 0) - return; - - remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE); - remove_interface(data, DBUS_INTERFACE_PROPERTIES); - - invalidate_parent_data(data->conn, data->path); - - dbus_connection_unregister_object_path(data->conn, data->path); -} - -static gboolean check_signal(DBusConnection* conn, const char* path, - const char* interface, const char* name, - const GDBusArgInfo** args) -{ - struct generic_data* data = NULL; - struct interface_data* iface; - const GDBusSignalTable* signal; - - *args = NULL; - if (!dbus_connection_get_object_path_data(conn, path, (void*)&data) - || data == NULL) { - error("dbus_connection_emit_signal: path %s isn't registered", - path); - return FALSE; - } - - iface = find_interface(data->interfaces, interface); - if (iface == NULL) { - error("dbus_connection_emit_signal: %s does not implement %s", - path, interface); - return FALSE; - } - - for (signal = iface->signals; signal && signal->name; signal++) { - if (strcmp0(signal->name, name) != 0) - continue; - - if (signal->flags & G_DBUS_SIGNAL_FLAG_EXPERIMENTAL) { - const char* env = getenv("GDBUS_EXPERIMENTAL"); - if (env == NULL || strcmp0(env, "1") != 0) - break; - } - - *args = signal->args; - return TRUE; - } - - error("No signal named %s on interface %s", name, interface); - return FALSE; -} - -gboolean dbus_register_interface(DBusConnection* connection, - const char* path, const char* name, - const GDBusMethodTable* methods, - const GDBusSignalTable* signals, - const GDBusPropertyTable* properties, - void* user_data, - GDBusDestroyFunction destroy) -{ - struct generic_data* data; - - if (!dbus_validate_path(path, NULL)) { - error("Invalid object path: %s", path); - return FALSE; - } - - if (!dbus_validate_interface(name, NULL)) { - error("Invalid interface: %s", name); - return FALSE; - } - - data = object_path_ref(connection, path); - if (data == NULL) - return FALSE; - - if (find_interface(data->interfaces, name)) { - object_path_unref(connection, path); - return FALSE; - } - - if (!add_interface(data, name, methods, signals, properties, user_data, - destroy)) { - object_path_unref(connection, path); - return FALSE; - } - - if (properties != NULL - && !find_interface(data->interfaces, DBUS_INTERFACE_PROPERTIES)) - add_interface(data, DBUS_INTERFACE_PROPERTIES, - properties_methods, properties_signals, NULL, - data, NULL); - - free(data->introspect); - data->introspect = NULL; - - return TRUE; -} - -gboolean dbus_unregister_interface(DBusConnection* connection, - const char* path, const char* name) -{ - struct generic_data* data = NULL; - - if (path == NULL) - return FALSE; - - if (dbus_connection_get_object_path_data(connection, path, - (void*)&data) - == FALSE) - return FALSE; - - if (data == NULL) - return FALSE; - - if (remove_interface(data, name) == FALSE) - return FALSE; - - free(data->introspect); - data->introspect = NULL; - - object_path_unref(connection, data->path); - - return TRUE; -} - -gboolean dbus_register_security(const GDBusSecurityTable* security) -{ - if (security_table != NULL) - return FALSE; - - security_table = security; - - return TRUE; -} - -gboolean dbus_unregister_security(const GDBusSecurityTable* security) -{ - security_table = NULL; - - return TRUE; -} - -DBusMessage* dbus_create_error_valist(DBusMessage* message, const char* name, - const char* format, va_list args) -{ - char str[1024]; - - /* Check if the message can be replied */ - if (dbus_message_get_no_reply(message)) - return NULL; - - if (format) - vsnprintf(str, sizeof(str), format, args); - else - str[0] = '\0'; - - return dbus_message_new_error(message, name, str); -} - -DBusMessage* dbus_create_error(DBusMessage* message, const char* name, - const char* format, ...) -{ - va_list args; - DBusMessage* reply; - - va_start(args, format); - - reply = dbus_create_error_valist(message, name, format, args); - - va_end(args); - - return reply; -} - -DBusMessage* dbus_create_reply_valist(DBusMessage* message, - int type, va_list args) -{ - DBusMessage* reply; - - /* Check if the message can be replied */ - if (dbus_message_get_no_reply(message)) - return NULL; - - reply = dbus_message_new_method_return(message); - if (reply == NULL) - return NULL; - - if (dbus_message_append_args_valist(reply, type, args) == FALSE) { - dbus_message_unref(reply); - return NULL; - } - - return reply; -} - -DBusMessage* dbus_create_reply(DBusMessage* message, int type, ...) -{ - va_list args; - DBusMessage* reply; - - va_start(args, type); - - reply = dbus_create_reply_valist(message, type, args); - - va_end(args); - - return reply; -} - -static void dbus_flush(DBusConnection* connection) -{ - DBusList* l; - DBusList* next; - - for (l = _dbus_list_get_first_link(&generic_pending); l;) { - struct generic_data* data = l->data; - - next = _dbus_list_get_next_link(&generic_pending, l); - _dbus_list_remove_link(&generic_pending, l); - l = next; - - if (data->conn != connection) - continue; - - process_changes(&data->handle); - } -} - -gboolean dbus_send_message(DBusConnection* connection, DBusMessage* message) -{ - dbus_bool_t result = FALSE; - - if (!message) - return FALSE; - - if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL) - dbus_message_set_no_reply(message, TRUE); - else if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL) { - const char* path = dbus_message_get_path(message); - const char* interface = dbus_message_get_interface(message); - const char* name = dbus_message_get_member(message); - const GDBusArgInfo* args; - - if (!check_signal(connection, path, interface, name, &args)) - goto out; - } - - /* Flush pending signal to guarantee message order */ - dbus_flush(connection); - - result = dbus_connection_send(connection, message, NULL); - -out: - dbus_message_unref(message); - - return result; -} - -gboolean dbus_send_message_with_reply(DBusConnection* connection, - DBusMessage* message, - DBusPendingCall** call, int timeout) -{ - dbus_bool_t ret; - - /* Flush pending signal to guarantee message order */ - dbus_flush(connection); - - ret = dbus_connection_send_with_reply(connection, message, call, - timeout); - - if (ret == TRUE && call != NULL && *call == NULL) { - error("Unable to send message (passing fd blocked?)"); - return FALSE; - } - - return ret; -} - -gboolean dbus_send_error_valist(DBusConnection* connection, - DBusMessage* message, const char* name, - const char* format, va_list args) -{ - DBusMessage* error; - - error = dbus_create_error_valist(message, name, format, args); - if (error == NULL) - return FALSE; - - return dbus_send_message(connection, error); -} - -gboolean dbus_send_error(DBusConnection* connection, DBusMessage* message, - const char* name, const char* format, ...) -{ - va_list args; - gboolean result; - - va_start(args, format); - - result = dbus_send_error_valist(connection, message, name, - format, args); - - va_end(args); - - return result; -} - -gboolean dbus_send_reply_valist(DBusConnection* connection, - DBusMessage* message, int type, va_list args) -{ - DBusMessage* reply; - - reply = dbus_create_reply_valist(message, type, args); - if (!reply) - return FALSE; - - return dbus_send_message(connection, reply); -} - -gboolean dbus_send_reply(DBusConnection* connection, - DBusMessage* message, int type, ...) -{ - va_list args; - gboolean result; - - va_start(args, type); - - result = dbus_send_reply_valist(connection, message, type, args); - - va_end(args); - - return result; -} - -gboolean dbus_emit_signal(DBusConnection* connection, - const char* path, const char* interface, - const char* name, int type, ...) -{ - va_list args; - gboolean result; - - va_start(args, type); - - result = dbus_emit_signal_valist(connection, path, interface, - name, type, args); - - va_end(args); - - return result; -} - -gboolean dbus_emit_signal_valist(DBusConnection* connection, - const char* path, const char* interface, - const char* name, int type, va_list args) -{ - DBusMessage* signal; - dbus_bool_t ret; - const GDBusArgInfo* args_info; - - if (!check_signal(connection, path, interface, name, &args_info)) - return FALSE; - - signal = dbus_message_new_signal(path, interface, name); - if (signal == NULL) { - error("Unable to allocate new %s.%s signal", interface, name); - return FALSE; - } - - ret = dbus_message_append_args_valist(signal, type, args); - if (!ret) - goto fail; - - if (dbus_args_have_signature(args_info, signal) == FALSE) { - error("%s.%s: got unexpected signature '%s'", interface, name, - dbus_message_get_signature(signal)); - ret = FALSE; - goto fail; - } - - return dbus_send_message(connection, signal); - -fail: - dbus_message_unref(signal); - - return ret; -} - -static void dbus_list_reverse(DBusList** list) -{ - DBusList* last; - - last = NULL; - while (*list) { - last = *list; - *list = last->next; - last->next = last->prev; - last->prev = *list; - } - - *list = last; -} - -static DBusMessage* create_properties_changed_signal(struct generic_data* data, - struct interface_data* iface) -{ - DBusMessage* signal = dbus_message_new_signal(data->path, - DBUS_INTERFACE_PROPERTIES, "PropertiesChanged"); - if (!signal) { - error("Unable to allocate new " DBUS_INTERFACE_PROPERTIES - ".PropertiesChanged signal"); - } - return signal; -} - -static void process_valid_properties(struct interface_data* iface, - DBusMessageIter* dict, DBusList** invalidated) -{ - DBusList* list = _dbus_list_get_first_link(&iface->pending_prop); - while (list != NULL) { - GDBusPropertyTable* p = list->data; - list = _dbus_list_get_next_link(&iface->pending_prop, list); - if (!p->get) - continue; - - if (p->exists && !p->exists(p, iface->user_data)) { - _dbus_list_prepend(invalidated, p); - continue; - } - append_property(iface, p, dict); - } -} - -static void process_invalid_properties(DBusList* invalidated, - DBusMessageIter* array) -{ - DBusList* l; - for (l = _dbus_list_get_first_link(&invalidated); l; - l = _dbus_list_get_next_link(&invalidated, l)) { - GDBusPropertyTable* p = l->data; - dbus_message_iter_append_basic(array, DBUS_TYPE_STRING, &p->name); - } -} - -static void process_properties_from_interface(struct generic_data* data, - struct interface_data* iface) -{ - DBusMessageIter iter, dict, array; - DBusList* invalidated = NULL; - DBusMessage* signal; - - if (!iface->pending_prop) - return; - - signal = create_properties_changed_signal(data, iface); - if (!signal) - return; - - dbus_list_reverse(&iface->pending_prop); - - dbus_message_iter_init_append(signal, &iter); - dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &iface->name); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, - &dict); - - process_valid_properties(iface, &dict, &invalidated); - dbus_message_iter_close_container(&iter, &dict); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_STRING_AS_STRING, &array); - process_invalid_properties(invalidated, &array); - _dbus_list_clear(&invalidated); - dbus_message_iter_close_container(&iter, &array); - - _dbus_list_clear(&iface->pending_prop); - /* Use dbus_connection_send to avoid recursive calls to dbus_flush */ - dbus_connection_send(data->conn, signal, NULL); - dbus_message_unref(signal); -} - -static void process_property_changes(struct generic_data* data) -{ - DBusList* l; - - data->pending_prop = FALSE; - - for (l = _dbus_list_get_first_link(&data->interfaces); l; - l = _dbus_list_get_next_link(&data->interfaces, l)) { - struct interface_data* iface = l->data; - - process_properties_from_interface(data, iface); - } -} - -void dbus_emit_property_changed_full(DBusConnection* connection, - const char* path, const char* interface, - const char* name, - GDbusPropertyChangedFlags flags) -{ - const GDBusPropertyTable* property; - struct generic_data* data; - struct interface_data* iface; - - if (path == NULL) - return; - - if (!dbus_connection_get_object_path_data(connection, path, (void**)&data) - || data == NULL) - return; - - iface = find_interface(data->interfaces, interface); - if (iface == NULL) - return; - - /* - * If ObjectManager is attached, don't emit property changed if - * interface is not yet published - */ - if (root && _dbus_list_find_last(&data->added, iface) != NULL) - return; - - property = find_property(iface->properties, name); - if (property == NULL) { - error("Could not find property %s in %p", name, - iface->properties); - return; - } - - if (_dbus_list_find_last(&iface->pending_prop, (void*)property) != NULL) - return; - - data->pending_prop = TRUE; - _dbus_list_prepend(&iface->pending_prop, (void*)property); - - if (flags & G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH) - process_property_changes(data); - else - add_pending(data); -} - -void dbus_emit_property_changed(DBusConnection* connection, const char* path, - const char* interface, const char* name) -{ - dbus_emit_property_changed_full(connection, path, interface, name, 0); -} - -gboolean dbus_get_properties(DBusConnection* connection, const char* path, - const char* interface, DBusMessageIter* iter) -{ - struct generic_data* data; - struct interface_data* iface; - - if (path == NULL) - return FALSE; - - if (!dbus_connection_get_object_path_data(connection, path, (void**)&data) - || data == NULL) - return FALSE; - - iface = find_interface(data->interfaces, interface); - if (iface == NULL) - return FALSE; - - append_properties(iface, iter); - - return TRUE; -} - -gboolean dbus_attach_object_manager(DBusConnection* connection) -{ - struct generic_data* data; - - data = object_path_ref(connection, "/"); - if (data == NULL) - return FALSE; - - add_interface(data, DBUS_INTERFACE_OBJECT_MANAGER, - manager_methods, manager_signals, - NULL, data, NULL); - root = data; - - return TRUE; -} - -gboolean dbus_detach_object_manager(DBusConnection* connection) -{ - if (!dbus_unregister_interface(connection, "/", - DBUS_INTERFACE_OBJECT_MANAGER)) - return FALSE; - - root = NULL; - - return TRUE; -} - -void dbus_set_flags(int flags) -{ - global_flags = flags; -} - -int dbus_get_flags(void) -{ - return global_flags; -} diff --git a/gdbus/polkit.c b/gdbus/polkit.c deleted file mode 100644 index bcf320b..0000000 --- a/gdbus/polkit.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2025 Xiaomi Corporation - * - * 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 -#include - -#include - -#include "gdbus-internal.h" - -#define POLICY_KIT_DBUS_NAME "org.freedesktop.PolicyKit1" -#define POLICY_KIT_INTERFACE "org.freedesktop.PolicyKit1.Authority" -#define POLICY_KIT_PATH "/org/freedesktop/PolicyKit1/Authority" -#define POLICY_KIT_ACTION "org.freedesktop.policykit.exec" - -typedef enum polkit_interaction_flag { - POLKIT_FLAG_NONE = 0x00000000, - POLKIT_FLAG_ALLOW = 0x00000001 -} polkit_interaction_flag; - -typedef struct authorization_context { - void (*callback)(dbus_bool_t authorized, void* user_data); - void* user_data; -} authorization_context; - -typedef struct dict_entry_builder { - DBusMessageIter iter; - const char* key; - const char* value; -} dict_entry_builder; - -typedef enum contained_sig_type { - SIG_TYPE_EMPTY, - SIG_TYPE_VARIANT, -} contained_sig_type; - -static const char* init_contained_signature_type(contained_sig_type type) -{ - switch (type) { - case SIG_TYPE_EMPTY: - return DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING - DBUS_TYPE_STRING_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING; - case SIG_TYPE_VARIANT: - return DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING - DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING; - default: - return NULL; - } -} - -static void build_dict_entry(dict_entry_builder* builder) -{ - DBusMessageIter dict, entry, variant; - - dbus_message_iter_open_container(&builder->iter, DBUS_TYPE_ARRAY, - init_contained_signature_type(SIG_TYPE_VARIANT), &dict); - - dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); - dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &builder->key); - - dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, - DBUS_TYPE_STRING_AS_STRING, &variant); - dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &builder->value); - - dbus_message_iter_close_container(&entry, &variant); - dbus_message_iter_close_container(&dict, &entry); - dbus_message_iter_close_container(&builder->iter, &dict); -} - -static void build_empty_dict(DBusMessageIter* iter) -{ - DBusMessageIter dict; - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, - init_contained_signature_type(SIG_TYPE_EMPTY), &dict); - dbus_message_iter_close_container(iter, &dict); -} - -static void build_authorization_arguments(DBusConnection* conn, DBusMessageIter* iter, - const char* action, polkit_interaction_flag flags) -{ - const char* bus_name = dbus_bus_get_unique_name(conn); - const char* subject_kind = "system-bus-name"; - const char* cancellation_id = ""; - DBusMessageIter subject; - - // build main subject - dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &subject); - dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &subject_kind); - - dict_entry_builder builder = { - .iter = subject, - .key = "name", - .value = bus_name - }; - build_dict_entry(&builder); - dbus_message_iter_close_container(iter, &subject); - - // add less parameters - dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, - action ? action : POLICY_KIT_ACTION); - build_empty_dict(iter); - dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags); - dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancellation_id); -} - -static dbus_bool_t parse_authorization_result(DBusMessageIter* iter) -{ - DBusMessageIter recurse_iter; - dbus_bool_t auth = FALSE; - - if (!iter) - return FALSE; - - dbus_message_iter_recurse(iter, &recurse_iter); - dbus_message_iter_get_basic(&recurse_iter, &auth); - - return auth; -} - -static void handle_authorization_reply(DBusPendingCall* call, void* user_data) -{ - authorization_context* context = user_data; - DBusMessage* reply = NULL; - DBusMessageIter iter; - dbus_bool_t authorized = FALSE; - - if (!call || !context) - goto cleanup; - - reply = dbus_pending_call_steal_reply(call); - if (!reply) - goto cleanup; - - if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) { - error("Authorization error: %s\n", dbus_message_get_error_name(reply)); - goto cleanup; - } - - if (!dbus_message_has_signature(reply, "(bba{ss})")) { - error("Invalid reply signature\n"); - goto cleanup; - } - - if (!dbus_message_iter_init(reply, &iter)) - goto cleanup; - - authorized = parse_authorization_result(&iter); - -cleanup: - if (context != NULL && context->callback) { - context->callback(authorized, context->user_data); - } - - if (reply) - dbus_message_unref(reply); - - dbus_pending_call_unref(call); -} - -int dbus_polkit_check_authorization(DBusConnection* conn, - const char* action, gboolean allow_interaction, - void (*callback)(dbus_bool_t, void*), - void* user_data, int timeout_ms) -{ - DBusMessage* msg; - DBusMessageIter iter; - DBusPendingCall* pending_call = NULL; - polkit_interaction_flag flags; - - if (!conn) - return -EINVAL; - - authorization_context* context = calloc(1, sizeof(authorization_context)); - if (!context) - return -ENOMEM; - - msg = dbus_message_new_method_call(POLICY_KIT_DBUS_NAME, POLICY_KIT_PATH, - POLICY_KIT_INTERFACE, "CheckAuthorization"); - if (!msg) { - free(context); - return -ENOMEM; - } - - flags = allow_interaction ? POLKIT_FLAG_ALLOW : POLKIT_FLAG_NONE; - - dbus_message_iter_init_append(msg, &iter); - build_authorization_arguments(conn, &iter, action, flags); - - if (!dbus_connection_send_with_reply(conn, msg, &pending_call, timeout_ms)) { - dbus_message_unref(msg); - free(context); - return -EIO; - } - - if (!pending_call) { - dbus_message_unref(msg); - free(context); - return -EIO; - } - - context->callback = callback; - context->user_data = user_data; - - dbus_pending_call_set_notify(pending_call, handle_authorization_reply, - context, free); - dbus_message_unref(msg); - - return 0; -} diff --git a/gdbus/watch.c b/gdbus/watch.c deleted file mode 100644 index 072fc0f..0000000 --- a/gdbus/watch.c +++ /dev/null @@ -1,993 +0,0 @@ -/* - * Copyright (C) 2025 Xiaomi Corporation - * - * 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 -#include - -#include -#include -#include - -#include "gdbus-internal.h" - -static DBusHandlerResult message_filter(DBusConnection* connection, - DBusMessage* message, void* user_data); - -struct GDBusWatch { - DBusConnection* conn; - guint serial; - DBusList* list; - gboolean conn_closed; -}; - -struct service_data { - DBusConnection* conn; - DBusPendingCall* call; - char* name; - const char* owner; - struct filter_callback* callback; - uv_idle_t handle; -}; - -struct filter_callback { - GDBusWatchFunction conn_func; - GDBusWatchFunction disc_func; - GDBusSignalFunction signal_func; - GDBusDestroyFunction destroy_func; - struct service_data* data; - void* user_data; - guint id; -}; - -struct filter_data { - DBusConnection* connection; - DBusHandleMessageFunction handle_func; - char* name; - char* owner; - char* path; - char* interface; - char* member; - char* argument; - DBusList* callbacks; - DBusList* processed; - guint name_watch; - gboolean lock; - gboolean registered; - GDBusWatch* watcher; -}; - -static int strcmp0(const char* str1, const char* str2) -{ - if (str1 == NULL) - return -(str1 != str2); - - if (str2 == NULL) - return -(str1 != str2); - - return strcmp(str1, str2); -} - -static char* strdup0(const char* str) -{ - if (str) - return strdup(str); - - return NULL; -} - -static int get_watch_serial(GDBusWatch* watcher) -{ - return ++watcher->serial; -} - -static struct filter_data* filter_data_find_match(DBusConnection* connection, - DBusList* listener_list, const char* name, const char* owner, - const char* path, const char* interface, const char* member, const char* argument) -{ - DBusList* current; - - for (current = _dbus_list_get_first_link(&listener_list); current != NULL; - current = _dbus_list_get_next_link(&listener_list, current)) { - struct filter_data* data = current->data; - - if (connection != data->connection) - continue; - - if (strcmp0(name, data->name) != 0) - continue; - - if (strcmp0(owner, data->owner) != 0) - continue; - - if (strcmp0(path, data->path) != 0) - continue; - - if (strcmp0(interface, data->interface) != 0) - continue; - - if (strcmp0(member, data->member) != 0) - continue; - - if (strcmp0(argument, data->argument) != 0) - continue; - - return data; - } - - return NULL; -} - -static struct filter_data* filter_data_find(DBusConnection* connection, DBusList* listener_list) -{ - DBusList* current; - - for (current = _dbus_list_get_first_link(&listener_list); current != NULL; - current = _dbus_list_get_next_link(&listener_list, current)) { - struct filter_data* data = current->data; - - if (connection != data->connection) - continue; - - return data; - } - - return NULL; -} - -static void format_rule(struct filter_data* data, char* rule, size_t size) -{ - const char* sender; - int offset; - - offset = snprintf(rule, size, "type='signal'"); - sender = data->name ?: data->owner; - - if (sender) - offset += snprintf(rule + offset, size - offset, - ",sender='%s'", sender); - if (data->path) - offset += snprintf(rule + offset, size - offset, - ",path='%s'", data->path); - if (data->interface) - offset += snprintf(rule + offset, size - offset, - ",interface='%s'", data->interface); - if (data->member) - offset += snprintf(rule + offset, size - offset, - ",member='%s'", data->member); - if (data->argument) - snprintf(rule + offset, size - offset, - ",arg0='%s'", data->argument); -} - -static gboolean add_match(struct filter_data* data, - DBusHandleMessageFunction filter) -{ - DBusError err; - char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; - - format_rule(data, rule, sizeof(rule)); - dbus_error_init(&err); - - dbus_bus_add_match(data->connection, rule, &err); - if (dbus_error_is_set(&err)) { - error("Adding match rule \"%s\" failed: %s", rule, - err.message); - dbus_error_free(&err); - return FALSE; - } - - data->handle_func = filter; - data->registered = TRUE; - - return TRUE; -} - -static gboolean remove_match(struct filter_data* data) -{ - DBusError err; - char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; - - if (data->watcher->conn_closed) { - /* If the connection is disconnected, we don't need to remove the match */ - return TRUE; - } - - format_rule(data, rule, sizeof(rule)); - - dbus_error_init(&err); - - dbus_bus_remove_match(data->connection, rule, &err); - if (dbus_error_is_set(&err)) { - error("Removing owner match rule for %s failed: %s", - rule, err.message); - dbus_error_free(&err); - return FALSE; - } - - return TRUE; -} - -static void filter_data_free(struct filter_data* data, DBusList* listener_list) -{ - DBusList* l; - - /* Remove filter if there are no listeners left for the connection */ - if (filter_data_find(data->connection, listener_list) == NULL) { - dbus_connection_remove_filter(data->connection, message_filter, - data->watcher); - } - - for (l = _dbus_list_get_first_link(&data->callbacks); l != NULL; - l = _dbus_list_get_next_link(&data->callbacks, l)) - free(l->data); - - _dbus_list_clear(&data->callbacks); - dbus_remove_watch(data->watcher, data->name_watch); - free(data->name); - free(data->owner); - free(data->path); - free(data->interface); - free(data->member); - free(data->argument); - - /* no listeners left for the connection */ - if (_dbus_list_get_last(&listener_list) == NULL) { - free_dbus_watch(data->watcher); - } - dbus_connection_unref(data->connection); - free(data); -} - -static struct filter_data* filter_data_get(GDBusWatch* watcher, - DBusHandleMessageFunction filter, - const char* sender, const char* path, const char* interface, - const char* member, const char* argument) -{ - struct filter_data* data; - const char *name = NULL, *owner = NULL; - - if (filter_data_find(watcher->conn, watcher->list) == NULL) { - if (!dbus_connection_add_filter(watcher->conn, - message_filter, watcher, NULL)) { - error("dbus_connection_add_filter() failed"); - return NULL; - } - } - - if (sender != NULL) { - if (sender[0] == ':') - owner = sender; - else - name = sender; - } - - data = filter_data_find_match(watcher->conn, watcher->list, name, owner, path, - interface, member, argument); - if (data) - return data; - - data = calloc(1, sizeof(struct filter_data)); - - data->connection = dbus_connection_ref(watcher->conn); - data->name = strdup0(name); - data->owner = strdup0(owner); - data->path = strdup0(path); - data->interface = strdup0(interface); - data->member = strdup0(member); - data->argument = strdup0(argument); - data->watcher = watcher; - - if (!add_match(data, filter)) { - dbus_connection_unref(data->connection); - free(data->name); - free(data->owner); - free(data->path); - free(data->interface); - free(data->member); - free(data->argument); - free(data); - return NULL; - } - - _dbus_list_append(&watcher->list, data); - return data; -} - -static struct filter_callback* filter_data_find_callback( - struct filter_data* data, - guint id) -{ - DBusList* l; - - for (l = _dbus_list_get_first_link(&data->callbacks); l != NULL; - l = _dbus_list_get_next_link(&data->callbacks, l)) { - struct filter_callback* cb = l->data; - if (cb->id == id) - return cb; - } - for (l = _dbus_list_get_first_link(&data->processed); l != NULL; - l = _dbus_list_get_next_link(&data->processed, l)) { - struct filter_callback* cb = l->data; - if (cb->id == id) - return cb; - } - - return NULL; -} - -static void filter_data_call_and_free(struct filter_data* data, DBusList* listener_list) -{ - DBusList* l; - - for (l = _dbus_list_get_first_link(&data->callbacks); l != NULL; - l = _dbus_list_get_next_link(&data->callbacks, l)) { - struct filter_callback* cb = l->data; - if (cb->disc_func) - cb->disc_func(data->connection, cb->user_data); - if (cb->destroy_func) - cb->destroy_func(cb->user_data); - } - - filter_data_free(data, listener_list); -} - -static struct filter_callback* filter_data_add_callback( - struct filter_data* data, - GDBusWatchFunction connect, - GDBusWatchFunction disconnect, - GDBusSignalFunction signal, - GDBusDestroyFunction destroy, - guint watch_id, - void* user_data) -{ - struct filter_callback* cb = NULL; - - cb = calloc(1, sizeof(struct filter_callback)); - - cb->conn_func = connect; - cb->disc_func = disconnect; - cb->signal_func = signal; - cb->destroy_func = destroy; - cb->user_data = user_data; - cb->id = watch_id; - - if (data->lock) - _dbus_list_append(&data->processed, cb); - else - _dbus_list_append(&data->callbacks, cb); - - return cb; -} - -static void close_cb(uv_handle_t* handle) -{ - free(handle->data); -} - -static void service_data_free(struct service_data* data) -{ - struct filter_callback* callback = data->callback; - - dbus_connection_unref(data->conn); - - if (data->call) - dbus_pending_call_unref(data->call); - - free(data->name); - callback->data = NULL; - - uv_close((uv_handle_t*)&data->handle, close_cb); -} - -/* Returns TRUE if data is freed */ -static gboolean filter_data_remove_callback(struct filter_data* data, - struct filter_callback* cb) -{ - _dbus_list_remove(&data->callbacks, cb); - _dbus_list_remove(&data->processed, cb); - - /* Cancel pending operations */ - if (cb->data) { - if (cb->data->call) - dbus_pending_call_cancel(cb->data->call); - service_data_free(cb->data); - } - - if (cb->destroy_func) - cb->destroy_func(cb->user_data); - - free(cb); - - /* Don't remove the filter if other callbacks exist or data is lock - * processing callbacks */ - if (data->callbacks || data->lock) - return FALSE; - - if (data->registered && !remove_match(data)) - return FALSE; - - _dbus_list_remove(&data->watcher->list, data); - filter_data_free(data, data->watcher->list); - - return TRUE; -} - -static DBusHandlerResult signal_filter(DBusConnection* connection, - DBusMessage* message, void* user_data) -{ - struct filter_data* data = user_data; - struct filter_callback* cb; - - while (data->callbacks) { - cb = data->callbacks->data; - - if (cb->signal_func && !cb->signal_func(connection, message, cb->user_data)) { - if (filter_data_remove_callback(data, cb)) - break; - - continue; - } - - /* Check if the watch was removed/freed by the callback - * function */ - if (_dbus_list_find_last(&data->callbacks, cb) == NULL) - continue; - - _dbus_list_remove(&data->callbacks, cb); - _dbus_list_append(&data->processed, cb); - } - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static void update_name_cache(DBusList* listener_list, const char* name, const char* owner) -{ - DBusList* l; - - for (l = _dbus_list_get_first_link(&listener_list); l != NULL; - l = _dbus_list_get_next_link(&listener_list, l)) { - struct filter_data* data = l->data; - - if (strcmp0(data->name, name) != 0) - continue; - - free(data->owner); - data->owner = strdup0(owner); - } -} - -static const char* check_name_cache(DBusList* listener_list, const char* name) -{ - DBusList* l; - - for (l = _dbus_list_get_first_link(&listener_list); l != NULL; - l = _dbus_list_get_next_link(&listener_list, l)) { - struct filter_data* data = l->data; - - if (strcmp0(data->name, name) != 0) - continue; - - return data->owner; - } - - return NULL; -} - -static DBusHandlerResult service_filter(DBusConnection* connection, - DBusMessage* message, void* user_data) -{ - struct filter_data* data = user_data; - struct filter_callback* cb; - char *name, *old, *new; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &old, - DBUS_TYPE_STRING, &new, - DBUS_TYPE_INVALID)) { - error("Invalid arguments for NameOwnerChanged signal"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - update_name_cache(data->watcher->list, name, new); - - while (data->callbacks) { - cb = data->callbacks->data; - - if (*new == '\0') { - if (cb->disc_func) - cb->disc_func(connection, cb->user_data); - } else { - if (cb->conn_func) - cb->conn_func(connection, cb->user_data); - } - - /* Check if the watch was removed/freed by the callback - * function */ - if (_dbus_list_find_last(&data->callbacks, cb) == NULL) - continue; - - /* Only auto remove if it is a bus name watch */ - if (data->argument[0] == ':' && (cb->conn_func == NULL || cb->disc_func == NULL)) { - if (filter_data_remove_callback(data, cb)) - break; - - continue; - } - - _dbus_list_remove(&data->callbacks, cb); - _dbus_list_append(&data->processed, cb); - } - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static bool process_signal_message(DBusMessage* message, - const char** sender, const char** path, - const char** iface, const char** member, - const char** arg) -{ - if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) - return false; - - *sender = dbus_message_get_sender(message); - *path = dbus_message_get_path(message); - *iface = dbus_message_get_interface(message); - *member = dbus_message_get_member(message); - if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, arg, DBUS_TYPE_INVALID)) - *arg = NULL; - - return true; -} -static int filter_data_match(const char* str1, const char* data_param) -{ - if (data_param == NULL) - return 0; - - if (str1 == NULL) - return -1; - - return strcmp(str1, data_param); -} - -static gboolean match_listener_data(struct filter_data* data, DBusConnection* connection, - const char* sender, const char* path, - const char* iface, const char* member, const char* arg) -{ - if (connection != data->connection) - return FALSE; - - if (filter_data_match(sender, data->owner) != 0) - return FALSE; - - if (filter_data_match(path, data->path) != 0) - return FALSE; - - if (filter_data_match(iface, data->interface) != 0) - return FALSE; - - if (filter_data_match(member, data->member) != 0) - return FALSE; - - if (filter_data_match(arg, data->argument) != 0) - return FALSE; - - return TRUE; -} - -static void process_listener_callbacks(struct filter_data* data, DBusConnection* connection, - DBusMessage* message) -{ - - if (!data->handle_func) - return; - - data->lock = TRUE; - data->handle_func(connection, message, data); - data->callbacks = data->processed; - data->processed = NULL; - data->lock = FALSE; -} - -static void process_listeners(DBusList* listener_list, DBusConnection* connection, - DBusMessage* message, DBusList** delete_listener, const char* sender, - const char* path, const char* iface, const char* member, const char* arg) -{ - DBusList* current; - struct filter_data* data; - - for (current = _dbus_list_get_first_link(&listener_list); current; - current = _dbus_list_get_next_link(&listener_list, current)) { - data = current->data; - - if (!match_listener_data(data, connection, sender, - path, iface, member, arg)) - continue; - - process_listener_callbacks(data, connection, message); - - if (!data->callbacks) - _dbus_list_prepend(delete_listener, current); - } -} - -static void cleanup_listeners(DBusList* delete_listener, GDBusWatch* watcher) -{ - DBusList* current; - struct filter_data* data; - - for (current = _dbus_list_get_first_link(&delete_listener); current; - current = _dbus_list_get_next_link(&delete_listener, current)) { - DBusList* l = current->data; - data = l->data; - - /* Has any other callback added callbacks back to this data? */ - if (data->callbacks) - continue; - - remove_match(data); - _dbus_list_remove_link(&watcher->list, l); - filter_data_free(data, watcher->list); - } - _dbus_list_clear(&delete_listener); -} - -static DBusHandlerResult message_filter(DBusConnection* connection, - DBusMessage* message, void* user_data) -{ - const char *sender, *path, *iface, *member, *arg = NULL; - DBusList* delete_listener = NULL; - GDBusWatch* watcher = user_data; - - if (!process_signal_message(message, &sender, &path, - &iface, &member, &arg)) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - process_listeners(watcher->list, connection, message, &delete_listener, - sender, path, iface, member, arg); - - if (delete_listener) - cleanup_listeners(delete_listener, watcher); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static void update_service(uv_idle_t* handle) -{ - struct service_data* data = handle->data; - struct filter_callback* cb = data->callback; - DBusConnection* conn; - - conn = dbus_connection_ref(data->conn); - service_data_free(data); - - if (cb->conn_func) - cb->conn_func(conn, cb->user_data); - - dbus_connection_unref(conn); -} - -static void service_reply(DBusPendingCall* call, void* user_data) -{ - struct service_data* data = user_data; - DBusMessage* reply; - DBusError err; - - reply = dbus_pending_call_steal_reply(call); - if (reply == NULL) - return; - - dbus_error_init(&err); - - if (dbus_set_error_from_message(&err, reply)) - goto fail; - - if (dbus_message_get_args(reply, &err, DBUS_TYPE_STRING, - &data->owner, DBUS_TYPE_INVALID) - == FALSE) - goto fail; - - update_service(&data->handle); - - goto done; - -fail: - error("service_reply fail: %s", err.message); - dbus_error_free(&err); - service_data_free(data); -done: - dbus_message_unref(reply); -} - -static struct service_data* create_service_data(DBusConnection* conn, - const char* name, - struct filter_callback* cb) -{ - struct service_data* data = calloc(1, sizeof(*data)); - if (!data) { - error("%s: malloc failed", __func__); - return NULL; - } - - data->conn = dbus_connection_ref(conn); - data->name = strdup0(name); - data->callback = cb; - cb->data = data; - return data; -} - -static bool init_uv_handle(struct service_data* data) -{ - if (uv_idle_init(uv_default_loop(), &data->handle) != 0) { - error("%s: uv_idle_init failed", __func__); - return false; - } - data->handle.data = data; - return true; -} - -static gboolean send_name_owner_request(struct service_data* data, const char* name) -{ - DBusMessage* msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner"); - if (!msg) { - error("%s: dbus_message_new_method_call failed", __func__); - return FALSE; - } - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID); - if (!dbus_connection_send_with_reply(data->conn, msg, &data->call, -1)) { - error("%s: dbus_connection_send_with_reply failed", __func__); - dbus_message_unref(msg); - return FALSE; - } - - if (!data->call) { - error("%s: dbus reply failed with empty pendingcall", __func__); - dbus_message_unref(msg); - return FALSE; - } - - if (dbus_pending_call_get_completed(data->call)) { - service_reply(data->call, data); - } else { - dbus_pending_call_set_notify(data->call, service_reply, data, NULL); - } - - dbus_message_unref(msg); - return TRUE; -} - -static void check_service(DBusConnection* conn, const char* name, - struct filter_callback* cb, DBusList* listener_list) -{ - struct service_data* data = create_service_data(conn, name, cb); - if (!data) - return; - - if (!init_uv_handle(data)) { - dbus_connection_unref(data->conn); - free(data->name); - free(data); - return; - } - - data->owner = check_name_cache(listener_list, name); - if (data->owner) { - if (uv_idle_start(&data->handle, update_service) != 0) { - error("%s: uv_idle_start failed:", __func__); - uv_close((uv_handle_t*)&data->handle, NULL); - dbus_connection_unref(data->conn); - free(data->name); - free(data); - } - return; - } - - if (send_name_owner_request(data, name) == FALSE) { - uv_close((uv_handle_t*)&data->handle, NULL); - dbus_connection_unref(data->conn); - free(data->name); - free(data); - return; - } -} - -guint dbus_add_service_watch(GDBusWatch* watcher, const char* name, - GDBusWatchFunction connect, - GDBusWatchFunction disconnect, - void* user_data, GDBusDestroyFunction destroy) -{ - struct filter_data* data; - struct filter_callback* cb; - - if (name == NULL) - return 0; - - data = filter_data_get(watcher, service_filter, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, "NameOwnerChanged", name); - if (data == NULL) - return 0; - - cb = filter_data_add_callback(data, connect, disconnect, NULL, destroy, - get_watch_serial(watcher), user_data); - if (cb == NULL) - return 0; - - if (connect) - check_service(watcher->conn, name, cb, watcher->list); - - return cb->id; -} - -guint dbus_add_service_disconnect_watch(GDBusWatch* watcher, const char* name, - GDBusWatchFunction func, void* user_data, GDBusDestroyFunction destroy) -{ - return dbus_add_service_watch(watcher, name, NULL, func, user_data, destroy); -} - -guint dbus_add_signal_watch(GDBusWatch* watcher, - const char* sender, const char* path, - const char* interface, const char* member, - GDBusSignalFunction function, void* user_data, - GDBusDestroyFunction destroy) -{ - struct filter_data* data; - struct filter_callback* cb; - - data = filter_data_get(watcher, signal_filter, sender, path, - interface, member, NULL); - if (data == NULL) - return 0; - - cb = filter_data_add_callback(data, NULL, NULL, function, destroy, - get_watch_serial(watcher), user_data); - if (cb == NULL) - return 0; - - if (data->name != NULL && data->name_watch == 0) - data->name_watch = dbus_add_service_watch(watcher, - data->name, NULL, - NULL, NULL, NULL); - - return cb->id; -} - -guint dbus_add_properties_watch(GDBusWatch* watcher, - const char* sender, const char* path, - const char* interface, - GDBusSignalFunction function, void* user_data, - GDBusDestroyFunction destroy) -{ - struct filter_data* data; - struct filter_callback* cb; - - data = filter_data_get(watcher, signal_filter, sender, path, - DBUS_INTERFACE_PROPERTIES, "PropertiesChanged", interface); - if (data == NULL) - return 0; - - cb = filter_data_add_callback(data, NULL, NULL, function, destroy, - get_watch_serial(watcher), user_data); - if (cb == NULL) - return 0; - - if (data->name != NULL && data->name_watch == 0) - data->name_watch = dbus_add_service_watch(watcher, - data->name, NULL, - NULL, NULL, NULL); - - return cb->id; -} - -gboolean dbus_remove_watch(GDBusWatch* watcher, guint id) -{ - struct filter_data* data; - struct filter_callback* cb; - DBusList* ldata; - - if (id == 0) - return FALSE; - - for (ldata = _dbus_list_get_first_link(&watcher->list); ldata != NULL; - ldata = _dbus_list_get_next_link(&watcher->list, ldata)) { - data = ldata->data; - - cb = filter_data_find_callback(data, id); - if (cb) { - filter_data_remove_callback(data, cb); - return TRUE; - } - } - - return FALSE; -} - -void dbus_remove_all_watches(GDBusWatch* watcher) -{ - struct filter_data* data; - - while ((data = filter_data_find(watcher->conn, watcher->list))) { - _dbus_list_remove(&watcher->list, data); - filter_data_call_and_free(data, watcher->list); - } -} - -GDBusWatch* new_dbus_watch(DBusConnection* connection) -{ - GDBusWatch* watcher; - - watcher = calloc(1, sizeof(GDBusWatch)); - if (watcher == NULL) - return NULL; - - watcher->conn = dbus_connection_ref(connection); - watcher->list = NULL; - watcher->serial = 0; - watcher->conn_closed = FALSE; - - return watcher; -} -void free_dbus_watch(GDBusWatch* watcher) -{ - dbus_connection_unref(watcher->conn); - free(watcher); -} - -guint dbus_client_add_service_watch(GDBusClient* client, const char* name, - GDBusWatchFunction connect, GDBusWatchFunction disconnect, - void* user_data, GDBusDestroyFunction destroy) -{ - return dbus_add_service_watch(client->watcher, name, connect, disconnect, - user_data, destroy); -} - -guint dbus_client_add_service_disconnect_watch(GDBusClient* client, const char* name, - GDBusWatchFunction function, void* user_data, GDBusDestroyFunction destroy) -{ - return dbus_add_service_disconnect_watch(client->watcher, name, function, user_data, destroy); -} - -guint dbus_client_add_signal_watch(GDBusClient* client, const char* sender, - const char* path, const char* interface, const char* member, - GDBusSignalFunction function, void* user_data, GDBusDestroyFunction destroy) -{ - return dbus_add_signal_watch(client->watcher, sender, path, interface, - member, function, user_data, destroy); -} - -guint dbus_client_add_properties_watch(GDBusClient* client, - const char* sender, const char* path, const char* interface, - GDBusSignalFunction function, void* user_data, GDBusDestroyFunction destroy) -{ - return dbus_add_properties_watch(client->watcher, sender, path, interface, - function, user_data, destroy); -} - -gboolean dbus_client_remove_watch(GDBusClient* client, guint tag) -{ - return dbus_remove_watch(client->watcher, tag); -} - -void dbus_client_remove_all_watches(GDBusClient* client) -{ - dbus_remove_all_watches(client->watcher); -} - -void dbus_watch_set_connection_state(GDBusWatch* watcher, gboolean closed) -{ - watcher->conn_closed = closed; -}