X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd-bus%2Fsd-bus.c;h=f42d5d0ee133fde80b7a6ba27f78795198c21e1f;hb=e1636421f46db6d06fbd028ef20a3113fa3e11f8;hp=ee5f3955695fb618ba50ef964eabf8d72ccb747a;hpb=8ce2afd6347dcf01e33fe1ff257e2b0fffa8edfe;p=elogind.git diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c index ee5f39556..f42d5d0ee 100644 --- a/src/libsystemd-bus/sd-bus.c +++ b/src/libsystemd-bus/sd-bus.c @@ -45,6 +45,8 @@ #include "bus-introspect.h" #include "bus-signature.h" #include "bus-objects.h" +#include "bus-util.h" +#include "bus-container.h" static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec); @@ -104,6 +106,8 @@ static void bus_free(sd_bus *b) { assert(b); + sd_bus_detach_event(b); + bus_close_fds(b); if (b->kdbus_buffer) @@ -114,6 +118,7 @@ static void bus_free(sd_bus *b) { free(b->auth_buffer); free(b->address); free(b->kernel); + free(b->machine); free(b->exec_path); strv_free(b->exec_argv); @@ -350,9 +355,11 @@ static int hello_callback(sd_bus *bus, sd_bus_message *reply, void *userdata) { assert(bus->state == BUS_HELLO); assert(reply); - r = bus_message_to_errno(reply); + r = sd_bus_message_get_errno(reply); if (r < 0) return r; + if (r > 0) + return -r; r = sd_bus_message_read(reply, "s", &s); if (r < 0) @@ -748,6 +755,45 @@ static int parse_kernel_address(sd_bus *b, const char **p, char **guid) { return 0; } +static int parse_container_address(sd_bus *b, const char **p, char **guid) { + _cleanup_free_ char *machine = NULL; + int r; + + assert(b); + assert(p); + assert(*p); + assert(guid); + + while (**p != 0 && **p != ';') { + r = parse_address_key(p, "guid", guid); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "machine", &machine); + if (r < 0) + return r; + else if (r > 0) + continue; + + skip_address_key(p); + } + + if (!machine) + return -EINVAL; + + free(b->machine); + b->machine = machine; + machine = NULL; + + b->sockaddr.un.sun_family = AF_UNIX; + strncpy(b->sockaddr.un.sun_path, "/var/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path)); + b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + sizeof("/var/run/dbus/system_bus_socket") - 1; + + return 0; +} + static void bus_reset_parsed_address(sd_bus *b) { assert(b); @@ -760,6 +806,8 @@ static void bus_reset_parsed_address(sd_bus *b) { b->server_id = SD_ID128_NULL; free(b->kernel); b->kernel = NULL; + free(b->machine); + b->machine = NULL; } static int bus_parse_next_address(sd_bus *b) { @@ -818,6 +866,14 @@ static int bus_parse_next_address(sd_bus *b) { if (r < 0) return r; + break; + } else if (startswith(a, "x-container:")) { + + a += 12; + r = parse_container_address(b, &a, &guid); + if (r < 0) + return r; + break; } @@ -844,24 +900,32 @@ static int bus_start_address(sd_bus *b) { for (;;) { sd_bus_close(b); - if (b->sockaddr.sa.sa_family != AF_UNSPEC) { + if (b->exec_path) { - r = bus_socket_connect(b); + r = bus_socket_exec(b); if (r >= 0) return r; b->last_connect_error = -r; + } else if (b->kernel) { - } else if (b->exec_path) { + r = bus_kernel_connect(b); + if (r >= 0) + return r; - r = bus_socket_exec(b); + b->last_connect_error = -r; + + } else if (b->machine) { + + r = bus_container_connect(b); if (r >= 0) return r; b->last_connect_error = -r; - } else if (b->kernel) { - r = bus_kernel_connect(b); + } else if (b->sockaddr.sa.sa_family != AF_UNSPEC) { + + r = bus_socket_connect(b); if (r >= 0) return r; @@ -932,7 +996,7 @@ int sd_bus_start(sd_bus *bus) { if (bus->input_fd >= 0) r = bus_start_fd(bus); - else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path || bus->kernel) + else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path || bus->kernel || bus->machine) r = bus_start_address(bus); else return -EINVAL; @@ -1028,6 +1092,78 @@ fail: return r; } +int sd_bus_open_system_remote(const char *host, sd_bus **ret) { + _cleanup_free_ char *e = NULL; + char *p = NULL; + sd_bus *bus; + int r; + + assert_return(host, -EINVAL); + assert_return(ret, -EINVAL); + + e = bus_address_escape(host); + if (!e) + return -ENOMEM; + + p = strjoin("unixexec:path=ssh,argv1=-xT,argv2=", e, ",argv3=systemd-stdio-bridge", NULL); + if (!p) + return -ENOMEM; + + r = sd_bus_new(&bus); + if (r < 0) { + free(p); + return r; + } + + bus->address = p; + bus->bus_client = true; + + r = sd_bus_start(bus); + if (r < 0) { + bus_free(bus); + return r; + } + + *ret = bus; + return 0; +} + +int sd_bus_open_system_container(const char *machine, sd_bus **ret) { + _cleanup_free_ char *e = NULL; + sd_bus *bus; + char *p; + int r; + + assert_return(machine, -EINVAL); + assert_return(ret, -EINVAL); + + e = bus_address_escape(machine); + if (!e) + return -ENOMEM; + + p = strjoin("x-container:machine=", e, NULL); + if (!p) + return -ENOMEM; + + r = sd_bus_new(&bus); + if (r < 0) { + free(p); + return r; + } + + bus->address = p; + bus->bus_client = true; + + r = sd_bus_start(bus); + if (r < 0) { + bus_free(bus); + return r; + } + + *ret = bus; + return 0; +} + void sd_bus_close(sd_bus *bus) { if (!bus) return; @@ -1038,6 +1174,8 @@ void sd_bus_close(sd_bus *bus) { bus->state = BUS_CLOSED; + sd_bus_detach_event(bus); + if (!bus->is_kernel) bus_close_fds(bus); @@ -1231,7 +1369,7 @@ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial) { /* If this is a reply and no reply was requested, then let's * suppress this, if we can */ if (m->dont_send && !serial) - return 0; + return 1; if ((bus->state == BUS_RUNNING || bus->state == BUS_HELLO) && bus->wqueue_size <= 0) { size_t idx = 0; @@ -1273,7 +1411,7 @@ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *serial) { if (serial) *serial = BUS_MESSAGE_SERIAL(m); - return 0; + return 1; } static usec_t calc_elapse(uint64_t usec) { @@ -1318,7 +1456,7 @@ int sd_bus_send_with_reply( assert_return(bus, -EINVAL); assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN); assert_return(m, -EINVAL); - assert_return(m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL, -EINVAL); + assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); assert_return(!(m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL); assert_return(callback, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -1428,7 +1566,7 @@ int sd_bus_send_with_reply_and_block( assert_return(bus, -EINVAL); assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN); assert_return(m, -EINVAL); - assert_return(m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL, -EINVAL); + assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); assert_return(!(m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL); assert_return(!bus_error_is_dirty(error), -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -1475,17 +1613,17 @@ int sd_bus_send_with_reply_and_block( if (incoming->reply_serial == serial) { /* Found a match! */ - if (incoming->header->type == SD_BUS_MESSAGE_TYPE_METHOD_RETURN) { + if (incoming->header->type == SD_BUS_MESSAGE_METHOD_RETURN) { if (reply) *reply = incoming; else sd_bus_message_unref(incoming); - return 0; + return 1; } - if (incoming->header->type == SD_BUS_MESSAGE_TYPE_METHOD_ERROR) { + if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR) { int k; r = sd_bus_error_copy(error, &incoming->error); @@ -1494,9 +1632,9 @@ int sd_bus_send_with_reply_and_block( return r; } - k = bus_error_to_errno(&incoming->error); + k = sd_bus_error_get_errno(&incoming->error); sd_bus_message_unref(incoming); - return k; + return -k; } sd_bus_message_unref(incoming); @@ -1589,6 +1727,11 @@ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) { return 0; } + if (bus->rqueue_size > 0) { + *timeout_usec = 0; + return 1; + } + c = prioq_peek(bus->reply_callbacks_prioq); if (!c) { *timeout_usec = (uint64_t) -1; @@ -1618,7 +1761,7 @@ static int process_timeout(sd_bus *bus) { r = bus_message_new_synthetic_error( bus, c->serial, - &SD_BUS_ERROR_MAKE("org.freedesktop.DBus.Error.Timeout", "Timed out"), + &SD_BUS_ERROR_MAKE(SD_BUS_ERROR_NO_REPLY, "Method call timed out"), &m); if (r < 0) return r; @@ -1644,8 +1787,8 @@ static int process_hello(sd_bus *bus, sd_bus_message *m) { * here (we leave that to the usual handling), we just verify * we don't let any earlier msg through. */ - if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_RETURN && - m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_ERROR) + if (m->header->type != SD_BUS_MESSAGE_METHOD_RETURN && + m->header->type != SD_BUS_MESSAGE_METHOD_ERROR) return -EIO; if (m->reply_serial != bus->hello_serial) @@ -1661,8 +1804,8 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) { assert(bus); assert(m); - if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_RETURN && - m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_ERROR) + if (m->header->type != SD_BUS_MESSAGE_METHOD_RETURN && + m->header->type != SD_BUS_MESSAGE_METHOD_ERROR) return 0; c = hashmap_remove(bus->reply_callbacks, &m->reply_serial); @@ -1743,7 +1886,7 @@ static int process_builtin(sd_bus *bus, sd_bus_message *m) { assert(bus); assert(m); - if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL) + if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) return 0; if (!streq_ptr(m->interface, "org.freedesktop.DBus.Peer")) @@ -1770,7 +1913,7 @@ static int process_builtin(sd_bus *bus, sd_bus_message *m) { } else { r = sd_bus_message_new_method_errorf( bus, m, &reply, - "org.freedesktop.DBus.Error.UnknownMethod", + SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method '%s' on interface '%s'.", m->member, m->interface); } @@ -1792,6 +1935,12 @@ static int process_message(sd_bus *bus, sd_bus_message *m) { bus->iteration_counter++; + log_debug("Got message sender=%s object=%s interface=%s member=%s", + strna(sd_bus_message_get_sender(m)), + strna(sd_bus_message_get_path(m)), + strna(sd_bus_message_get_interface(m)), + strna(sd_bus_message_get_member(m))); + r = process_hello(bus, m); if (r != 0) return r; @@ -1850,11 +1999,11 @@ static int process_running(sd_bus *bus, sd_bus_message **ret) { return 1; } - if (m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL) { + if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) { r = sd_bus_reply_method_errorf( bus, m, - "org.freedesktop.DBus.Error.UnknownObject", + SD_BUS_ERROR_UNKNOWN_OBJECT, "Unknown object '%s'.", m->path); if (r < 0) return r; @@ -1924,7 +2073,7 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) { struct pollfd p[2] = {}; int r, e, n; struct timespec ts; - usec_t until, m; + usec_t m = (usec_t) -1; assert(bus); assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN); @@ -1934,17 +2083,23 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) { return e; if (need_more) + /* The caller really needs some more data, he doesn't + * care about what's already read, or any timeouts + * except its own.*/ e |= POLLIN; - - r = sd_bus_get_timeout(bus, &until); - if (r < 0) - return r; - if (r == 0) - m = (uint64_t) -1; else { - usec_t nw; - nw = now(CLOCK_MONOTONIC); - m = until > nw ? until - nw : 0; + usec_t until; + /* The caller wants to process if there's something to + * process, but doesn't care otherwise */ + + r = sd_bus_get_timeout(bus, &until); + if (r < 0) + return r; + if (r > 0) { + usec_t nw; + nw = now(CLOCK_MONOTONIC); + m = until > nw ? until - nw : 0; + } } if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m)) @@ -2112,3 +2267,160 @@ bool bus_pid_changed(sd_bus *bus) { return bus->original_pid != getpid(); } + +static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + void *bus = userdata; + int r; + + assert(bus); + + r = sd_bus_process(bus, NULL); + if (r < 0) + return r; + + return 1; +} + +static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) { + void *bus = userdata; + int r; + + assert(bus); + + r = sd_bus_process(bus, NULL); + if (r < 0) + return r; + + return 1; +} + +static int prepare_callback(sd_event_source *s, void *userdata) { + sd_bus *bus = userdata; + int r, e; + usec_t until; + + assert(s); + assert(bus); + + e = sd_bus_get_events(bus); + if (e < 0) + return e; + + if (bus->output_fd != bus->input_fd) { + + r = sd_event_source_set_io_events(bus->input_io_event_source, e & POLLIN); + if (r < 0) + return r; + + r = sd_event_source_set_io_events(bus->output_io_event_source, e & POLLOUT); + if (r < 0) + return r; + } else { + r = sd_event_source_set_io_events(bus->input_io_event_source, e); + if (r < 0) + return r; + } + + r = sd_bus_get_timeout(bus, &until); + if (r < 0) + return r; + if (r > 0) { + int j; + + j = sd_event_source_set_time(bus->time_event_source, until); + if (j < 0) + return j; + } + + r = sd_event_source_set_enabled(bus->time_event_source, r > 0); + if (r < 0) + return r; + + return 1; +} + +static int quit_callback(sd_event_source *event, void *userdata) { + sd_bus *bus = userdata; + + assert(event); + + sd_bus_flush(bus); + + return 1; +} + +int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) { + int r; + + assert_return(bus, -EINVAL); + assert_return(event, -EINVAL); + assert_return(!bus->event, -EBUSY); + + assert(!bus->input_io_event_source); + assert(!bus->output_io_event_source); + assert(!bus->time_event_source); + + bus->event = sd_event_ref(event); + + r = sd_event_add_io(event, bus->input_fd, 0, io_callback, bus, &bus->input_io_event_source); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(bus->input_io_event_source, priority); + if (r < 0) + goto fail; + + if (bus->output_fd != bus->input_fd) { + r = sd_event_add_io(event, bus->output_fd, 0, io_callback, bus, &bus->output_io_event_source); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(bus->output_io_event_source, priority); + if (r < 0) + goto fail; + } + + r = sd_event_source_set_prepare(bus->input_io_event_source, prepare_callback); + if (r < 0) + goto fail; + + r = sd_event_add_monotonic(event, 0, 0, time_callback, bus, &bus->time_event_source); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(bus->time_event_source, priority); + if (r < 0) + goto fail; + + r = sd_event_add_quit(event, quit_callback, bus, &bus->quit_event_source); + if (r < 0) + goto fail; + + return 0; + +fail: + sd_bus_detach_event(bus); + return r; +} + +int sd_bus_detach_event(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(bus->event, -ENXIO); + + if (bus->input_io_event_source) + bus->input_io_event_source = sd_event_source_unref(bus->input_io_event_source); + + if (bus->output_io_event_source) + bus->output_io_event_source = sd_event_source_unref(bus->output_io_event_source); + + if (bus->time_event_source) + bus->time_event_source = sd_event_source_unref(bus->time_event_source); + + if (bus->quit_event_source) + bus->quit_event_source = sd_event_source_unref(bus->quit_event_source); + + if (bus->event) + bus->event = sd_event_unref(bus->event); + + return 0; +}