X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fdbus-common.c;h=9bf0dabe4cf1da450dc84d3a58f63330059064ef;hb=7948c4dfbea73ac21250b588089039aa17a90386;hp=25b718ec026533463341bb382eadbbd3d03e35e8;hpb=a8f11321c209830a35edd0357e8def5d4437d854;p=elogind.git diff --git a/src/dbus-common.c b/src/dbus-common.c index 25b718ec0..9bf0dabe4 100644 --- a/src/dbus-common.c +++ b/src/dbus-common.c @@ -26,10 +26,13 @@ #include #include #include +#include #include "log.h" #include "dbus-common.h" #include "util.h" +#include "def.h" +#include "strv.h" int bus_check_peercred(DBusConnection *c) { int fd; @@ -57,8 +60,6 @@ int bus_check_peercred(DBusConnection *c) { return 1; } -#define TIMEOUT_USEC (60*USEC_PER_SEC) - static int sync_auth(DBusConnection *bus, DBusError *error) { usec_t begin, tstamp; @@ -71,13 +72,13 @@ static int sync_auth(DBusConnection *bus, DBusError *error) { begin = tstamp = now(CLOCK_MONOTONIC); for (;;) { - if (tstamp > begin + TIMEOUT_USEC) + if (tstamp > begin + DEFAULT_TIMEOUT_USEC) break; if (dbus_connection_get_is_authenticated(bus)) break; - if (!dbus_connection_read_write_dispatch(bus, ((begin + TIMEOUT_USEC - tstamp) + USEC_PER_MSEC - 1) / USEC_PER_MSEC)) + if (!dbus_connection_read_write_dispatch(bus, ((begin + DEFAULT_TIMEOUT_USEC - tstamp) + USEC_PER_MSEC - 1) / USEC_PER_MSEC)) break; tstamp = now(CLOCK_MONOTONIC); @@ -104,8 +105,16 @@ int bus_connect(DBusBusType t, DBusConnection **_bus, bool *private, DBusError * /* If we are root, then let's not go via the bus */ if (geteuid() == 0 && t == DBUS_BUS_SYSTEM) { - if (!(bus = dbus_connection_open_private("unix:abstract=/org/freedesktop/systemd1/private", error))) - return -EIO; + + if (!(bus = dbus_connection_open_private("unix:path=/run/systemd/private", error))) { +#ifndef LEGACY + dbus_error_free(error); + + /* Retry with the pre v21 socket name, to ease upgrades */ + if (!(bus = dbus_connection_open_private("unix:abstract=/org/freedesktop/systemd1/private", error))) +#endif + return -EIO; + } dbus_connection_set_exit_on_disconnect(bus, FALSE); @@ -225,3 +234,463 @@ const char *bus_error_message(const DBusError *error) { return error->message; } + +DBusHandlerResult bus_default_message_handler( + DBusConnection *c, + DBusMessage *message, + const char *introspection, + const char *interfaces, + const BusProperty *properties) { + + DBusError error; + DBusMessage *reply = NULL; + int r; + + assert(c); + assert(message); + + dbus_error_init(&error); + + if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") && introspection) { + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Get") && properties) { + const char *interface, *property; + const BusProperty *p; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(c, message, &error, -EINVAL); + + for (p = properties; p->property; p++) + if (streq(p->interface, interface) && streq(p->property, property)) + break; + + if (p->property) { + DBusMessageIter iter, sub; + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + dbus_message_iter_init_append(reply, &iter); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, p->signature, &sub)) + goto oom; + + if ((r = p->append(&sub, property, (void*) p->data)) < 0) { + + if (r == -ENOMEM) + goto oom; + + dbus_message_unref(reply); + return bus_send_error_reply(c, message, NULL, r); + } + + 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(c, message, &error, -EINVAL); + } + + } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "GetAll") && properties) { + const char *interface; + const BusProperty *p; + DBusMessageIter iter, sub, sub2, sub3; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(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(c, message, &error, -EINVAL); + } + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + dbus_message_iter_init_append(reply, &iter); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub)) + goto oom; + + for (p = properties; p->property; p++) { + if (interface[0] && !streq(p->interface, interface)) + continue; + + if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, NULL, &sub2) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &p->property) || + !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, p->signature, &sub3)) + goto oom; + + if ((r = p->append(&sub3, p->property, (void*) p->data)) < 0) { + + if (r == -ENOMEM) + goto oom; + + dbus_message_unref(reply); + return bus_send_error_reply(c, message, NULL, r); + } + + if (!dbus_message_iter_close_container(&sub2, &sub3) || + !dbus_message_iter_close_container(&sub, &sub2)) + goto oom; + } + + 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(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(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(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(c, message, NULL, -EINVAL); + } + + dbus_free(sig); + + if ((r = p->set(&sub, property)) < 0) { + if (r == -ENOMEM) + goto oom; + return bus_send_error_reply(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(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(c, message, &error, -EINVAL); + } + + if (reply) { + if (!dbus_connection_send(c, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +int bus_property_append_string(DBusMessageIter *i, const char *property, void *data) { + const char *t = data; + + assert(i); + assert(property); + + if (!t) + t = ""; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t)) + return -ENOMEM; + + return 0; +} + +int bus_property_append_strv(DBusMessageIter *i, const char *property, void *data) { + DBusMessageIter sub; + char **t = data; + + assert(i); + assert(property); + + if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub)) + return -ENOMEM; + + STRV_FOREACH(t, t) + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) + return -ENOMEM; + + if (!dbus_message_iter_close_container(i, &sub)) + return -ENOMEM; + + return 0; +} + +int bus_property_append_bool(DBusMessageIter *i, const char *property, void *data) { + bool *b = data; + dbus_bool_t db; + + assert(i); + assert(property); + assert(b); + + db = *b; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db)) + return -ENOMEM; + + return 0; +} + +int bus_property_append_uint64(DBusMessageIter *i, const char *property, void *data) { + assert(i); + 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; + + return 0; +} + +int bus_property_append_uint32(DBusMessageIter *i, const char *property, void *data) { + assert(i); + 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(DBusMessageIter *i, const char *property, void *data) { + 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; +} + +int bus_property_append_size(DBusMessageIter *i, const char *property, void *data) { + uint64_t u; + + assert(i); + assert(property); + assert(data); + + u = (uint64_t) *(size_t*) data; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u)) + return -ENOMEM; + + return 0; +} + +int bus_property_append_ul(DBusMessageIter *i, const char *property, void *data) { + uint64_t u; + + assert(i); + assert(property); + assert(data); + + u = (uint64_t) *(unsigned long*) data; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u)) + return -ENOMEM; + + return 0; +} + +int bus_property_append_long(DBusMessageIter *i, const char *property, void *data) { + int64_t l; + + assert(i); + assert(property); + assert(data); + + l = (int64_t) *(long*) data; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT64, &l)) + return -ENOMEM; + + return 0; +} + +const char *bus_errno_to_dbus(int error) { + + switch(error) { + + case -EINVAL: + return DBUS_ERROR_INVALID_ARGS; + + case -ENOMEM: + return DBUS_ERROR_NO_MEMORY; + + case -EPERM: + case -EACCES: + return DBUS_ERROR_ACCESS_DENIED; + + case -ESRCH: + return DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN; + + case -ENOENT: + return DBUS_ERROR_FILE_NOT_FOUND; + + case -EEXIST: + return DBUS_ERROR_FILE_EXISTS; + + case -ETIMEDOUT: + case -ETIME: + return DBUS_ERROR_TIMEOUT; + + case -EIO: + return DBUS_ERROR_IO_ERROR; + + case -ENETRESET: + case -ECONNABORTED: + case -ECONNRESET: + return DBUS_ERROR_DISCONNECTED; + } + + return DBUS_ERROR_FAILED; +} + +DBusHandlerResult bus_send_error_reply(DBusConnection *c, DBusMessage *message, DBusError *berror, int error) { + DBusMessage *reply = NULL; + const char *name, *text; + + if (berror && dbus_error_is_set(berror)) { + name = berror->name; + text = berror->message; + } else { + name = bus_errno_to_dbus(error); + text = strerror(-error); + } + + if (!(reply = dbus_message_new_error(message, name, text))) + goto oom; + + if (!dbus_connection_send(c, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + if (berror) + dbus_error_free(berror); + + return DBUS_HANDLER_RESULT_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + if (berror) + dbus_error_free(berror); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +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; +}