X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fdbus.c;h=31b1ce6ee0706ec825892378eaf615e86cfffe70;hp=e8828d02df4e4c00e8e5fdd6d5ce169ae045383b;hb=fe8954ab47b57703d2fb3116f5dbe389426e99bc;hpb=9028d0ecba8bc51c4e2067a7571eb789cb20d308 diff --git a/src/dbus.c b/src/dbus.c index e8828d02d..31b1ce6ee 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -43,6 +43,7 @@ #include "dbus-timer.h" #include "dbus-path.h" #include "bus-errors.h" +#include "special.h" #define CONNECTIONS_MAX 52 @@ -383,18 +384,24 @@ static DBusHandlerResult api_bus_message_filter(DBusConnection *connection, DBus log_debug("Got D-Bus activation request for %s", name); - r = manager_load_unit(m, name, NULL, &error, &u); + if (manager_unit_pending_inactive(m, SPECIAL_DBUS_SERVICE) || + manager_unit_pending_inactive(m, SPECIAL_DBUS_SOCKET)) { + r = -EADDRNOTAVAIL; + dbus_set_error(&error, BUS_ERROR_SHUTTING_DOWN, "Refusing activation, D-Bus is shutting down."); + } else { + r = manager_load_unit(m, name, NULL, &error, &u); - if (r >= 0 && u->meta.refuse_manual_start) - r = -EPERM; + if (r >= 0 && u->meta.refuse_manual_start) + r = -EPERM; - if (r >= 0) - r = manager_add_job(m, JOB_START, u, JOB_REPLACE, true, &error, NULL); + if (r >= 0) + r = manager_add_job(m, JOB_START, u, JOB_REPLACE, true, &error, NULL); + } if (r < 0) { const char *id, *text; - log_warning("D-Bus activation failed for %s: %s", name, strerror(-r)); + log_debug("D-Bus activation failed for %s: %s", name, strerror(-r)); if (!(reply = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Activator", "ActivationFailure"))) goto oom; @@ -411,7 +418,7 @@ static DBusHandlerResult api_bus_message_filter(DBusConnection *connection, DBus goto oom; } - /* On success we don't do anything, the service will be spwaned now */ + /* On success we don't do anything, the service will be spawned now */ } } @@ -457,7 +464,9 @@ static DBusHandlerResult system_bus_message_filter(DBusConnection *connection, D log_debug("System D-Bus connection terminated."); bus_done_system(m); - } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) { + } else if (m->running_as != MANAGER_SYSTEM && + dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) { + const char *cgroup; if (!dbus_message_get_args(message, &error, @@ -491,7 +500,9 @@ static DBusHandlerResult private_bus_message_filter(DBusConnection *connection, if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) shutdown_connection(m, connection); - else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) { + else if (m->running_as == MANAGER_SYSTEM && + dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) { + const char *cgroup; if (!dbus_message_get_args(message, &error, @@ -500,6 +511,12 @@ static DBusHandlerResult private_bus_message_filter(DBusConnection *connection, log_error("Failed to parse Released message: %s", error.message); else cgroup_notify_empty(m, cgroup); + + /* Forward the message to the system bus, so that user + * instances are notified as well */ + + if (m->system_bus) + dbus_connection_send(m->system_bus, message, NULL); } dbus_error_free(&error); @@ -801,17 +818,19 @@ static int bus_init_system(Manager *m) { goto fail; } - dbus_bus_add_match(m->system_bus, - "type='signal'," - "interface='org.freedesktop.systemd1.Agent'," - "member='Released'," - "path='/org/freedesktop/systemd1/agent'", - &error); - - if (dbus_error_is_set(&error)) { - log_error("Failed to register match: %s", error.message); - r = -EIO; - goto fail; + if (m->running_as != MANAGER_SYSTEM) { + dbus_bus_add_match(m->system_bus, + "type='signal'," + "interface='org.freedesktop.systemd1.Agent'," + "member='Released'," + "path='/org/freedesktop/systemd1/agent'", + &error); + + if (dbus_error_is_set(&error)) { + log_error("Failed to register match: %s", error.message); + r = -EIO; + goto fail; + } } if (m->api_bus != m->system_bus) { @@ -845,7 +864,7 @@ static int bus_init_api(Manager *m) { if (m->running_as == MANAGER_SYSTEM && m->system_bus) m->api_bus = m->system_bus; else { - if (!(m->api_bus = dbus_bus_get_private(m->running_as == MANAGER_SESSION ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error))) { + if (!(m->api_bus = dbus_bus_get_private(m->running_as == MANAGER_USER ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error))) { log_debug("Failed to get API D-Bus connection, retrying later: %s", error.message); r = 0; goto fail; @@ -936,7 +955,8 @@ static int bus_init_private(Manager *m) { if (getpid() != 1) return 0; - if (!(m->private_bus = dbus_server_listen("unix:abstract=/org/freedesktop/systemd1/private", &error))) { + unlink("/run/systemd/private"); + if (!(m->private_bus = dbus_server_listen("unix:path=/run/systemd/private", &error))) { log_error("Failed to create private D-Bus server: %s", error.message); r = -EIO; goto fail; @@ -963,7 +983,7 @@ fail: return r; } -int bus_init(Manager *m) { +int bus_init(Manager *m, bool try_bus_connect) { int r; if (set_ensure_allocated(&m->bus_connections, trivial_hash_func, trivial_compare_func) < 0 || @@ -979,14 +999,18 @@ int bus_init(Manager *m) { } if (m->subscribed_data_slot < 0) - if (!dbus_pending_call_allocate_data_slot(&m->subscribed_data_slot)) { + if (!dbus_connection_allocate_data_slot(&m->subscribed_data_slot)) { log_error("Not enough memory"); return -ENOMEM; } - if ((r = bus_init_system(m)) < 0 || - (r = bus_init_api(m)) < 0 || - (r = bus_init_private(m)) < 0) + if (try_bus_connect) { + if ((r = bus_init_system(m)) < 0 || + (r = bus_init_api(m)) < 0) + return r; + } + + if ((r = bus_init_private(m)) < 0) return r; return 0; @@ -1091,7 +1115,7 @@ void bus_done(Manager *m) { dbus_pending_call_free_data_slot(&m->name_data_slot); if (m->subscribed_data_slot >= 0) - dbus_pending_call_free_data_slot(&m->subscribed_data_slot); + dbus_connection_free_data_slot(&m->subscribed_data_slot); } static void query_pid_pending_cb(DBusPendingCall *pending, void *userdata) { @@ -1190,12 +1214,20 @@ oom: return -ENOMEM; } -DBusHandlerResult bus_default_message_handler(Manager *m, DBusConnection *c, DBusMessage *message, const char*introspection, const BusProperty *properties) { +DBusHandlerResult bus_default_message_handler( + Manager *m, + DBusConnection *c, + DBusMessage *message, + const char *introspection, + const char *interfaces, + const BusProperty *properties) { + DBusError error; DBusMessage *reply = NULL; int r; assert(m); + assert(c); assert(message); dbus_error_init(&error); @@ -1246,7 +1278,15 @@ DBusHandlerResult bus_default_message_handler(Manager *m, DBusConnection *c, DBu if (!dbus_message_iter_close_container(&iter, &sub)) goto oom; + } else { + if (!nulstr_contains(interfaces, interface)) + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); + else + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property"); + + return bus_send_error_reply(m, c, message, &error, -EINVAL); } + } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "GetAll") && properties) { const char *interface; const BusProperty *p; @@ -1259,6 +1299,11 @@ DBusHandlerResult bus_default_message_handler(Manager *m, DBusConnection *c, DBu DBUS_TYPE_INVALID)) return bus_send_error_reply(m, c, message, &error, -EINVAL); + if (interface[0] && !nulstr_contains(interfaces, interface)) { + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); + return bus_send_error_reply(m, c, message, &error, -EINVAL); + } + if (!(reply = dbus_message_new_method_return(message))) goto oom; @@ -1292,6 +1337,71 @@ DBusHandlerResult bus_default_message_handler(Manager *m, DBusConnection *c, DBu if (!dbus_message_iter_close_container(&iter, &sub)) goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Set") && properties) { + const char *interface, *property; + DBusMessageIter iter; + const BusProperty *p; + + if (!dbus_message_iter_init(message, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return bus_send_error_reply(m, c, message, NULL, -EINVAL); + + dbus_message_iter_get_basic(&iter, &interface); + + if (!dbus_message_iter_next(&iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return bus_send_error_reply(m, c, message, NULL, -EINVAL); + + dbus_message_iter_get_basic(&iter, &property); + + if (!dbus_message_iter_next(&iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT || + dbus_message_iter_has_next(&iter)) + return bus_send_error_reply(m, c, message, NULL, -EINVAL); + + for (p = properties; p->property; p++) + if (streq(p->interface, interface) && streq(p->property, property)) + break; + + if (p->set) { + DBusMessageIter sub; + char *sig; + + dbus_message_iter_recurse(&iter, &sub); + + if (!(sig = dbus_message_iter_get_signature(&sub))) + goto oom; + + if (!streq(sig, p->signature)) { + dbus_free(sig); + return bus_send_error_reply(m, c, message, NULL, -EINVAL); + } + + dbus_free(sig); + + if ((r = p->set(m, &sub, property)) < 0) { + if (r == -ENOMEM) + goto oom; + return bus_send_error_reply(m, c, message, NULL, r); + } + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + } else { + if (p->property) + dbus_set_error_const(&error, DBUS_ERROR_PROPERTY_READ_ONLY, "Property read-only"); + else if (!nulstr_contains(interfaces, interface)) + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); + else + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property"); + + return bus_send_error_reply(m, c, message, &error, -EINVAL); + } + + } else if (!nulstr_contains(interfaces, dbus_message_get_interface(message))) { + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); + return bus_send_error_reply(m, c, message, &error, -EINVAL); } if (reply) { @@ -1337,6 +1447,7 @@ static const char *error_to_dbus(int error) { return DBUS_ERROR_FILE_EXISTS; case -ETIMEDOUT: + case -ETIME: return DBUS_ERROR_TIMEOUT; case -EIO: @@ -1611,3 +1722,41 @@ bool bus_connection_has_subscriber(Manager *m, DBusConnection *c) { return !set_isempty(BUS_CONNECTION_SUBSCRIBED(m, c)); } + +DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties) { + DBusMessage *m; + DBusMessageIter iter, sub; + const char *i; + + assert(interface); + assert(properties); + + if (!(m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged"))) + goto oom; + + dbus_message_iter_init_append(m, &iter); + + /* We won't send any property values, since they might be + * large and sometimes not cheap to generated */ + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) || + !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) || + !dbus_message_iter_close_container(&iter, &sub) || + !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) + goto oom; + + NULSTR_FOREACH(i, properties) + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &i)) + goto oom; + + if (!dbus_message_iter_close_container(&iter, &sub)) + goto oom; + + return m; + +oom: + if (m) + dbus_message_unref(m); + + return NULL; +}