chiark / gitweb /
sd-daemon: Add sd_is_special for special file descriptors
[elogind.git] / src / dbus-common.c
index dcce10e5525351a43fd7df06fafef8de5ff9a265..5db077b74fb8115157cd8932ea1a8b98ae70c188 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <dbus/dbus.h>
+#include <string.h>
 
 #include "log.h"
 #include "dbus-common.h"
 #include "util.h"
 #include "def.h"
+#include "strv.h"
 
 int bus_check_peercred(DBusConnection *c) {
         int fd;
@@ -232,3 +234,467 @@ 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 {
+                const char *interface = dbus_message_get_interface(message);
+
+                if (!interface || !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) {
+                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;
+}