X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=dbus.c;h=0054d1519e3bfdfd936dc0edec533f7b4a52063e;hp=546a7c59eb0a5333fa439a8b6fb92884b4aa7d26;hb=3a76266192da84f6f7b84725e7647c82f8407278;hpb=266e525cc1861bd37ffa6e03504d5c6662118cf1 diff --git a/dbus.c b/dbus.c index 546a7c59e..0054d1519 100644 --- a/dbus.c +++ b/dbus.c @@ -19,23 +19,29 @@ along with systemd; If not, see . ***/ -#include - #include #include #include #include +#include #include "dbus.h" #include "log.h" #include "strv.h" #include "cgroup.h" +#include "dbus-unit.h" +#include "dbus-job.h" +#include "dbus-manager.h" static void api_bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus status, void *data) { Manager *m = data; assert(bus); assert(m); + + if (!m->api_bus) + return; + assert(m->api_bus == bus); m->request_api_bus_dispatch = status != DBUS_DISPATCH_COMPLETE; @@ -46,6 +52,10 @@ static void system_bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus s assert(bus); assert(m); + + if (!m->system_bus) + return; + assert(m->system_bus == bus); m->request_system_bus_dispatch = status != DBUS_DISPATCH_COMPLETE; @@ -304,21 +314,29 @@ static DBusHandlerResult api_bus_message_filter(DBusConnection *connection, DBu /* dbus_message_get_path(message)); */ if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) { - log_error("Warning! D-Bus connection terminated."); + log_error("Warning! API D-Bus connection terminated."); bus_done_api(m); } else if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { - const char *name, *old, *new; + const char *name, *old_owner, *new_owner; if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &old, - DBUS_TYPE_STRING, &new, + DBUS_TYPE_STRING, &old_owner, + DBUS_TYPE_STRING, &new_owner, DBUS_TYPE_INVALID)) log_error("Failed to parse NameOwnerChanged message: %s", error.message); else { if (set_remove(m->subscribed, (char*) name)) log_debug("Subscription client vanished: %s (left: %u)", name, set_size(m->subscribed)); + + if (old_owner[0] == 0) + old_owner = NULL; + + if (new_owner[0] == 0) + new_owner = NULL; + + manager_dispatch_bus_name_owner_changed(m, name, old_owner, new_owner); } } @@ -342,7 +360,7 @@ static DBusHandlerResult system_bus_message_filter(DBusConnection *connection, /* dbus_message_get_path(message)); */ if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) { - log_error("Warning! D-Bus connection terminated."); + log_error("Warning! System D-Bus connection terminated."); bus_done_system(m); } if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) { @@ -363,22 +381,36 @@ static DBusHandlerResult system_bus_message_filter(DBusConnection *connection, unsigned bus_dispatch(Manager *m) { assert(m); - if (m->request_api_bus_dispatch) - if (dbus_connection_dispatch(m->api_bus) == DBUS_DISPATCH_COMPLETE) { + if (m->queued_message) { + /* If we cannot get rid of this message we won't + * dispatch any D-Bus messages, so that we won't end + * up wanting to queue another message. */ + + if (!dbus_connection_send(m->api_bus, m->queued_message, NULL)) + return 0; + + dbus_message_unref(m->queued_message); + m->queued_message = NULL; + } + + if (m->request_api_bus_dispatch) { + if (dbus_connection_dispatch(m->api_bus) == DBUS_DISPATCH_COMPLETE) m->request_api_bus_dispatch = false; - return 1; - } - if (m->request_system_bus_dispatch) - if (dbus_connection_dispatch(m->system_bus) == DBUS_DISPATCH_COMPLETE) { + return 1; + } + + if (m->request_system_bus_dispatch) { + if (dbus_connection_dispatch(m->system_bus) == DBUS_DISPATCH_COMPLETE) m->request_system_bus_dispatch = false; - return 1; - } + + return 1; + } return 0; } -static void pending_cb(DBusPendingCall *pending, void *userdata) { +static void request_name_pending_cb(DBusPendingCall *pending, void *userdata) { DBusMessage *reply; DBusError error; @@ -422,48 +454,49 @@ static void pending_cb(DBusPendingCall *pending, void *userdata) { } static int request_name(Manager *m) { - DBusMessage *message; const char *name = "org.freedesktop.systemd1"; uint32_t flags = 0; - DBusPendingCall *pending; + DBusMessage *message = NULL; + DBusPendingCall *pending = NULL; if (!(message = dbus_message_new_method_call( DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "RequestName"))) - return -ENOMEM; + goto oom; if (!dbus_message_append_args( message, DBUS_TYPE_STRING, &name, DBUS_TYPE_UINT32, &flags, - DBUS_TYPE_INVALID)) { - dbus_message_unref(message); - return -ENOMEM; - } + DBUS_TYPE_INVALID)) + goto oom; - if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1)) { - dbus_message_unref(message); - return -ENOMEM; - } + if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1)) + goto oom; + if (!dbus_pending_call_set_notify(pending, request_name_pending_cb, m, NULL)) + goto oom; dbus_message_unref(message); - - if (!dbus_pending_call_set_notify(pending, pending_cb, NULL, NULL)) { - dbus_pending_call_cancel(pending); - dbus_pending_call_unref(pending); - return -ENOMEM; - } - - dbus_pending_call_unref(pending); /* We simple ask for the name and don't wait for it. Sooner or * later we'll have it. */ return 0; + +oom: + if (pending) { + dbus_pending_call_cancel(pending); + dbus_pending_call_unref(pending); + } + + if (message) + dbus_message_unref(message); + + return -ENOMEM; } static int bus_setup_loop(Manager *m, DBusConnection *bus) { @@ -471,6 +504,7 @@ static int bus_setup_loop(Manager *m, DBusConnection *bus) { assert(bus); dbus_connection_set_exit_on_disconnect(bus, FALSE); + if (!dbus_connection_set_watch_functions(bus, bus_add_watch, bus_remove_watch, bus_toggle_watch, m, NULL) || !dbus_connection_set_timeout_functions(bus, bus_add_timeout, bus_remove_timeout, bus_toggle_timeout, m, NULL)) return -ENOMEM; @@ -499,12 +533,13 @@ int bus_init_system(Manager *m) { return 0; } + dbus_connection_set_dispatch_status_function(m->system_bus, system_bus_dispatch_status, m, NULL); + m->request_system_bus_dispatch = true; + if ((r = bus_setup_loop(m, m->system_bus)) < 0) { bus_done_system(m); return r; } - - dbus_connection_set_dispatch_status_function(m->system_bus, system_bus_dispatch_status, m, NULL); } if (!dbus_connection_add_filter(m->system_bus, system_bus_message_filter, m, NULL)) { @@ -530,8 +565,6 @@ int bus_init_system(Manager *m) { strnull(dbus_bus_get_unique_name(m->system_bus))); dbus_free(id); - m->request_system_bus_dispatch = true; - return 0; } @@ -547,6 +580,10 @@ int bus_init_api(Manager *m) { if (m->api_bus) return 0; + if (m->name_data_slot < 0) + if (!dbus_pending_call_allocate_data_slot(&m->name_data_slot)) + return -ENOMEM; + if (m->running_as != MANAGER_SESSION && m->system_bus) m->api_bus = m->system_bus; else { @@ -556,12 +593,13 @@ int bus_init_api(Manager *m) { return 0; } + dbus_connection_set_dispatch_status_function(m->api_bus, api_bus_dispatch_status, m, NULL); + m->request_api_bus_dispatch = true; + if ((r = bus_setup_loop(m, m->api_bus)) < 0) { bus_done_api(m); return r; } - - dbus_connection_set_dispatch_status_function(m->api_bus, api_bus_dispatch_status, m, NULL); } if (!dbus_connection_register_object_path(m->api_bus, "/org/freedesktop/systemd1", &bus_manager_vtable, m) || @@ -600,8 +638,6 @@ int bus_init_api(Manager *m) { if (!(m->subscribed = set_new(string_hash_func, string_compare_func))) return -ENOMEM; - m->request_api_bus_dispatch = true; - return 0; } @@ -612,10 +648,11 @@ void bus_done_api(Manager *m) { if (m->system_bus == m->api_bus) m->system_bus = NULL; + dbus_connection_set_dispatch_status_function(m->api_bus, NULL, NULL, NULL); + dbus_connection_flush(m->api_bus); dbus_connection_close(m->api_bus); dbus_connection_unref(m->api_bus); m->api_bus = NULL; - } if (m->subscribed) { @@ -627,6 +664,14 @@ void bus_done_api(Manager *m) { set_free(m->subscribed); m->subscribed = NULL; } + + if (m->name_data_slot >= 0) + dbus_pending_call_free_data_slot(&m->name_data_slot); + + if (m->queued_message) { + dbus_message_unref(m->queued_message); + m->queued_message = NULL; + } } void bus_done_system(Manager *m) { @@ -636,12 +681,110 @@ void bus_done_system(Manager *m) { bus_done_api(m); if (m->system_bus) { + dbus_connection_set_dispatch_status_function(m->system_bus, NULL, NULL, NULL); + dbus_connection_flush(m->system_bus); dbus_connection_close(m->system_bus); dbus_connection_unref(m->system_bus); m->system_bus = NULL; } } +static void query_pid_pending_cb(DBusPendingCall *pending, void *userdata) { + Manager *m = userdata; + DBusMessage *reply; + DBusError error; + const char *name; + + dbus_error_init(&error); + + assert_se(name = dbus_pending_call_get_data(pending, m->name_data_slot)); + assert_se(reply = dbus_pending_call_steal_reply(pending)); + + switch (dbus_message_get_type(reply)) { + + case DBUS_MESSAGE_TYPE_ERROR: + + assert_se(dbus_set_error_from_message(&error, reply)); + log_warning("GetConnectionUnixProcessID() failed: %s", error.message); + break; + + case DBUS_MESSAGE_TYPE_METHOD_RETURN: { + uint32_t r; + + if (!dbus_message_get_args(reply, + &error, + DBUS_TYPE_UINT32, &r, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse GetConnectionUnixProcessID() reply: %s", error.message); + break; + } + + manager_dispatch_bus_query_pid_done(m, name, (pid_t) r); + break; + } + + default: + assert_not_reached("Invalid reply message"); + } + + dbus_message_unref(reply); + dbus_error_free(&error); +} + +int bus_query_pid(Manager *m, const char *name) { + DBusMessage *message = NULL; + DBusPendingCall *pending = NULL; + char *n = NULL; + + assert(m); + assert(name); + + if (!(message = dbus_message_new_method_call( + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetConnectionUnixProcessID"))) + goto oom; + + if (!(dbus_message_append_args( + message, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID))) + goto oom; + + if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1)) + goto oom; + + if (!(n = strdup(name))) + goto oom; + + if (!dbus_pending_call_set_data(pending, m->name_data_slot, n, free)) + goto oom; + + n = NULL; + + if (!dbus_pending_call_set_notify(pending, query_pid_pending_cb, m, NULL)) + goto oom; + + dbus_message_unref(message); + dbus_pending_call_unref(pending); + + return 0; + +oom: + free(n); + + if (pending) { + dbus_pending_call_cancel(pending); + dbus_pending_call_unref(pending); + } + + if (message) + dbus_message_unref(message); + + return -ENOMEM; +} + DBusHandlerResult bus_default_message_handler(Manager *m, DBusMessage *message, const char*introspection, const BusProperty *properties) { DBusError error; DBusMessage *reply = NULL; @@ -687,7 +830,7 @@ DBusHandlerResult bus_default_message_handler(Manager *m, DBusMessage *message, if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, p->signature, &sub)) goto oom; - if ((r = p->append(m, &sub, property, p->data)) < 0) { + if ((r = p->append(m, &sub, property, (void*) p->data)) < 0) { if (r == -ENOMEM) goto oom; @@ -729,7 +872,7 @@ DBusHandlerResult bus_default_message_handler(Manager *m, DBusMessage *message, !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, p->signature, &sub3)) goto oom; - if ((r = p->append(m, &sub3, p->property, p->data)) < 0) { + if ((r = p->append(m, &sub3, p->property, (void*) p->data)) < 0) { if (r == -ENOMEM) goto oom; @@ -901,6 +1044,10 @@ int bus_property_append_uint64(Manager *m, DBusMessageIter *i, const char *prope assert(property); assert(data); + /* Let's ensure that pid_t is actually 64bit, and hence this + * function can be used for usec_t */ + assert_cc(sizeof(uint64_t) == sizeof(usec_t)); + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, data)) return -ENOMEM; @@ -913,8 +1060,28 @@ int bus_property_append_uint32(Manager *m, DBusMessageIter *i, const char *prope assert(property); assert(data); + /* Let's ensure that pid_t and mode_t is actually 32bit, and + * hence this function can be used for pid_t/mode_t */ + assert_cc(sizeof(uint32_t) == sizeof(pid_t)); + assert_cc(sizeof(uint32_t) == sizeof(mode_t)); + assert_cc(sizeof(uint32_t) == sizeof(unsigned)); + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, data)) return -ENOMEM; return 0; } + +int bus_property_append_int32(Manager *m, DBusMessageIter *i, const char *property, void *data) { + assert(m); + assert(i); + assert(property); + assert(data); + + assert_cc(sizeof(int32_t) == sizeof(int)); + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, data)) + return -ENOMEM; + + return 0; +}