char *path;
GSList *interfaces;
GSList *objects;
+ GSList *added;
+ GSList *removed;
+ guint process_id;
char *introspect;
struct generic_data *parent;
};
DBusMessage *message;
};
+static gboolean process_changes(gpointer user_data);
+
static void print_arguments(GString *gstr, const GDBusArgInfo *args,
const char *direction)
{
child->parent = parent;
}
-static void generic_unregister(DBusConnection *connection, void *user_data)
+static void append_properties(struct interface_data *data,
+ DBusMessageIter *iter)
{
- struct generic_data *data = user_data;
+ DBusMessageIter dict;
+
+ 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);
+
+ /* TODO: list properties */
+
+ 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;
struct generic_data *parent = data->parent;
- if (parent != NULL)
- parent->objects = g_slist_remove(parent->objects, data);
+ if (parent == NULL)
+ return;
- g_slist_foreach(data->objects, reset_parent, data->parent);
- g_slist_free(data->objects);
+ signal = dbus_message_new_signal(parent->path,
+ DBUS_INTERFACE_OBJECT_MANAGER,
+ "InterfacesAdded");
+ if (signal == NULL)
+ return;
- dbus_connection_unref(data->conn);
- g_free(data->introspect);
- g_free(data->path);
- g_free(data);
+ 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);
+
+ g_slist_foreach(data->added, append_interface, &array);
+ g_slist_free(data->added);
+ data->added = NULL;
+
+ dbus_message_iter_close_container(&iter, &array);
+
+ g_dbus_send_message(data->conn, signal);
}
static struct interface_data *find_interface(GSList *interfaces,
return TRUE;
}
-static DBusHandlerResult generic_message(DBusConnection *connection,
- DBusMessage *message, void *user_data)
+static gboolean remove_interface(struct generic_data *data, const char *name)
{
- 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);
+ iface = find_interface(data->interfaces, name);
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;
+ return FALSE;
- if (g_dbus_args_have_signature(method->in_args,
- message) == FALSE)
- continue;
+ data->interfaces = g_slist_remove(data->interfaces, iface);
- if (check_privilege(connection, message, method,
- iface->user_data) == TRUE)
- return DBUS_HANDLER_RESULT_HANDLED;
+ if (iface->destroy) {
+ iface->destroy(iface->user_data);
+ iface->user_data = NULL;
+ }
- return process_message(connection, message, method,
- iface->user_data);
+ if (data->parent == NULL) {
+ g_free(iface->name);
+ g_free(iface);
+ return TRUE;
}
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
+ data->removed = g_slist_prepend(data->removed, iface->name);
+ g_free(iface);
-static DBusObjectPathVTable generic_table = {
- .unregister_function = generic_unregister,
- .message_function = generic_message,
-};
+ if (data->process_id > 0)
+ return TRUE;
+
+ data->process_id = g_idle_add(process_changes, data);
+
+ return TRUE;
+}
static struct generic_data *invalidate_parent_data(DBusConnection *conn,
const char *child_path)
return data;
}
-static const GDBusMethodTable introspect_methods[] = {
- { GDBUS_METHOD("Introspect", NULL,
- GDBUS_ARGS({ "xml", "s" }), introspect) },
- { }
-};
-
static inline const GDBusPropertyTable *find_property(const GDBusPropertyTable *properties,
const char *name)
{
{ }
};
-static void append_properties(struct interface_data *data,
- DBusMessageIter *iter)
+static void append_name(gpointer data, gpointer user_data)
{
- DBusMessageIter dict;
+ char *name = data;
+ DBusMessageIter *iter = user_data;
- 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);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name);
+}
- /* TODO: list properties */
+static void emit_interfaces_removed(struct generic_data *data)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter, array;
+ struct generic_data *parent = data->parent;
- dbus_message_iter_close_container(iter, &dict);
+ if (parent == NULL)
+ return;
+
+ signal = dbus_message_new_signal(parent->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);
+
+ g_slist_foreach(data->removed, append_name, &array);
+ g_slist_free_full(data->removed, g_free);
+ data->removed = NULL;
+
+ dbus_message_iter_close_container(&iter, &array);
+
+ g_dbus_send_message(data->conn, signal);
}
-static void append_interface(gpointer data, gpointer user_data)
+static gboolean process_changes(gpointer user_data)
{
- struct interface_data *iface = data;
- DBusMessageIter *array = user_data;
- DBusMessageIter entry;
+ struct generic_data *data = user_data;
- 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);
+ data->process_id = 0;
+
+ if (data->added != NULL)
+ emit_interfaces_added(data);
+
+ if (data->removed != NULL)
+ emit_interfaces_removed(data);
+
+ return FALSE;
}
+static void generic_unregister(DBusConnection *connection, void *user_data)
+{
+ struct generic_data *data = user_data;
+ struct generic_data *parent = data->parent;
+
+ if (parent != NULL)
+ parent->objects = g_slist_remove(parent->objects, data);
+
+ if (data->process_id > 0) {
+ g_source_remove(data->process_id);
+ if (data->removed != NULL)
+ emit_interfaces_removed(data);
+ }
+
+ g_slist_foreach(data->objects, reset_parent, data->parent);
+ g_slist_free(data->objects);
+
+ dbus_connection_unref(data->conn);
+ g_free(data->introspect);
+ g_free(data->path);
+ g_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 (g_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;
{ }
};
-static void emit_interface_added(struct generic_data *data,
- struct interface_data *iface)
-{
- DBusMessage *signal;
- DBusMessageIter iter, array;
- struct generic_data *parent = data->parent;
-
- if (parent == NULL)
- return;
-
- signal = dbus_message_new_signal(parent->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);
-
- append_interface(iface, &array);
-
- dbus_message_iter_close_container(&iter, &array);
-
- g_dbus_send_message(data->conn, signal);
-}
-
static void add_interface(struct generic_data *data,
const char *name,
const GDBusMethodTable *methods,
iface->destroy = destroy;
data->interfaces = g_slist_append(data->interfaces, iface);
+ if (data->parent == NULL)
+ return;
+
+ data->added = g_slist_append(data->added, iface);
+ if (data->process_id > 0)
+ return;
- emit_interface_added(data, iface);
+ data->process_id = g_idle_add(process_changes, data);
}
static struct generic_data *object_path_ref(DBusConnection *connection,
return data;
}
-static void emit_interface_remove(struct generic_data *data,
- struct interface_data *iface)
-{
- DBusMessage *signal;
- DBusMessageIter iter, array;
- struct generic_data *parent = data->parent;
-
- if (parent == NULL)
- return;
-
- signal = dbus_message_new_signal(parent->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_message_iter_append_basic(&array, DBUS_TYPE_STRING, &iface->name);
- dbus_message_iter_close_container(&iter, &array);
-
- g_dbus_send_message(data->conn, signal);
-}
-
-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;
-
- emit_interface_remove(data, iface);
-
- data->interfaces = g_slist_remove(data->interfaces, iface);
-
- if (iface->destroy)
- iface->destroy(iface->user_data);
-
- g_free(iface->name);
- g_free(iface);
-
- return TRUE;
-}
-
static void object_path_unref(DBusConnection *connection, const char *path)
{
struct generic_data *data = NULL;
remove_interface(data, DBUS_INTERFACE_PROPERTIES);
remove_interface(data, DBUS_INTERFACE_OBJECT_MANAGER);
- invalidate_parent_data(connection, path);
+ invalidate_parent_data(data->conn, data->path);
- dbus_connection_unregister_object_path(connection, path);
+ dbus_connection_unregister_object_path(data->conn, data->path);
}
static gboolean check_signal(DBusConnection *conn, const char *path,
g_free(data->introspect);
data->introspect = NULL;
- object_path_unref(connection, path);
+ object_path_unref(connection, data->path);
return TRUE;
}