X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gdbus%2Fobject.c;h=c333d5b702438de733ea81fb76f05569afebaaca;hb=f2074eea6e8ead5bde4d712f0707cf6a3e025fb8;hp=fef663574abeb5c25b0731159e0896de31053d1c;hpb=9614c2022b5ca0decd8b97e4582c7626c305f03d;p=platform%2Fupstream%2Fconnman.git diff --git a/gdbus/object.c b/gdbus/object.c old mode 100644 new mode 100755 index fef6635..c333d5b --- a/gdbus/object.c +++ b/gdbus/object.c @@ -39,6 +39,14 @@ #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 + struct generic_data { unsigned int refcount; DBusConnection *conn; @@ -48,6 +56,7 @@ struct generic_data { GSList *added; GSList *removed; guint process_id; + gboolean pending_prop; char *introspect; struct generic_data *parent; }; @@ -57,6 +66,7 @@ struct interface_data { const GDBusMethodTable *methods; const GDBusSignalTable *signals; const GDBusPropertyTable *properties; + GSList *pending_prop; void *user_data; GDBusDestroyFunction destroy; }; @@ -69,18 +79,26 @@ struct security_data { }; struct property_data { + DBusConnection *conn; GDBusPendingPropertySet id; DBusMessage *message; }; +static int global_flags = 0; +static struct generic_data *root; +static GSList *pending = NULL; + static gboolean process_changes(gpointer user_data); +static void process_properties_from_interface(struct generic_data *data, + struct interface_data *iface); +static void process_property_changes(struct generic_data *data); static void print_arguments(GString *gstr, const GDBusArgInfo *args, const char *direction) { for (; args && args->name; args++) { g_string_append_printf(gstr, - "\t\t\tname, args->signature); if (direction) @@ -92,15 +110,23 @@ static void print_arguments(GString *gstr, const GDBusArgInfo *args, } } -#define G_DBUS_ANNOTATE(prefix_, name_, value_) \ - prefix_ "\n" +#define G_DBUS_ANNOTATE(name_, value_) \ + "" -#define G_DBUS_ANNOTATE_DEPRECATED(prefix_) \ - G_DBUS_ANNOTATE(prefix_, "Deprecated", "true") +#define G_DBUS_ANNOTATE_DEPRECATED \ + G_DBUS_ANNOTATE("Deprecated", "true") -#define G_DBUS_ANNOTATE_NOREPLY(prefix_) \ - G_DBUS_ANNOTATE(prefix_, "Method.NoReply", "true") +#define G_DBUS_ANNOTATE_NOREPLY \ + G_DBUS_ANNOTATE("Method.NoReply", "true") + +static gboolean check_experimental(int flags, int flag) +{ + if (!(flags & flag)) + return FALSE; + + return !(global_flags & G_DBUS_FLAG_ENABLE_EXPERIMENTAL); +} static void generate_interface_xml(GString *gstr, struct interface_data *iface) { @@ -109,73 +135,58 @@ static void generate_interface_xml(GString *gstr, struct interface_data *iface) const GDBusPropertyTable *property; for (method = iface->methods; method && method->name; method++) { - gboolean deprecated = method->flags & - G_DBUS_METHOD_FLAG_DEPRECATED; - gboolean noreply = method->flags & - G_DBUS_METHOD_FLAG_NOREPLY; - - if (!deprecated && !noreply && - !(method->in_args && method->in_args->name) && - !(method->out_args && method->out_args->name)) - g_string_append_printf(gstr, - "\t\t\n", - method->name); - else { + if (check_experimental(method->flags, + G_DBUS_METHOD_FLAG_EXPERIMENTAL)) + continue; + + g_string_append_printf(gstr, "", + method->name); + print_arguments(gstr, method->in_args, "in"); + print_arguments(gstr, method->out_args, "out"); + + if (method->flags & G_DBUS_METHOD_FLAG_DEPRECATED) g_string_append_printf(gstr, - "\t\t\n", - method->name); - print_arguments(gstr, method->in_args, "in"); - print_arguments(gstr, method->out_args, "out"); - - if (deprecated) - g_string_append_printf(gstr, - G_DBUS_ANNOTATE_DEPRECATED("\t\t\t")); - if (noreply) - g_string_append_printf(gstr, - G_DBUS_ANNOTATE_NOREPLY("\t\t\t")); - - g_string_append_printf(gstr, "\t\t\n"); - } + G_DBUS_ANNOTATE_DEPRECATED); + + if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) + g_string_append_printf(gstr, G_DBUS_ANNOTATE_NOREPLY); + + g_string_append_printf(gstr, ""); } for (signal = iface->signals; signal && signal->name; signal++) { - gboolean deprecated = signal->flags & - G_DBUS_SIGNAL_FLAG_DEPRECATED; + if (check_experimental(signal->flags, + G_DBUS_SIGNAL_FLAG_EXPERIMENTAL)) + continue; - if (!deprecated && !(signal->args && signal->args->name)) - g_string_append_printf(gstr, - "\t\t\n", - signal->name); - else { - g_string_append_printf(gstr, - "\t\t\n", - signal->name); - print_arguments(gstr, signal->args, NULL); + g_string_append_printf(gstr, "", + signal->name); + print_arguments(gstr, signal->args, NULL); - if (deprecated) - g_string_append_printf(gstr, - G_DBUS_ANNOTATE_DEPRECATED("\t\t\t")); + if (signal->flags & G_DBUS_SIGNAL_FLAG_DEPRECATED) + g_string_append_printf(gstr, + G_DBUS_ANNOTATE_DEPRECATED); - g_string_append_printf(gstr, "\t\t\n"); - } + g_string_append_printf(gstr, "\n"); } for (property = iface->properties; property && property->name; property++) { - gboolean deprecated = property->flags & - G_DBUS_PROPERTY_FLAG_DEPRECATED; + if (check_experimental(property->flags, + G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) + continue; - g_string_append_printf(gstr, "\t\t", property->name, property->type, property->get ? "read" : "", property->set ? "write" : ""); - if (!deprecated) - g_string_append_printf(gstr, "/>\n"); - else + if (property->flags & G_DBUS_PROPERTY_FLAG_DEPRECATED) g_string_append_printf(gstr, - G_DBUS_ANNOTATE_DEPRECATED(">\n\t\t\t")); + G_DBUS_ANNOTATE_DEPRECATED); + + g_string_append_printf(gstr, ""); } } @@ -191,30 +202,30 @@ static void generate_introspection_xml(DBusConnection *conn, gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); - g_string_append_printf(gstr, "\n"); + g_string_append_printf(gstr, ""); for (list = data->interfaces; list; list = list->next) { struct interface_data *iface = list->data; - g_string_append_printf(gstr, "\t\n", + g_string_append_printf(gstr, "", iface->name); generate_interface_xml(gstr, iface); - g_string_append_printf(gstr, "\t\n"); + g_string_append_printf(gstr, ""); } if (!dbus_connection_list_registered(conn, path, &children)) goto done; for (i = 0; children[i]; i++) - g_string_append_printf(gstr, "\t\n", + g_string_append_printf(gstr, "", children[i]); dbus_free_string_array(children); done: - g_string_append_printf(gstr, "\n"); + g_string_append_printf(gstr, ""); data->introspect = g_string_free(gstr, FALSE); } @@ -247,7 +258,8 @@ static DBusHandlerResult process_message(DBusConnection *connection, reply = method->function(connection, message, iface_user_data); - if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) { + 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; @@ -261,8 +273,7 @@ static DBusHandlerResult process_message(DBusConnection *connection, if (reply == NULL) return DBUS_HANDLER_RESULT_NEED_MEMORY; - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); + g_dbus_send_message(connection, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -277,7 +288,7 @@ void g_dbus_pending_success(DBusConnection *connection, { GSList *list; - for (list = pending_security; list; list = list->next) { + for (list = pending_security; list; list = list->next) { struct security_data *secdata = list->data; if (secdata->pending != pending) @@ -291,7 +302,7 @@ void g_dbus_pending_success(DBusConnection *connection, dbus_message_unref(secdata->message); g_free(secdata); return; - } + } } void g_dbus_pending_error_valist(DBusConnection *connection, @@ -300,26 +311,21 @@ void g_dbus_pending_error_valist(DBusConnection *connection, { GSList *list; - for (list = pending_security; list; list = list->next) { + for (list = pending_security; list; list = list->next) { struct security_data *secdata = list->data; - DBusMessage *reply; if (secdata->pending != pending) continue; pending_security = g_slist_remove(pending_security, secdata); - reply = g_dbus_create_error_valist(secdata->message, + g_dbus_send_error_valist(connection, secdata->message, name, format, args); - if (reply != NULL) { - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } dbus_message_unref(secdata->message); g_free(secdata); return; - } + } } void g_dbus_pending_error(DBusConnection *connection, @@ -428,6 +434,8 @@ static struct property_data *remove_pending_property_data( propdata = l->data; if (propdata->id != id) continue; + + break; } if (l == NULL) @@ -438,8 +446,7 @@ static struct property_data *remove_pending_property_data( return propdata; } -void g_dbus_pending_property_success(DBusConnection *connection, - GDBusPendingPropertySet id) +void g_dbus_pending_property_success(GDBusPendingPropertySet id) { struct property_data *propdata; @@ -447,43 +454,37 @@ void g_dbus_pending_property_success(DBusConnection *connection, if (propdata == NULL) return; - g_dbus_send_reply(connection, propdata->message, DBUS_TYPE_INVALID); + g_dbus_send_reply(propdata->conn, propdata->message, + DBUS_TYPE_INVALID); dbus_message_unref(propdata->message); g_free(propdata); } -void g_dbus_pending_property_error_valist(DBusConnection *connection, - GDBusPendingReply id, const char *name, - const char *format, va_list args) +void g_dbus_pending_property_error_valist(GDBusPendingReply id, + const char *name, const char *format, + va_list args) { struct property_data *propdata; - DBusMessage *reply; propdata = remove_pending_property_data(id); if (propdata == NULL) return; - reply = g_dbus_create_error_valist(propdata->message, name, format, - args); - if (reply != NULL) { - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } + g_dbus_send_error_valist(propdata->conn, propdata->message, name, + format, args); dbus_message_unref(propdata->message); g_free(propdata); } -void g_dbus_pending_property_error(DBusConnection *connection, - GDBusPendingReply id, const char *name, - const char *format, ...) +void g_dbus_pending_property_error(GDBusPendingReply id, const char *name, + const char *format, ...) { va_list args; va_start(args, format); - g_dbus_pending_property_error_valist(connection, id, name, format, - args); + g_dbus_pending_property_error_valist(id, name, format, args); va_end(args); } @@ -496,10 +497,28 @@ static void reset_parent(gpointer data, gpointer 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; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING @@ -507,7 +526,19 @@ static void append_properties(struct interface_data *data, DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); - /* TODO: list properties */ + for (p = data->properties; p && p->name; p++) { + if (check_experimental(p->flags, + G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) + continue; + + if (p->get == NULL) + continue; + + if (p->exists != NULL && !p->exists(p, data->user_data)) + continue; + + append_property(data, p, &dict); + } dbus_message_iter_close_container(iter, &dict); } @@ -529,12 +560,11 @@ static void emit_interfaces_added(struct generic_data *data) { DBusMessage *signal; DBusMessageIter iter, array; - struct generic_data *parent = data->parent; - if (parent == NULL) + if (root == NULL || data == root) return; - signal = dbus_message_new_signal(parent->path, + signal = dbus_message_new_signal(root->path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded"); if (signal == NULL) @@ -560,7 +590,9 @@ static void emit_interfaces_added(struct generic_data *data) dbus_message_iter_close_container(&iter, &array); - g_dbus_send_message(data->conn, signal); + /* Use dbus_connection_send to avoid recursive calls to g_dbus_flush */ + dbus_connection_send(data->conn, signal, NULL); + dbus_message_unref(signal); } static struct interface_data *find_interface(GSList *interfaces, @@ -601,6 +633,16 @@ static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args, return TRUE; } +static void add_pending(struct generic_data *data) +{ + if (data->process_id > 0) + return; + + data->process_id = g_idle_add(process_changes, data); + + pending = g_slist_append(pending, data); +} + static gboolean remove_interface(struct generic_data *data, const char *name) { struct interface_data *iface; @@ -609,6 +651,8 @@ static gboolean remove_interface(struct generic_data *data, const char *name) if (iface == NULL) return FALSE; + process_properties_from_interface(data, iface); + data->interfaces = g_slist_remove(data->interfaces, iface); if (iface->destroy) { @@ -616,6 +660,17 @@ static gboolean remove_interface(struct generic_data *data, const char *name) iface->user_data = NULL; } + /* + * Interface being removed was just added, on the same mainloop + * iteration? Don't send any signal + */ + if (g_slist_find(data->added, iface)) { + data->added = g_slist_remove(data->added, iface); + g_free(iface->name); + g_free(iface); + return TRUE; + } + if (data->parent == NULL) { g_free(iface->name); g_free(iface); @@ -625,10 +680,7 @@ static gboolean remove_interface(struct generic_data *data, const char *name) data->removed = g_slist_prepend(data->removed, iface->name); g_free(iface); - if (data->process_id > 0) - return TRUE; - - data->process_id = g_idle_add(process_changes, data); + add_pending(data); return TRUE; } @@ -689,8 +741,14 @@ static inline const GDBusPropertyTable *find_property(const GDBusPropertyTable * const GDBusPropertyTable *p; for (p = properties; p && p->name; p++) { - if (strcmp(name, p->name) == 0) - return p; + if (strcmp(name, p->name) != 0) + continue; + + if (check_experimental(p->flags, + G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) + break; + + return p; } return NULL; @@ -754,9 +812,8 @@ static DBusMessage *properties_get_all(DBusConnection *connection, { struct generic_data *data = user_data; struct interface_data *iface; - const GDBusPropertyTable *p; const char *interface; - DBusMessageIter iter, dict; + DBusMessageIter iter; DBusMessage *reply; if (!dbus_message_get_args(message, NULL, @@ -774,37 +831,8 @@ static DBusMessage *properties_get_all(DBusConnection *connection, 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_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); - - for (p = iface->properties; p && p->name; p++) { - DBusMessageIter entry, value; - - if (p->get == NULL) - continue; - if (p->exists != NULL && !p->exists(p, iface->user_data)) - continue; - - 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); - - if (!p->get(p, &value, iface->user_data)) { - dbus_message_unref(reply); - return NULL; - } - - dbus_message_iter_close_container(&entry, &value); - dbus_message_iter_close_container(&dict, &entry); - } - - dbus_message_iter_close_container(&iter, &dict); + append_properties(iface, &iter); return reply; } @@ -818,6 +846,8 @@ static DBusMessage *properties_set(DBusConnection *connection, const GDBusPropertyTable *property; const char *name, *interface; struct property_data *propdata; + gboolean valid_signature; + char *signature; if (!dbus_message_iter_init(message, &iter)) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, @@ -828,7 +858,7 @@ static DBusMessage *properties_set(DBusConnection *connection, "Invalid argument type: '%c'", dbus_message_iter_get_arg_type(&iter)); - dbus_message_iter_get_basic(&iter, &name); + dbus_message_iter_get_basic(&iter, &interface); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) @@ -836,7 +866,7 @@ static DBusMessage *properties_set(DBusConnection *connection, "Invalid argument type: '%c'", dbus_message_iter_get_arg_type(&iter)); - dbus_message_iter_get_basic(&iter, &interface); + dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) @@ -868,9 +898,19 @@ static DBusMessage *properties_set(DBusConnection *connection, DBUS_ERROR_UNKNOWN_PROPERTY, "No such property '%s'", name); + signature = dbus_message_iter_get_signature(&sub); + valid_signature = strcmp(signature, property->type) ? FALSE : TRUE; + dbus_free(signature); + if (!valid_signature) + return g_dbus_create_error(message, + DBUS_ERROR_INVALID_SIGNATURE, + "Invalid signature for '%s'", name); + propdata = g_new(struct property_data, 1); propdata->id = next_pending_property++; propdata->message = dbus_message_ref(message); + propdata->conn = connection; + pending_property_set = g_slist_prepend(pending_property_set, propdata); property->set(property, &sub, propdata->id, iface->user_data); @@ -882,9 +922,10 @@ static const GDBusMethodTable properties_methods[] = { GDBUS_ARGS({ "interface", "s" }, { "name", "s" }), GDBUS_ARGS({ "value", "v" }), properties_get) }, - { GDBUS_ASYNC_METHOD("Set", NULL, + { GDBUS_ASYNC_METHOD("Set", GDBUS_ARGS({ "interface", "s" }, { "name", "s" }, { "value", "v" }), + NULL, properties_set) }, { GDBUS_METHOD("GetAll", GDBUS_ARGS({ "interface", "s" }), @@ -913,12 +954,11 @@ static void emit_interfaces_removed(struct generic_data *data) { DBusMessage *signal; DBusMessageIter iter, array; - struct generic_data *parent = data->parent; - if (parent == NULL) + if (root == NULL || data == root) return; - signal = dbus_message_new_signal(parent->path, + signal = dbus_message_new_signal(root->path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved"); if (signal == NULL) @@ -936,21 +976,39 @@ static void emit_interfaces_removed(struct generic_data *data) dbus_message_iter_close_container(&iter, &array); - g_dbus_send_message(data->conn, signal); + /* Use dbus_connection_send to avoid recursive calls to g_dbus_flush */ + dbus_connection_send(data->conn, signal, NULL); + dbus_message_unref(signal); +} + +static void remove_pending(struct generic_data *data) +{ + if (data->process_id > 0) { + g_source_remove(data->process_id); + data->process_id = 0; + } + + pending = g_slist_remove(pending, data); } static gboolean process_changes(gpointer user_data) { struct generic_data *data = user_data; - data->process_id = 0; + 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); + data->process_id = 0; + return FALSE; } @@ -964,8 +1022,8 @@ static void generic_unregister(DBusConnection *connection, void *user_data) if (data->process_id > 0) { g_source_remove(data->process_id); - if (data->removed != NULL) - emit_interfaces_removed(data); + data->process_id = 0; + process_changes(data); } g_slist_foreach(data->objects, reset_parent, data->parent); @@ -993,10 +1051,15 @@ static DBusHandlerResult generic_message(DBusConnection *connection, 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 (g_dbus_args_have_signature(method->in_args, message) == FALSE) continue; @@ -1054,6 +1117,8 @@ static void append_object(gpointer data, gpointer user_data) &child->path); append_interfaces(child, &entry); dbus_message_iter_close_container(array, &entry); + + g_slist_foreach(child->objects, append_object, user_data); } static DBusMessage *get_objects(DBusConnection *connection, @@ -1107,7 +1172,7 @@ static const GDBusSignalTable manager_signals[] = { { } }; -static void add_interface(struct generic_data *data, +static gboolean add_interface(struct generic_data *data, const char *name, const GDBusMethodTable *methods, const GDBusSignalTable *signals, @@ -1116,7 +1181,32 @@ static void add_interface(struct generic_data *data, GDBusDestroyFunction destroy) { struct interface_data *iface; + const GDBusMethodTable *method; + const GDBusSignalTable *signal; + const GDBusPropertyTable *property; + + for (method = methods; method && method->name; method++) { + if (!check_experimental(method->flags, + G_DBUS_METHOD_FLAG_EXPERIMENTAL)) + goto done; + } + + for (signal = signals; signal && signal->name; signal++) { + if (!check_experimental(signal->flags, + G_DBUS_SIGNAL_FLAG_EXPERIMENTAL)) + goto done; + } + for (property = properties; property && property->name; property++) { + if (!check_experimental(property->flags, + G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) + goto done; + } + + /* Nothing to register */ + return FALSE; + +done: iface = g_new0(struct interface_data, 1); iface->name = g_strdup(name); iface->methods = methods; @@ -1127,13 +1217,13 @@ static void add_interface(struct generic_data *data, data->interfaces = g_slist_append(data->interfaces, iface); if (data->parent == NULL) - return; + return TRUE; data->added = g_slist_append(data->added, iface); - if (data->process_id > 0) - return; - data->process_id = g_idle_add(process_changes, data); + add_pending(data); + + return TRUE; } static struct generic_data *object_path_ref(DBusConnection *connection, @@ -1158,6 +1248,10 @@ static struct generic_data *object_path_ref(DBusConnection *connection, if (!dbus_connection_register_object_path(connection, path, &generic_table, data)) { +#if !defined TIZEN_EXT + dbus_connection_unref(data->conn); + g_free(data->path); +#endif g_free(data->introspect); g_free(data); return NULL; @@ -1168,12 +1262,6 @@ static struct generic_data *object_path_ref(DBusConnection *connection, add_interface(data, DBUS_INTERFACE_INTROSPECTABLE, introspect_methods, NULL, NULL, data, NULL); - add_interface(data, DBUS_INTERFACE_OBJECT_MANAGER, manager_methods, - manager_signals, NULL, data, NULL); - - add_interface(data, DBUS_INTERFACE_PROPERTIES, properties_methods, - properties_signals, NULL, data, NULL); - return data; } @@ -1195,7 +1283,6 @@ static void object_path_unref(DBusConnection *connection, const char *path) remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE); remove_interface(data, DBUS_INTERFACE_PROPERTIES); - remove_interface(data, DBUS_INTERFACE_OBJECT_MANAGER); invalidate_parent_data(data->conn, data->path); @@ -1226,55 +1313,23 @@ static gboolean check_signal(DBusConnection *conn, const char *path, } for (signal = iface->signals; signal && signal->name; signal++) { - if (!strcmp(signal->name, name)) { - *args = signal->args; - return TRUE; + if (strcmp(signal->name, name) != 0) + continue; + + if (signal->flags & G_DBUS_SIGNAL_FLAG_EXPERIMENTAL) { + const char *env = g_getenv("GDBUS_EXPERIMENTAL"); + if (g_strcmp0(env, "1") != 0) + break; } + + *args = signal->args; + return TRUE; } error("No signal named %s on interface %s", name, interface); return FALSE; } -static dbus_bool_t emit_signal_valist(DBusConnection *conn, - const char *path, - const char *interface, - const char *name, - int first, - va_list var_args) -{ - DBusMessage *signal; - dbus_bool_t ret; - const GDBusArgInfo *args; - - if (!check_signal(conn, path, interface, name, &args)) - 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, first, var_args); - if (!ret) - goto fail; - - if (g_dbus_args_have_signature(args, signal) == FALSE) { - error("%s.%s: got unexpected signature '%s'", interface, name, - dbus_message_get_signature(signal)); - ret = FALSE; - goto fail; - } - - ret = dbus_connection_send(conn, signal, NULL); - -fail: - dbus_message_unref(signal); - - return ret; -} - gboolean g_dbus_register_interface(DBusConnection *connection, const char *path, const char *name, const GDBusMethodTable *methods, @@ -1294,8 +1349,17 @@ gboolean g_dbus_register_interface(DBusConnection *connection, return FALSE; } - add_interface(data, name, methods, signals, properties, user_data, - destroy); + 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); g_free(data->introspect); data->introspect = NULL; @@ -1351,7 +1415,10 @@ DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name, { char str[1024]; - vsnprintf(str, sizeof(str), format, args); + if (format) + vsnprintf(str, sizeof(str), format, args); + else + str[0] = '\0'; return dbus_message_new_error(message, name, str); } @@ -1402,20 +1469,97 @@ DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...) return reply; } +static void g_dbus_flush(DBusConnection *connection) +{ + GSList *l; + + for (l = pending; l;) { + struct generic_data *data = l->data; + + l = l->next; + if (data->conn != connection) + continue; + + process_changes(data); + } +} + gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message) { - dbus_bool_t result; + dbus_bool_t result = 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 */ + g_dbus_flush(connection); result = dbus_connection_send(connection, message, NULL); +out: dbus_message_unref(message); return result; } +gboolean g_dbus_send_message_with_reply(DBusConnection *connection, + DBusMessage *message, + DBusPendingCall **call, int timeout) +{ + dbus_bool_t ret; + + /* Flush pending signal to guarantee message order */ + g_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 g_dbus_send_error_valist(DBusConnection *connection, + DBusMessage *message, const char *name, + const char *format, va_list args) +{ + DBusMessage *error; + + error = g_dbus_create_error_valist(message, name, format, args); + if (error == NULL) + return FALSE; + + return g_dbus_send_message(connection, error); +} + +gboolean g_dbus_send_error(DBusConnection *connection, DBusMessage *message, + const char *name, const char *format, ...) +{ + va_list args; + gboolean result; + + va_start(args, format); + + result = g_dbus_send_error_valist(connection, message, name, + format, args); + + va_end(args); + + return result; +} + gboolean g_dbus_send_reply_valist(DBusConnection *connection, DBusMessage *message, int type, va_list args) { @@ -1457,7 +1601,7 @@ gboolean g_dbus_emit_signal(DBusConnection *connection, va_start(args, type); - result = emit_signal_valist(connection, path, interface, + result = g_dbus_emit_signal_valist(connection, path, interface, name, type, args); va_end(args); @@ -1469,6 +1613,224 @@ gboolean g_dbus_emit_signal_valist(DBusConnection *connection, const char *path, const char *interface, const char *name, int type, va_list args) { - return emit_signal_valist(connection, path, interface, - name, type, 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 (g_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 g_dbus_send_message(connection, signal); + +fail: + dbus_message_unref(signal); + + return ret; +} + +static void process_properties_from_interface(struct generic_data *data, + struct interface_data *iface) +{ + GSList *l; + DBusMessage *signal; + DBusMessageIter iter, dict, array; + GSList *invalidated; + + data->pending_prop = FALSE; + + if (iface->pending_prop == NULL) + return; + + signal = dbus_message_new_signal(data->path, + DBUS_INTERFACE_PROPERTIES, "PropertiesChanged"); + if (signal == NULL) { + error("Unable to allocate new " DBUS_INTERFACE_PROPERTIES + ".PropertiesChanged signal"); + return; + } + + iface->pending_prop = g_slist_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); + + invalidated = NULL; + + for (l = iface->pending_prop; l != NULL; l = l->next) { + GDBusPropertyTable *p = l->data; + + if (p->get == NULL) + continue; + + if (p->exists != NULL && !p->exists(p, iface->user_data)) { + invalidated = g_slist_prepend(invalidated, p); + continue; + } + + append_property(iface, p, &dict); + } + + dbus_message_iter_close_container(&iter, &dict); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &array); + for (l = invalidated; l != NULL; l = g_slist_next(l)) { + GDBusPropertyTable *p = l->data; + + dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, + &p->name); + } + g_slist_free(invalidated); + dbus_message_iter_close_container(&iter, &array); + + g_slist_free(iface->pending_prop); + iface->pending_prop = NULL; + + /* Use dbus_connection_send to avoid recursive calls to g_dbus_flush */ + dbus_connection_send(data->conn, signal, NULL); + dbus_message_unref(signal); +} + +static void process_property_changes(struct generic_data *data) +{ + GSList *l; + + for (l = data->interfaces; l != NULL; l = l->next) { + struct interface_data *iface = l->data; + + process_properties_from_interface(data, iface); + } +} + +void g_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 && g_slist_find(data->added, iface)) + return; + + property = find_property(iface->properties, name); + if (property == NULL) { + error("Could not find property %s in %p", name, + iface->properties); + return; + } + + if (g_slist_find(iface->pending_prop, (void *) property) != NULL) + return; + + data->pending_prop = TRUE; + iface->pending_prop = g_slist_prepend(iface->pending_prop, + (void *) property); + + if (flags & G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH) + process_property_changes(data); + else + add_pending(data); +} + +void g_dbus_emit_property_changed(DBusConnection *connection, const char *path, + const char *interface, const char *name) +{ + g_dbus_emit_property_changed_full(connection, path, interface, name, 0); +} + +gboolean g_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 g_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 g_dbus_detach_object_manager(DBusConnection *connection) +{ + if (!g_dbus_unregister_interface(connection, "/", + DBUS_INTERFACE_OBJECT_MANAGER)) + return FALSE; + + root = NULL; + + return TRUE; +} + +void g_dbus_set_flags(int flags) +{ + global_flags = flags; +} + +int g_dbus_get_flags(void) +{ + return global_flags; }