#include "edbus_private_types.h" #include "edbus_private.h" #define DBUS_ANNOTATION(name, value) \ "" #define DBUS_ANNOTATION_DEPRECATED DBUS_ANNOTATION("Deprecated", "true") #define DBUS_ANNOTATION_NOREPLY DBUS_ANNOTATION("Method.NoReply", "true") #ifndef DBUS_ERROR_UNKNOWN_INTERFACE # define DBUS_ERROR_UNKNOWN_INTERFACE "org.freedesktop.DBus.Error.UnknownInterface" #endif #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 EDBUS_SERVICE_INTERFACE_CHECK(obj) \ do \ { \ EINA_SAFETY_ON_NULL_RETURN(obj); \ if (!EINA_MAGIC_CHECK(obj, EDBUS_SERVICE_INTERFACE_MAGIC)) \ { \ EINA_MAGIC_FAIL(obj, EDBUS_SERVICE_INTERFACE_MAGIC); \ return; \ } \ } \ while (0) #define EDBUS_SERVICE_INTERFACE_CHECK_RETVAL(obj, retval) \ do \ { \ EINA_SAFETY_ON_NULL_RETURN_VAL(obj, retval); \ if (!EINA_MAGIC_CHECK(obj, EDBUS_SERVICE_INTERFACE_MAGIC)) \ { \ EINA_MAGIC_FAIL(obj, EDBUS_SERVICE_INTERFACE_MAGIC); \ return retval; \ } \ } \ while (0) static void _object_unregister(DBusConnection *conn, void *user_data); static DBusHandlerResult _object_handler(DBusConnection *conn, DBusMessage *message, void *user_data); static void _object_free(EDBus_Service_Object *obj); static void _interface_free(EDBus_Service_Interface *interface); static void _on_connection_free(void *data, const void *dead_pointer); static DBusObjectPathVTable vtable = { _object_unregister, _object_handler, NULL, NULL, NULL, NULL }; EDBus_Service_Interface *introspectable; EDBus_Service_Interface *properties_iface; EDBus_Service_Interface *objmanager; static inline void _introspect_arguments_append(Eina_Strbuf *buf, const EDBus_Arg_Info *args, const char *direction) { for (; args && args->signature; args++) { if (args->name && args->name[0]) eina_strbuf_append_printf(buf, "signature, args->name); else eina_strbuf_append_printf(buf, "signature); if (direction) eina_strbuf_append_printf(buf, " direction=\"%s\" />", direction); else eina_strbuf_append(buf, " />"); } } static inline void _introspect_append_signal(Eina_Strbuf *buf, const EDBus_Signal *sig) { eina_strbuf_append_printf(buf, "name); if (!sig->flags && !(sig->args && sig->args->signature)) { eina_strbuf_append(buf, " />"); return; } eina_strbuf_append(buf, ">"); if (sig->flags & EDBUS_SIGNAL_FLAG_DEPRECATED) eina_strbuf_append(buf, DBUS_ANNOTATION_DEPRECATED); _introspect_arguments_append(buf, sig->args, NULL); eina_strbuf_append(buf, ""); } static inline void _instrospect_append_property(Eina_Strbuf *buf, const EDBus_Property *prop, const EDBus_Service_Interface *iface) { eina_strbuf_append_printf(buf, "name, prop->type); if (iface->get_func || prop->get_func) eina_strbuf_append(buf, "read"); if (iface->set_func || prop->set_func) eina_strbuf_append(buf, "write"); if (!prop->flags) { eina_strbuf_append(buf, "\" />"); return; } eina_strbuf_append(buf, "\">"); if (prop->flags & EDBUS_PROPERTY_FLAG_DEPRECATED) eina_strbuf_append(buf, DBUS_ANNOTATION_DEPRECATED); eina_strbuf_append(buf, ""); } static inline void _introspect_append_method(Eina_Strbuf *buf, const EDBus_Method *method) { eina_strbuf_append_printf(buf, "", method->member); if (method->flags & EDBUS_METHOD_FLAG_DEPRECATED) eina_strbuf_append(buf, DBUS_ANNOTATION_DEPRECATED); if (method->flags & EDBUS_METHOD_FLAG_NOREPLY) eina_strbuf_append(buf, DBUS_ANNOTATION_NOREPLY); _introspect_arguments_append(buf, method->in, "in"); _introspect_arguments_append(buf, method->out, "out"); eina_strbuf_append(buf, ""); } typedef struct _Property { const EDBus_Property *property; Eina_Bool is_invalidate:1; } Property; static void _introspect_append_interface(Eina_Strbuf *buf, EDBus_Service_Interface *iface) { const EDBus_Method *method; Property *prop; Eina_Iterator *iterator; unsigned short i; eina_strbuf_append_printf(buf, "", iface->name); iterator = eina_hash_iterator_data_new(iface->methods); EINA_ITERATOR_FOREACH(iterator, method) _introspect_append_method(buf, method); eina_iterator_free(iterator); for (i = 0; i < eina_array_count(iface->sign_of_signals); i++) _introspect_append_signal(buf, &iface->signals[i]); iterator = eina_hash_iterator_data_new(iface->properties); EINA_ITERATOR_FOREACH(iterator, prop) _instrospect_append_property(buf, prop->property, iface); eina_iterator_free(iterator); eina_strbuf_append(buf, ""); } static EDBus_Message * _cb_property_get(const EDBus_Service_Interface *piface, const EDBus_Message *msg) { const char *propname, *iface_name; EDBus_Service_Object *obj = piface->obj; EDBus_Service_Interface *iface; Property *prop; EDBus_Message *reply, *error_reply = NULL; EDBus_Message_Iter *main_iter, *variant; Eina_Bool ret; EDBus_Property_Get_Cb getter = NULL; if (!edbus_message_arguments_get(msg, "ss", &iface_name, &propname)) return NULL; iface = eina_hash_find(obj->interfaces, iface_name); if (!iface) return edbus_message_error_new(msg, DBUS_ERROR_UNKNOWN_INTERFACE, "Interface not found."); prop = eina_hash_find(iface->properties, propname); if (!prop || prop->is_invalidate) goto not_found; if (prop->property->get_func) getter = prop->property->get_func; else if (iface->get_func) getter = iface->get_func; if (!getter) goto not_found; reply = edbus_message_method_return_new(msg); EINA_SAFETY_ON_NULL_RETURN_VAL(reply, NULL); main_iter = edbus_message_iter_get(reply); variant = edbus_message_iter_container_new(main_iter, 'v', prop->property->type); ret = getter(iface, propname, variant, msg, &error_reply); if (ret) { edbus_message_iter_container_close(main_iter, variant); return reply; } edbus_message_unref(reply); return error_reply; not_found: return edbus_message_error_new(msg, DBUS_ERROR_UNKNOWN_PROPERTY, "Property not found."); } static Eina_Bool _props_getall(EDBus_Service_Interface *iface, Eina_Iterator *iterator, EDBus_Message_Iter *dict, const EDBus_Message *input_msg, EDBus_Message **error_reply) { Property *prop; EINA_ITERATOR_FOREACH(iterator, prop) { EDBus_Message_Iter *entry, *var; Eina_Bool ret; EDBus_Property_Get_Cb getter = NULL; if (prop->property->get_func) getter = prop->property->get_func; else if (iface->get_func) getter = iface->get_func; if (!getter || prop->is_invalidate) continue; if (!edbus_message_iter_arguments_append(dict, "{sv}", &entry)) continue; edbus_message_iter_basic_append(entry, 's', prop->property->name); var = edbus_message_iter_container_new(entry, 'v', prop->property->type); ret = getter(iface, prop->property->name, var, input_msg, error_reply); if (!ret) return EINA_FALSE; edbus_message_iter_container_close(entry, var); edbus_message_iter_container_close(dict, entry); } return EINA_TRUE; } static EDBus_Message * _cb_property_getall(const EDBus_Service_Interface *piface, const EDBus_Message *msg) { const char *iface_name; EDBus_Service_Object *obj = piface->obj; EDBus_Service_Interface *iface; Eina_Iterator *iterator; EDBus_Message *reply, *error_reply; EDBus_Message_Iter *main_iter, *dict; if (!edbus_message_arguments_get(msg, "s", &iface_name)) return NULL; iface = eina_hash_find(obj->interfaces, iface_name); if (!iface) return edbus_message_error_new(msg, DBUS_ERROR_UNKNOWN_INTERFACE, "Interface not found."); reply = edbus_message_method_return_new(msg); EINA_SAFETY_ON_NULL_RETURN_VAL(reply, NULL); main_iter = edbus_message_iter_get(reply); if (!edbus_message_iter_arguments_append(main_iter, "a{sv}", &dict)) { edbus_message_unref(reply); return NULL; } iterator = eina_hash_iterator_data_new(iface->properties); if (!_props_getall(iface, iterator, dict, msg, &error_reply)) { edbus_message_unref(reply); eina_iterator_free(iterator); return error_reply; } edbus_message_iter_container_close(main_iter, dict); eina_iterator_free(iterator); return reply; } static EDBus_Message * _cb_property_set(const EDBus_Service_Interface *piface, const EDBus_Message *msg) { const char *propname, *iface_name; EDBus_Service_Object *obj = piface->obj; EDBus_Service_Interface *iface; Property *prop; EDBus_Message *reply; EDBus_Message_Iter *variant; EDBus_Property_Set_Cb setter = NULL; if (!edbus_message_arguments_get(msg, "ssv", &iface_name, &propname, &variant)) return NULL; iface = eina_hash_find(obj->interfaces, iface_name); if (!iface) return edbus_message_error_new(msg, DBUS_ERROR_UNKNOWN_INTERFACE, "Interface not found."); prop = eina_hash_find(iface->properties, propname); if (!prop || prop->is_invalidate) return edbus_message_error_new(msg, DBUS_ERROR_UNKNOWN_PROPERTY, "Property not found."); if (prop->property->set_func) setter = prop->property->set_func; else if (iface->set_func) setter = iface->set_func; if (!setter) return edbus_message_error_new(msg, DBUS_ERROR_PROPERTY_READ_ONLY, "This property is read only"); reply = setter(iface, propname, variant, msg); return reply; } static EDBus_Message * cb_introspect(const EDBus_Service_Interface *_iface, const EDBus_Message *message) { EDBus_Service_Object *obj = _iface->obj; EDBus_Message *reply = edbus_message_method_return_new(message); if (obj->introspection_dirty || !obj->introspection_data) { Eina_Iterator *iterator; EDBus_Service_Interface *iface; EDBus_Service_Object *child; size_t baselen; if (obj->introspection_data) eina_strbuf_reset(obj->introspection_data); else obj->introspection_data = eina_strbuf_new(); EINA_SAFETY_ON_NULL_RETURN_VAL(obj->introspection_data, NULL); eina_strbuf_append(obj->introspection_data, ""); eina_strbuf_append_printf(obj->introspection_data, "", obj->path); iterator = eina_hash_iterator_data_new(obj->interfaces); EINA_ITERATOR_FOREACH(iterator, iface) _introspect_append_interface(obj->introspection_data, iface); eina_iterator_free(iterator); baselen = strlen(obj->path); /* account for the last '/' */ if (baselen != 1) baselen++; EINA_INLIST_FOREACH(obj->children, child) eina_strbuf_append_printf(obj->introspection_data, "", child->path + baselen); eina_strbuf_append(obj->introspection_data, ""); obj->introspection_dirty = EINA_FALSE; } edbus_message_arguments_append(reply, "s", eina_strbuf_string_get(obj->introspection_data)); return reply; } static const EDBus_Method introspect = { "Introspect", NULL, EDBUS_ARGS({ "s", "xml" }), cb_introspect }; static void _introspectable_create(void) { introspectable = calloc(1, sizeof(EDBus_Service_Interface)); EINA_SAFETY_ON_NULL_RETURN(introspectable); EINA_MAGIC_SET(introspectable, EDBUS_SERVICE_INTERFACE_MAGIC); introspectable->sign_of_signals = eina_array_new(1); introspectable->properties = eina_hash_string_small_new(NULL); introspectable->name = EDBUS_FDO_INTERFACE_INTROSPECTABLE; introspectable->methods = eina_hash_string_small_new(NULL); eina_hash_add(introspectable->methods, introspect.member, &introspect); } static void _default_interfaces_free(void) { eina_hash_free(introspectable->methods); eina_hash_free(introspectable->properties); eina_array_free(introspectable->sign_of_signals); free(introspectable); eina_hash_free(properties_iface->methods); eina_hash_free(properties_iface->properties); eina_array_free(properties_iface->sign_of_signals); free(properties_iface); eina_hash_free(objmanager->methods); eina_hash_free(objmanager->properties); eina_array_free(objmanager->sign_of_signals); free(objmanager); } static const EDBus_Method _property_methods[] = { { "Get", EDBUS_ARGS({"s", "interface"}, {"s", "property"}), EDBUS_ARGS({"v", "value"}), _cb_property_get }, { "Set", EDBUS_ARGS({"s", "interface"}, {"s", "property"}, {"v", "value"}), NULL, _cb_property_set }, { "GetAll", EDBUS_ARGS({"s", "interface"}), EDBUS_ARGS({"a{sv}", "props"}), _cb_property_getall } }; static const EDBus_Signal _properties_signals[] = { { "PropertiesChanged", EDBUS_ARGS({"s", "interface"}, {"a{sv}", "changed_properties"}, {"as", "invalidated_properties"}) } }; static void _properties_create(void) { properties_iface = calloc(1, sizeof(EDBus_Service_Interface)); if (!properties_iface) return; properties_iface->sign_of_signals = eina_array_new(1); properties_iface->properties = eina_hash_string_small_new(NULL); properties_iface->name = EDBUS_FDO_INTERFACE_PROPERTIES; properties_iface->methods = eina_hash_string_small_new(NULL); EINA_MAGIC_SET(properties_iface, EDBUS_SERVICE_INTERFACE_MAGIC); eina_hash_add(properties_iface->methods, _property_methods[0].member, &_property_methods[0]); eina_hash_add(properties_iface->methods, _property_methods[1].member, &_property_methods[1]); eina_hash_add(properties_iface->methods, _property_methods[2].member, &_property_methods[2]); properties_iface->signals = _properties_signals; eina_array_push(properties_iface->sign_of_signals, "sa{sv}as"); } static Eina_Bool _propmgr_iface_props_append(EDBus_Service_Interface *iface, EDBus_Message_Iter *array) { EDBus_Message_Iter *iface_entry, *props_array; Eina_Iterator *iterator; EDBus_Message *error_msg; edbus_message_iter_arguments_append(array, "{sa{sv}}", &iface_entry); edbus_message_iter_arguments_append(iface_entry, "sa{sv}", iface->name, &props_array); iterator = eina_hash_iterator_data_new(iface->properties); if (!_props_getall(iface, iterator, props_array, NULL, &error_msg)) { ERR("Error reply was set without pass any input message."); edbus_message_unref(error_msg); eina_iterator_free(iterator); return EINA_FALSE; } eina_iterator_free(iterator); edbus_message_iter_container_close(iface_entry, props_array); edbus_message_iter_container_close(array, iface_entry); return EINA_TRUE; } static Eina_Bool _managed_obj_append(EDBus_Service_Object *obj, EDBus_Message_Iter *array, Eina_Bool first) { EDBus_Message_Iter *obj_entry, *array_interface; Eina_Iterator *iface_iter; EDBus_Service_Interface *children_iface; EDBus_Service_Object *children; if (first) goto foreach; if (obj->has_objectmanager) return EINA_TRUE; edbus_message_iter_arguments_append(array, "{oa{sa{sv}}}", &obj_entry); edbus_message_iter_arguments_append(obj_entry, "oa{sa{sv}}", obj->path, &array_interface); iface_iter = eina_hash_iterator_data_new(obj->interfaces); EINA_ITERATOR_FOREACH(iface_iter, children_iface) { Eina_Bool ret; ret = _propmgr_iface_props_append(children_iface, array_interface); if (ret) continue; eina_iterator_free(iface_iter); return EINA_FALSE; } eina_iterator_free(iface_iter); edbus_message_iter_container_close(obj_entry, array_interface); edbus_message_iter_container_close(array, obj_entry); foreach: EINA_INLIST_FOREACH(obj->children, children) { Eina_Bool ret; ret = _managed_obj_append(children, array, EINA_FALSE); if (!ret) return EINA_FALSE; } return EINA_TRUE; } static EDBus_Message * _cb_managed_objects(const EDBus_Service_Interface *iface, const EDBus_Message *msg) { EDBus_Message *reply = edbus_message_method_return_new(msg); EDBus_Message_Iter *array_path, *main_iter; Eina_Bool ret; EINA_SAFETY_ON_NULL_RETURN_VAL(reply, NULL); main_iter = edbus_message_iter_get(reply); edbus_message_iter_arguments_append(main_iter, "a{oa{sa{sv}}}", &array_path); ret = _managed_obj_append(iface->obj, array_path, EINA_TRUE); if (!ret) { edbus_message_unref(reply); return edbus_message_error_new(msg, "org.freedesktop.DBus.Error", "Irrecoverable error happen"); } edbus_message_iter_container_close(main_iter, array_path); return reply; } static EDBus_Method get_managed_objects = { "GetManagedObjects", NULL, EDBUS_ARGS({"a{oa{sa{sv}}}", "objects"}), _cb_managed_objects }; static const EDBus_Signal _object_manager_signals[] = { { "InterfacesAdded", EDBUS_ARGS({"o", "object"}, {"a{sa{sv}}", "interfaces"}) }, { "InterfacesRemoved", EDBUS_ARGS({"o", "object"}, {"as", "interfaces"}) } }; static void _object_manager_create(void) { objmanager = calloc(1, sizeof(EDBus_Service_Interface)); if (!objmanager) return; EINA_MAGIC_SET(objmanager, EDBUS_SERVICE_INTERFACE_MAGIC); objmanager->sign_of_signals = eina_array_new(1); objmanager->properties = eina_hash_string_small_new(NULL); objmanager->name = EDBUS_FDO_INTERFACE_OBJECT_MANAGER; objmanager->methods = eina_hash_string_small_new(NULL); eina_hash_add(objmanager->methods, get_managed_objects.member, &get_managed_objects); objmanager->signals = _object_manager_signals; eina_array_push(objmanager->sign_of_signals, "oa{sa{sv}}"); eina_array_push(objmanager->sign_of_signals, "oas"); } Eina_Bool edbus_service_init(void) { _introspectable_create(); EINA_SAFETY_ON_NULL_RETURN_VAL(introspectable, EINA_FALSE); _properties_create(); EINA_SAFETY_ON_NULL_RETURN_VAL(properties_iface, EINA_FALSE); _object_manager_create(); EINA_SAFETY_ON_NULL_RETURN_VAL(objmanager, EINA_FALSE); return EINA_TRUE; } void edbus_service_shutdown(void) { _default_interfaces_free(); } static EDBus_Service_Object * _edbus_service_object_parent_find(EDBus_Service_Object *obj) { EDBus_Service_Object *parent = NULL; size_t len = strlen(obj->path); char *path = strdup(obj->path); char *slash; for (slash = path[len] != '/' ? &path[len - 1] : &path[len - 2]; slash > path; slash--) { if (*slash != '/') continue; *slash = '\0'; if (dbus_connection_get_object_path_data(obj->conn->dbus_conn, path, (void **) &parent) && parent != NULL) break; } free(path); return parent; } static EDBus_Service_Object * _edbus_service_object_add(EDBus_Connection *conn, const char *path) { EDBus_Service_Object *obj, *rootobj; Eina_Inlist *safe; size_t pathlen; obj = calloc(1, sizeof(EDBus_Service_Object)); EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL); if (!dbus_connection_register_object_path(conn->dbus_conn, path, &vtable, obj)) { free(obj); return NULL; } obj->conn = conn; obj->path = eina_stringshare_add(path); obj->interfaces = eina_hash_string_superfast_new(NULL); edbus_connection_free_cb_add(conn, _on_connection_free, obj); eina_hash_add(obj->interfaces, introspectable->name, introspectable); eina_hash_add(obj->interfaces, properties_iface->name, properties_iface); obj->parent = _edbus_service_object_parent_find(obj); if (obj->parent) { obj->parent->children = eina_inlist_append(obj->parent->children, EINA_INLIST_GET(obj)); return obj; } /* * If there wasn't any object above us, check if anyone in conn->root_obj * should become our child and append ourselves there. */ pathlen = strlen(obj->path); EINA_INLIST_FOREACH_SAFE(conn->root_objs, safe, rootobj) { if (strncmp(obj->path, rootobj->path, pathlen) != 0) continue; if (rootobj->path[pathlen] != '/' && pathlen > 1) continue; conn->root_objs = eina_inlist_remove(conn->root_objs, EINA_INLIST_GET(rootobj)); obj->children = eina_inlist_append(obj->children, EINA_INLIST_GET(rootobj)); rootobj->parent = obj; } conn->root_objs = eina_inlist_append(conn->root_objs, EINA_INLIST_GET(obj)); return obj; } static void _props_free(void *data) { Property *p = data; free(p); } struct iface_remove_data { const char *obj_path; const char *iface; }; static Eina_Bool _iface_changed_send(void *data) { EDBus_Service_Object *parent = data; while (parent->iface_added) { EDBus_Service_Interface *iface, *next_iface; EDBus_Message *msg; EDBus_Message_Iter *array_iface, *main_iter; Eina_List *l, *l2; iface = eina_list_data_get(parent->iface_added); parent->iface_added = eina_list_remove_list(parent->iface_added, parent->iface_added); msg = edbus_message_signal_new(parent->path, EDBUS_FDO_INTERFACE_OBJECT_MANAGER, "InterfacesAdded"); if (!msg) { ERR("msg == NULL"); continue; } main_iter = edbus_message_iter_get(msg); edbus_message_iter_arguments_append(main_iter, "oa{sa{sv}}", iface->obj->path, &array_iface); if (!_propmgr_iface_props_append(iface, array_iface)) goto error; EINA_LIST_FOREACH_SAFE(parent->iface_added, l, l2, next_iface) { if (iface->obj->path != next_iface->obj->path) continue; parent->iface_added = eina_list_remove(parent->iface_added, next_iface); if (!_propmgr_iface_props_append(next_iface, array_iface)) goto error; } edbus_message_iter_container_close(main_iter, array_iface); edbus_connection_send(parent->conn, msg, NULL, NULL, -1); continue; error: ERR("Error appending InterfacesAdded to msg."); edbus_message_unref(msg); } while (parent->iface_removed) { EDBus_Message *msg; EDBus_Message_Iter *array_iface, *main_iter; struct iface_remove_data *iface_data, *iface_data_next; Eina_List *l, *l2; iface_data = eina_list_data_get(parent->iface_removed); parent->iface_removed = eina_list_remove_list(parent->iface_removed, parent->iface_removed); msg = edbus_message_signal_new(parent->path, EDBUS_FDO_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved"); EINA_SAFETY_ON_NULL_GOTO(msg, error2); main_iter = edbus_message_iter_get(msg); edbus_message_iter_arguments_append(main_iter, "oas", iface_data->obj_path, &array_iface); edbus_message_iter_basic_append(array_iface, 's', iface_data->iface); EINA_LIST_FOREACH_SAFE(parent->iface_removed, l, l2, iface_data_next) { if (iface_data->obj_path != iface_data_next->obj_path) continue; parent->iface_removed = eina_list_remove(parent->iface_removed, iface_data_next); edbus_message_iter_basic_append(array_iface, 's', iface_data_next->iface); eina_stringshare_del(iface_data_next->iface); eina_stringshare_del(iface_data_next->obj_path); free(iface_data_next); } edbus_message_iter_container_close(main_iter, array_iface); edbus_connection_send(parent->conn, msg, NULL, NULL, -1); error2: eina_stringshare_del(iface_data->iface); eina_stringshare_del(iface_data->obj_path); free(iface_data); } parent->idler_iface_changed = NULL; return EINA_FALSE; } static EDBus_Service_Object * _find_object_manager_parent(EDBus_Service_Object *obj) { if (!obj->parent) return NULL; if (obj->parent->has_objectmanager) return obj->parent; return _find_object_manager_parent(obj->parent); } static EDBus_Service_Interface * _edbus_service_interface_add(EDBus_Service_Object *obj, const char *interface) { EDBus_Service_Interface *iface; EDBus_Service_Object *parent; iface = eina_hash_find(obj->interfaces, interface); if (iface) return iface; iface = calloc(1, sizeof(EDBus_Service_Interface)); EINA_SAFETY_ON_NULL_RETURN_VAL(iface, NULL); EINA_MAGIC_SET(iface, EDBUS_SERVICE_INTERFACE_MAGIC); iface->name = eina_stringshare_add(interface); iface->methods = eina_hash_string_superfast_new(NULL); iface->properties = eina_hash_string_superfast_new(_props_free); iface->obj = obj; eina_hash_add(obj->interfaces, iface->name, iface); parent = _find_object_manager_parent(obj); if (parent) { if (!parent->idler_iface_changed) parent->idler_iface_changed = ecore_idler_add(_iface_changed_send, parent); parent->iface_added = eina_list_append(parent->iface_added, iface); } return iface; } static Eina_Bool _have_signature(const EDBus_Arg_Info *args, EDBus_Message *msg) { const char *sig = dbus_message_get_signature(msg->dbus_msg); const char *p = NULL; for (; args && args->signature && *sig; args++) { p = args->signature; for (; *sig && *p; sig++, p++) { if (*p != *sig) return EINA_FALSE; } } if (*sig || (p && *p) || (args && args->signature)) return EINA_FALSE; return EINA_TRUE; } static Eina_Bool _edbus_service_method_add(EDBus_Service_Interface *interface, const EDBus_Method *method) { EINA_SAFETY_ON_TRUE_RETURN_VAL(!!eina_hash_find(interface->methods, method->member), EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(method->member, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(method->cb, EINA_FALSE); eina_hash_add(interface->methods, method->member, method); return EINA_TRUE; } static Eina_Bool _edbus_service_property_add(EDBus_Service_Interface *interface, const EDBus_Property *property) { Property *p; EINA_SAFETY_ON_TRUE_RETURN_VAL(!!eina_hash_find(interface->properties, property->name), EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(property->type, EINA_FALSE); EINA_SAFETY_ON_FALSE_RETURN_VAL( dbus_signature_validate_single(property->type, NULL), EINA_FALSE); p = calloc(1, sizeof(Property)); EINA_SAFETY_ON_NULL_RETURN_VAL(p, EINA_FALSE); p->property = property; return eina_hash_add(interface->properties, property->name, p); } /* Check if all signals in desc have valid signatures and return an Eina_Array * with each of them. Return NULL if any of the signatures is invalid */ static inline Eina_Array * _edbus_service_interface_desc_signals_signatures_get( const EDBus_Service_Interface_Desc *desc) { const EDBus_Signal *sig; Eina_Strbuf *buf = eina_strbuf_new(); Eina_Array *signatures = eina_array_new(1); EINA_SAFETY_ON_NULL_RETURN_VAL(buf, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(signatures, NULL); for (sig = desc->signals; sig && sig->name; sig++) { const EDBus_Arg_Info *arg; eina_strbuf_reset(buf); for (arg = sig->args; arg && arg->signature; arg++) eina_strbuf_append(buf, arg->signature); if (!dbus_signature_validate(eina_strbuf_string_get(buf), NULL)) { ERR("Signal with invalid signature: interface=%s signal=%s", desc->interface, sig->name); goto fail_signature; } eina_array_push(signatures, eina_stringshare_add(eina_strbuf_string_get(buf))); } eina_strbuf_free(buf); return signatures; fail_signature: eina_strbuf_free(buf); eina_array_free(signatures); return NULL; } EAPI EDBus_Service_Interface * edbus_service_interface_register(EDBus_Connection *conn, const char *path, const EDBus_Service_Interface_Desc *desc) { EDBus_Service_Object *obj; EDBus_Service_Interface *iface; const EDBus_Method *method; const EDBus_Property *property; Eina_Array *signatures; EINA_SAFETY_ON_NULL_RETURN_VAL(conn, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(path, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(desc, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(desc->interface, EINA_FALSE); if (!dbus_connection_get_object_path_data(conn->dbus_conn, path, (void*)&obj)) { ERR("Invalid object path"); return NULL; } signatures = _edbus_service_interface_desc_signals_signatures_get(desc); if (!signatures) return NULL; if (!obj) obj = _edbus_service_object_add(conn, path); else obj->introspection_dirty = EINA_TRUE; EINA_SAFETY_ON_NULL_GOTO(obj, fail); iface = _edbus_service_interface_add(obj, desc->interface); if (!iface) goto fail; for (method = desc->methods; method && method->member; method++) _edbus_service_method_add(iface, method); iface->signals = desc->signals; iface->sign_of_signals = signatures; for (property = desc->properties; property && property->name; property++) _edbus_service_property_add(iface, property); iface->get_func = desc->default_get; iface->set_func = desc->default_set; return iface; fail: eina_array_free(signatures); if (obj && (eina_hash_population(obj->interfaces) < 2)) _object_free(obj); return NULL; } static void _interface_free(EDBus_Service_Interface *interface) { const char *sig; EDBus_Service_Object *parent; if (interface == introspectable || interface == properties_iface || interface == objmanager) return; eina_hash_free(interface->methods); while ((sig = eina_array_pop(interface->sign_of_signals))) eina_stringshare_del(sig); eina_array_free(interface->sign_of_signals); eina_hash_free(interface->properties); if (interface->props_changed) eina_array_free(interface->props_changed); if (interface->idler_propschanged) ecore_idler_del(interface->idler_propschanged); if (interface->prop_invalidated) eina_array_free(interface->prop_invalidated); parent = _find_object_manager_parent(interface->obj); if (parent) { struct iface_remove_data *data; data = malloc(sizeof(struct iface_remove_data)); EINA_SAFETY_ON_NULL_GOTO(data, end); data->obj_path = eina_stringshare_add(interface->obj->path); data->iface = eina_stringshare_add(interface->name); if (!parent->idler_iface_changed) parent->idler_iface_changed = ecore_idler_add(_iface_changed_send, parent); parent->iface_removed = eina_list_append(parent->iface_removed, data); parent->iface_added = eina_list_remove(parent->iface_added, interface); } end: eina_stringshare_del(interface->name); free(interface); } static void _object_free(EDBus_Service_Object *obj) { Eina_Iterator *iterator; EDBus_Service_Interface *iface; struct iface_remove_data *data; iterator = eina_hash_iterator_data_new(obj->interfaces); EINA_ITERATOR_FOREACH(iterator, iface) _interface_free(iface); while (obj->children) { EDBus_Service_Object *child; child = EINA_INLIST_CONTAINER_GET(obj->children, EDBus_Service_Object); obj->children = eina_inlist_remove(obj->children, obj->children); if (obj->parent) { obj->parent->children = eina_inlist_append(obj->parent->children, EINA_INLIST_GET(child)); child->parent = obj->parent; } else { obj->conn->root_objs = eina_inlist_append(obj->conn->root_objs, EINA_INLIST_GET(child)); child->parent = NULL; } } if (obj->parent) obj->parent->children = eina_inlist_remove(obj->parent->children, EINA_INLIST_GET(obj)); else obj->conn->root_objs = eina_inlist_remove(obj->conn->root_objs, EINA_INLIST_GET(obj)); edbus_data_del_all(&obj->data); EINA_LIST_FREE(obj->iface_removed, data) { eina_stringshare_del(data->iface); eina_stringshare_del(data->obj_path); free(data); } eina_list_free(obj->iface_added); if (obj->idler_iface_changed) ecore_idler_del(obj->idler_iface_changed); eina_hash_free(obj->interfaces); eina_iterator_free(iterator); if (obj->introspection_data) eina_strbuf_free(obj->introspection_data); eina_stringshare_del(obj->path); free(obj); } static void _on_connection_free(void *data, const void *dead_pointer) { EDBus_Service_Object *obj = data; dbus_connection_unregister_object_path(obj->conn->dbus_conn, obj->path); } EAPI void edbus_service_interface_unregister(EDBus_Service_Interface *iface) { EDBUS_SERVICE_INTERFACE_CHECK(iface); if (!eina_hash_find(iface->obj->interfaces, objmanager->name)) { //properties + introspectable + iface that user wants unregister if (eina_hash_population(iface->obj->interfaces) < 4) edbus_service_object_unregister(iface); return; } eina_hash_del(iface->obj->interfaces, NULL, iface); iface->obj->introspection_dirty = EINA_TRUE; _interface_free(iface); } EAPI void edbus_service_object_unregister(EDBus_Service_Interface *iface) { EDBUS_SERVICE_INTERFACE_CHECK(iface); /* * It will be freed when _object_unregister() is called * by libdbus. */ edbus_connection_free_cb_del(iface->obj->conn, _on_connection_free, iface->obj); dbus_connection_unregister_object_path(iface->obj->conn->dbus_conn, iface->obj->path); } static void _object_unregister(DBusConnection *conn, void *user_data) { EDBus_Service_Object *obj = user_data; _object_free(obj); } static DBusHandlerResult _object_handler(DBusConnection *conn, DBusMessage *msg, void *user_data) { EDBus_Service_Object *obj; EDBus_Service_Interface *iface; const EDBus_Method *method; EDBus_Message *edbus_msg; EDBus_Message *reply; obj = user_data; if (!obj) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; DBG("Connection@%p Got message:\n" " Type: %s\n" " Path: %s\n" " Interface: %s\n" " Member: %s\n" " Sender: %s", obj->conn, dbus_message_type_to_string(dbus_message_get_type(msg)), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), dbus_message_get_sender(msg)); iface = eina_hash_find(obj->interfaces, dbus_message_get_interface(msg)); if (!iface) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; method = eina_hash_find(iface->methods, dbus_message_get_member(msg)); if (!method) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; edbus_msg = edbus_message_new(EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(edbus_msg, DBUS_HANDLER_RESULT_NEED_MEMORY); edbus_msg->dbus_msg = msg; dbus_message_iter_init(edbus_msg->dbus_msg, &edbus_msg->iterator->dbus_iterator); if (!_have_signature(method->in, edbus_msg)) { reply = edbus_message_error_new(edbus_msg, DBUS_ERROR_INVALID_SIGNATURE, "See introspectable to know the expected signature"); } else { if (iface->obj) reply = method->cb(iface, edbus_msg); else { //if iface does have obj it is some of FreeDesktop interfaces: //Introspectable, Properties... EDBus_Service_Interface *cpy; cpy = calloc(1, sizeof(EDBus_Service_Interface)); if (!cpy) { dbus_message_ref(edbus_msg->dbus_msg); edbus_message_unref(edbus_msg); return DBUS_HANDLER_RESULT_NEED_MEMORY; } cpy->obj = obj; reply = method->cb(cpy, edbus_msg); free(cpy); } } dbus_message_ref(edbus_msg->dbus_msg); edbus_message_unref(edbus_msg); if (!reply) return DBUS_HANDLER_RESULT_HANDLED; _edbus_connection_send(obj->conn, reply, NULL, NULL, -1); edbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } EAPI EDBus_Connection * edbus_service_connection_get(const EDBus_Service_Interface *iface) { EDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, NULL); return iface->obj->conn; } EAPI const char * edbus_service_object_path_get(const EDBus_Service_Interface *iface) { EDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, NULL); return iface->obj->path; } EAPI EDBus_Message * edbus_service_signal_new(const EDBus_Service_Interface *iface, unsigned int signal_id) { unsigned size; EDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, EINA_FALSE); size = eina_array_count(iface->sign_of_signals); EINA_SAFETY_ON_FALSE_RETURN_VAL(signal_id < size, EINA_FALSE); return edbus_message_signal_new(iface->obj->path, iface->name, iface->signals[signal_id].name); } EAPI Eina_Bool edbus_service_signal_emit(const EDBus_Service_Interface *iface, unsigned int signal_id, ...) { EDBus_Message *sig; va_list ap; Eina_Bool r; const char *signature; unsigned size; EDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, EINA_FALSE); size = eina_array_count(iface->sign_of_signals); EINA_SAFETY_ON_FALSE_RETURN_VAL(signal_id < size, EINA_FALSE); sig = edbus_service_signal_new(iface, signal_id); EINA_SAFETY_ON_NULL_RETURN_VAL(sig, EINA_FALSE); signature = eina_array_data_get(iface->sign_of_signals, signal_id); va_start(ap, signal_id); r = edbus_message_arguments_vappend(sig, signature, ap); va_end(ap); EINA_SAFETY_ON_FALSE_RETURN_VAL(r, EINA_FALSE); edbus_service_signal_send(iface, sig); return EINA_TRUE; } EAPI Eina_Bool edbus_service_signal_send(const EDBus_Service_Interface *iface, EDBus_Message *signal_msg) { EDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(signal_msg, EINA_FALSE); _edbus_connection_send(iface->obj->conn, signal_msg, NULL, NULL, -1); edbus_message_unref(signal_msg); return EINA_TRUE; } EAPI void edbus_service_object_data_set(EDBus_Service_Interface *iface, const char *key, const void *data) { EDBUS_SERVICE_INTERFACE_CHECK(iface); EINA_SAFETY_ON_NULL_RETURN(key); EINA_SAFETY_ON_NULL_RETURN(data); edbus_data_set(&(iface->obj->data), key, data); } EAPI void * edbus_service_object_data_get(const EDBus_Service_Interface *iface, const char *key) { EDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL); return edbus_data_get(&(((EDBus_Service_Object *)iface->obj)->data), key); } EAPI void * edbus_service_object_data_del(EDBus_Service_Interface *iface, const char *key) { EDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL); return edbus_data_del(&(((EDBus_Service_Object *)iface->obj)->data), key); } static Eina_Bool _idler_propschanged(void *data) { EDBus_Service_Interface *iface = data; EDBus_Message *msg; EDBus_Message_Iter *main_iter, *dict, *array_invalidate; Eina_Hash *added = NULL; Property *prop; iface->idler_propschanged = NULL; added = eina_hash_string_small_new(NULL); msg = edbus_message_signal_new(iface->obj->path, properties_iface->name, properties_iface->signals[0].name); EINA_SAFETY_ON_NULL_GOTO(msg, error); main_iter = edbus_message_iter_get(msg); if (!edbus_message_iter_arguments_append(main_iter, "sa{sv}", iface->name, &dict)) { edbus_message_unref(msg); goto error; } if (!iface->props_changed) goto invalidate; while ((prop = eina_array_pop(iface->props_changed))) { EDBus_Message_Iter *entry, *var; EDBus_Message *error_reply = NULL; Eina_Bool ret; EDBus_Property_Get_Cb getter = NULL; if (eina_hash_find(added, prop->property->name)) continue; eina_hash_add(added, prop->property->name, prop); if (prop->property->get_func) getter = prop->property->get_func; else if (iface->get_func) getter = iface->get_func; if (!getter || prop->is_invalidate) continue; EINA_SAFETY_ON_FALSE_GOTO( edbus_message_iter_arguments_append(dict, "{sv}", &entry), error); edbus_message_iter_basic_append(entry, 's', prop->property->name); var = edbus_message_iter_container_new(entry, 'v', prop->property->type); ret = getter(iface, prop->property->name, var, NULL, &error_reply); if (!ret) { edbus_message_unref(msg); if (error_reply) { ERR("Error reply was set without pass any input message."); edbus_message_unref(error_reply); } ERR("Getter of property %s returned error.", prop->property->name); goto error; } edbus_message_iter_container_close(entry, var); edbus_message_iter_container_close(dict, entry); } invalidate: edbus_message_iter_container_close(main_iter, dict); edbus_message_iter_arguments_append(main_iter, "as", &array_invalidate); if (!iface->prop_invalidated) goto end; while ((prop = eina_array_pop(iface->prop_invalidated))) { if (!prop->is_invalidate) continue; edbus_message_iter_basic_append(array_invalidate, 's', prop->property->name); } end: edbus_message_iter_container_close(main_iter, array_invalidate); edbus_service_signal_send(iface, msg); error: if (added) eina_hash_free(added); if (iface->props_changed) eina_array_flush(iface->props_changed); if (iface->prop_invalidated) eina_array_flush(iface->prop_invalidated); return ECORE_CALLBACK_CANCEL; } EAPI Eina_Bool edbus_service_property_changed(const EDBus_Service_Interface *interface, const char *name) { Property *prop; EDBus_Service_Interface *iface = (EDBus_Service_Interface *)interface; EDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); prop = eina_hash_find(iface->properties, name); EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE); if (!iface->idler_propschanged) iface->idler_propschanged = ecore_idler_add(_idler_propschanged, iface); if (!iface->props_changed) iface->props_changed = eina_array_new(1); return eina_array_push(iface->props_changed, prop); } EAPI Eina_Bool edbus_service_property_invalidate_set(const EDBus_Service_Interface *interface, const char *name, Eina_Bool is_invalidate) { Property *prop; EDBus_Service_Interface *iface = (EDBus_Service_Interface *) interface; EDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); prop = eina_hash_find(iface->properties, name); EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE); if (prop->is_invalidate == is_invalidate) return EINA_TRUE; prop->is_invalidate = is_invalidate; if (!iface->idler_propschanged) iface->idler_propschanged = ecore_idler_add(_idler_propschanged, iface); if (is_invalidate) { if (!iface->props_changed) iface->props_changed = eina_array_new(1); return eina_array_push(iface->props_changed, prop); } if (!iface->prop_invalidated) iface->prop_invalidated = eina_array_new(1); return eina_array_push(iface->prop_invalidated, prop); } EAPI Eina_Bool edbus_service_object_manager_attach(EDBus_Service_Interface *iface) { EDBus_Service_Object *obj; EDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, EINA_FALSE); obj = iface->obj; if (!eina_hash_find(obj->interfaces, objmanager->name)) if (!eina_hash_add(obj->interfaces, objmanager->name, objmanager)) return EINA_FALSE; obj->has_objectmanager = EINA_TRUE; obj->introspection_dirty = EINA_TRUE; return EINA_TRUE; } EAPI Eina_Bool edbus_service_object_manager_detach(EDBus_Service_Interface *iface) { EDBus_Service_Object *obj; Eina_Bool ret; EDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, EINA_FALSE); obj = iface->obj; ret = eina_hash_del(obj->interfaces, objmanager->name, NULL); obj->has_objectmanager = EINA_FALSE; obj->introspection_dirty = EINA_TRUE; //properties + introspectable if (eina_hash_population(iface->obj->interfaces) < 3) edbus_service_object_unregister(iface); return ret; }