From: Lennart Poettering Date: Sat, 30 Nov 2013 00:02:51 +0000 (+0100) Subject: bus: synthesize local error reply when we cannot deliver a message to kdbus because... X-Git-Tag: v209~1195 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=a43b9ca3049d0f27cdb3bc8dad703e688cba31b3 bus: synthesize local error reply when we cannot deliver a message to kdbus because the destination is unavailable --- diff --git a/src/libsystemd-bus/bus-kernel.c b/src/libsystemd-bus/bus-kernel.c index a8579c98f..69143434b 100644 --- a/src/libsystemd-bus/bus-kernel.c +++ b/src/libsystemd-bus/bus-kernel.c @@ -393,13 +393,60 @@ int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) { assert(m); assert(bus->state == BUS_RUNNING); + /* If we can't deliver, we want room for the error message */ + r = bus_rqueue_make_room(bus); + if (r < 0) + return r; + r = bus_message_setup_kmsg(bus, m); if (r < 0) return r; r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus); - if (r < 0) - return errno == EAGAIN ? 0 : -errno; + if (r < 0) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus_message *reply; + + if (errno == EAGAIN || errno == EINTR) + return 0; + else if (errno == ENXIO || errno == ESRCH) { + + /* ENXIO: unique name not known + * ESRCH: well-known name not known */ + + if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) + sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Destination %s not known", m->destination); + else + return 0; + + } else if (errno == EADDRNOTAVAIL) { + + /* EADDRNOTAVAIL: activation is possible, but turned off in request flags */ + + if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) + sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Activation of %s not requested", m->destination); + else + return 0; + } else + return -errno; + + r = bus_message_new_synthetic_error( + bus, + BUS_MESSAGE_SERIAL(m), + &error, + &reply); + + if (r < 0) + return r; + + r = bus_seal_synthetic_message(bus, reply); + if (r < 0) + return r; + + bus->rqueue[bus->rqueue_size++] = reply; + + return 0; + } return 1; } diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c index 0eb61c4bd..05c5d8d1b 100644 --- a/src/libsystemd-bus/sd-bus.c +++ b/src/libsystemd-bus/sd-bus.c @@ -1274,9 +1274,9 @@ static int bus_write_message(sd_bus *bus, sd_bus_message *message, size_t *idx) assert(message); if (bus->is_kernel) - r = bus_kernel_write_message(bus, message); + return bus_kernel_write_message(bus, message); else - r = bus_socket_write_message(bus, message, idx); + return bus_socket_write_message(bus, message, idx); return r; } @@ -1627,20 +1627,17 @@ _public_ int sd_bus_call( if (r < 0) return r; + i = bus->rqueue_size; + r = sd_bus_send(bus, m, &serial); if (r < 0) return r; timeout = calc_elapse(usec); - i = bus->rqueue_size; for (;;) { usec_t left; - r = bus_read_message(bus); - if (r < 0) - return r; - while (i < bus->rqueue_size) { sd_bus_message *incoming = NULL; @@ -1660,24 +1657,13 @@ _public_ int sd_bus_call( sd_bus_message_unref(incoming); return 1; - } - - if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR) { - int k; - + } else if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR) r = sd_bus_error_copy(error, &incoming->error); - if (r < 0) { - sd_bus_message_unref(incoming); - return r; - } - - k = sd_bus_error_get_errno(&incoming->error); - sd_bus_message_unref(incoming); - return -k; - } + else + r = -EIO; sd_bus_message_unref(incoming); - return -EIO; + return r; } else if (incoming->header->serial == serial && bus->unique_name && @@ -1700,6 +1686,9 @@ _public_ int sd_bus_call( i++; } + r = bus_read_message(bus); + if (r < 0) + return r; if (r > 0) continue; diff --git a/src/libsystemd-bus/test-bus-kernel.c b/src/libsystemd-bus/test-bus-kernel.c index f970ca5ca..04dbc998d 100644 --- a/src/libsystemd-bus/test-bus-kernel.c +++ b/src/libsystemd-bus/test-bus-kernel.c @@ -35,6 +35,7 @@ int main(int argc, char *argv[]) { _cleanup_close_ int bus_ref = -1; _cleanup_free_ char *bus_name = NULL, *address = NULL; _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; const char *ua = NULL, *ub = NULL, *the_string = NULL; sd_bus *a, *b; int r, pipe_fds[2]; @@ -84,6 +85,10 @@ int main(int argc, char *argv[]) { printf("unique b: %s\n", ub); + r = sd_bus_call_method(a, "this.doesnt.exist", "/foo", "meh.mah", "muh", &error, NULL, "s", "yayayay"); + assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_SERVICE_UNKNOWN)); + assert_se(r == -EHOSTUNREACH); + r = sd_bus_add_match(b, "interface='waldo.com',member='Piep'", NULL, NULL); assert_se(r >= 0);