diff --git a/src/libsystemd-bus/sd-bus-protocol.h b/src/libsystemd-bus/sd-bus-protocol.h index f9a5217dda0..ac4d0b1dc53 100644 --- a/src/libsystemd-bus/sd-bus-protocol.h +++ b/src/libsystemd-bus/sd-bus-protocol.h @@ -100,4 +100,46 @@ enum { _SD_BUS_MESSAGE_HEADER_MAX }; +#define SD_BUS_INTROSPECT_DOCTYPE \ + "\n" + +#define SD_BUS_INTROSPECT_INTERFACE_PROPERTIES \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define SD_BUS_INTROSPECT_INTERFACE_INTROSPECTABLE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define SD_BUS_INTROSPECT_INTERFACE_PEER \ + "\n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + "\n" + #endif diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c index 6a76db960dc..c82b738e028 100644 --- a/src/libsystemd-bus/sd-bus.c +++ b/src/libsystemd-bus/sd-bus.c @@ -30,6 +30,7 @@ #include "util.h" #include "macro.h" #include "strv.h" +#include "set.h" #include "sd-bus.h" #include "bus-internal.h" @@ -1553,7 +1554,10 @@ static int process_object(sd_bus *bus, sd_bus_message *m) { } } - if (!found) + /* We found some handlers but none wanted to take this, then + * return this -- with one exception, we can handle + * introspection minimally ourselves */ + if (!found || sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) return 0; sd_bus_error_set(&error, @@ -1571,6 +1575,98 @@ static int process_object(sd_bus *bus, sd_bus_message *m) { return 1; } +static int process_introspect(sd_bus *bus, sd_bus_message *m) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_free_ char *introspection = NULL; + _cleanup_set_free_free_ Set *s = NULL; + _cleanup_fclose_ FILE *f = NULL; + struct object_callback *c; + Iterator i; + size_t size = 0; + char *node; + int r; + + assert(bus); + assert(m); + + if (!sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) + return 0; + + if (!m->path) + return 0; + + s = set_new(string_hash_func, string_compare_func); + if (!s) + return -ENOMEM; + + HASHMAP_FOREACH(c, bus->object_callbacks, i) { + const char *e; + char *a, *p; + + if (streq(c->path, "/")) + continue; + + if (streq(m->path, "/")) + e = c->path; + else { + e = startswith(c->path, m->path); + if (!e || *e != '/') + continue; + } + + a = strdup(e+1); + if (!a) + return -ENOMEM; + + p = strchr(a, '/'); + if (p) + *p = 0; + + r = set_put(s, a); + if (r < 0) { + free(a); + + if (r != -EEXIST) + return r; + } + } + + f = open_memstream(&introspection, &size); + if (!f) + return -ENOMEM; + + fputs(SD_BUS_INTROSPECT_DOCTYPE, f); + fputs("\n", f); + fputs(SD_BUS_INTROSPECT_INTERFACE_PEER, f); + fputs(SD_BUS_INTROSPECT_INTERFACE_INTROSPECTABLE, f); + + while ((node = set_steal_first(s))) { + fprintf(f, " \n", node); + free(node); + } + + fputs("\n", f); + + fflush(f); + + if (ferror(f)) + return -ENOMEM; + + r = sd_bus_message_new_method_return(bus, m, &reply); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "s", introspection); + if (r < 0) + return r; + + r = sd_bus_send(bus, reply, NULL); + if (r < 0) + return r; + + return 1; +} + static int process_message(sd_bus *bus, sd_bus_message *m) { int r; @@ -1589,7 +1685,11 @@ static int process_message(sd_bus *bus, sd_bus_message *m) { if (r != 0) return r; - return process_object(bus, m); + r = process_object(bus, m); + if (r != 0) + return r; + + return process_introspect(bus, m); } static int process_running(sd_bus *bus, sd_bus_message **ret) { diff --git a/src/libsystemd-bus/sd-bus.h b/src/libsystemd-bus/sd-bus.h index d680270c642..08c5e54230c 100644 --- a/src/libsystemd-bus/sd-bus.h +++ b/src/libsystemd-bus/sd-bus.h @@ -29,9 +29,10 @@ #include "sd-bus-protocol.h" /* TODO: - * - implicitly add stub introspection calls * - server side - * - allow installing match callbacks, + * - allow installing match callbacks + * - anonymous auth + * - default policy * * Later: * - add page donation logic