diff options
author | Lennart Poettering <lennart@poettering.net> | 2013-10-16 06:10:04 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2013-10-16 06:15:02 +0200 |
commit | 40ca29a1370379d43e44c0ed425eecc7218dcbca (patch) | |
tree | 39cde46c8166bd79471b0111251693edddfae3c4 /src | |
parent | 7a37d62501c97eb3b48a418ef764d0308e0c3fb9 (diff) |
timedated: use libsystemd-bus instead of libdbus for bus communication
Among other things this also adds a few things necessary for the change:
- Considerably more powerful error returning APIs in libsystemd-bus
- Adapter for connecting an sd_bus to an sd_event
- As I reworked the PolicyKit logic to the new library I also made it
asynchronous, so that PolicyKit requests of one user cannot block out
another user anymore.
- We always use the macro names for common bus error. That way it is
harder to mistype them since the compiler will notice
Diffstat (limited to 'src')
30 files changed, 1487 insertions, 835 deletions
diff --git a/src/journal/journal-gatewayd.c b/src/journal/journal-gatewayd.c index 06a236df6b..d4d4b7ef04 100644 --- a/src/journal/journal-gatewayd.c +++ b/src/journal/journal-gatewayd.c @@ -32,8 +32,7 @@ #include "sd-journal.h" #include "sd-daemon.h" #include "sd-bus.h" -#include "bus-message.h" -#include "bus-internal.h" +#include "bus-util.h" #include "logs-show.h" #include "microhttpd-util.h" #include "build.h" diff --git a/src/libsystemd-bus/bus-control.c b/src/libsystemd-bus/bus-control.c index 0ba8585805..836570d41e 100644 --- a/src/libsystemd-bus/bus-control.c +++ b/src/libsystemd-bus/bus-control.c @@ -33,6 +33,7 @@ #include "bus-message.h" #include "bus-control.h" #include "bus-bloom.h" +#include "bus-util.h" int sd_bus_get_unique_name(sd_bus *bus, const char **unique) { int r; diff --git a/src/libsystemd-bus/bus-convenience.c b/src/libsystemd-bus/bus-convenience.c index ddde5da163..95a7577b29 100644 --- a/src/libsystemd-bus/bus-convenience.c +++ b/src/libsystemd-bus/bus-convenience.c @@ -22,6 +22,7 @@ #include "bus-internal.h" #include "bus-message.h" #include "bus-signature.h" +#include "bus-util.h" int sd_bus_emit_signal( sd_bus *bus, @@ -99,7 +100,7 @@ int sd_bus_reply_method_return( assert_return(bus, -EINVAL); assert_return(call, -EINVAL); assert_return(call->sealed, -EPERM); - assert_return(call->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL, -EINVAL); + assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -134,7 +135,7 @@ int sd_bus_reply_method_error( assert_return(bus, -EINVAL); assert_return(call, -EINVAL); assert_return(call->sealed, -EPERM); - assert_return(call->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL, -EINVAL); + assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); assert_return(sd_bus_error_is_set(e), -EINVAL); assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -163,29 +164,74 @@ int sd_bus_reply_method_errorf( assert_return(bus, -EINVAL); assert_return(call, -EINVAL); assert_return(call->sealed, -EPERM); - assert_return(call->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL, -EINVAL); + assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN); assert_return(!bus_pid_changed(bus), -ECHILD); if (call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) return 0; - error.name = strdup(name); - if (!error.name) - return -ENOMEM; + va_start(ap, format); + r = bus_error_setfv(&error, name, format, ap); + va_end(ap); - error.need_free = true; + if (r < 0) + return r; - if (format) { - va_start(ap, format); - r = vasprintf((char**) &error.message, format, ap); - va_end(ap); + return sd_bus_reply_method_error(bus, call, &error); +} - if (r < 0) - return -ENOMEM; - } +int sd_bus_reply_method_errno( + sd_bus *bus, + sd_bus_message *call, + int error, + const sd_bus_error *p) { - return sd_bus_reply_method_error(bus, call, &error); + _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL; + + assert_return(bus, -EINVAL); + assert_return(call, -EINVAL); + assert_return(call->sealed, -EPERM); + assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); + assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) + return 0; + + if (sd_bus_error_is_set(p)) + return sd_bus_reply_method_error(bus, call, p); + + sd_bus_error_set_errno(&berror, error); + + return sd_bus_reply_method_error(bus, call, &berror); +} + +int sd_bus_reply_method_errnof( + sd_bus *bus, + sd_bus_message *call, + int error, + const char *format, + ...) { + + _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL; + va_list ap; + + assert_return(bus, -EINVAL); + assert_return(call, -EINVAL); + assert_return(call->sealed, -EPERM); + assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); + assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) + return 0; + + va_start(ap, format); + bus_error_set_errnofv(&berror, error, format, ap); + va_end(ap); + + return sd_bus_reply_method_error(bus, call, &berror); } int sd_bus_get_property( diff --git a/src/libsystemd-bus/bus-error.c b/src/libsystemd-bus/bus-error.c index 28fe15467f..c347b101bc 100644 --- a/src/libsystemd-bus/bus-error.c +++ b/src/libsystemd-bus/bus-error.c @@ -56,10 +56,9 @@ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) { if (!e) return 0; - if (bus_error_is_dirty(e)) - return -EINVAL; - if (!name) - return -EINVAL; + + assert_return(!bus_error_is_dirty(e), -EINVAL); + assert_return(name, -EINVAL); n = strdup(name); if (!n) @@ -78,27 +77,22 @@ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) { return 0; } -int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) { +int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) { char *n, *m = NULL; - va_list ap; int r; if (!e) return 0; - if (bus_error_is_dirty(e)) - return -EINVAL; - if (!name) - return -EINVAL; + + assert_return(!bus_error_is_dirty(e), -EINVAL); + assert_return(name, -EINVAL); n = strdup(name); if (!n) return -ENOMEM; if (format) { - va_start(ap, format); r = vasprintf(&m, format, ap); - va_end(ap); - if (r < 0) { free(n); return -ENOMEM; @@ -112,16 +106,32 @@ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ... return 0; } +int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) { + + if (format) { + int r; + va_list ap; + + va_start(ap, format); + r = bus_error_setfv(e, name, format, ap); + va_end(ap); + + return r; + } + + return sd_bus_error_set(e, name, NULL); +} + int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) { char *x, *y = NULL; if (!dest) return 0; - if (bus_error_is_dirty(dest)) - return -EINVAL; if (!sd_bus_error_is_set(e)) return 0; + assert_return(!bus_error_is_dirty(dest), -EINVAL); + x = strdup(e->name); if (!x) return -ENOMEM; @@ -140,13 +150,15 @@ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) { return 0; } -void sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) { +int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) { if (!e) - return; - if (bus_error_is_dirty(e)) - return; + return 0; + + assert_return(!bus_error_is_dirty(e), -EINVAL); + assert_return(name, -EINVAL); *e = SD_BUS_ERROR_MAKE(name, message); + return 0; } int sd_bus_error_is_set(const sd_bus_error *e) { @@ -163,106 +175,282 @@ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) { return streq_ptr(e->name, name); } -int bus_error_to_errno(const sd_bus_error* e) { +int sd_bus_error_get_errno(const sd_bus_error* e) { /* Better replce this with a gperf table */ if (!e) - return -EIO; + return EIO; if (!e->name) - return -EIO; + return EIO; - if (streq(e->name, "org.freedesktop.DBus.Error.NoMemory")) - return -ENOMEM; + if (streq(e->name, SD_BUS_ERROR_NO_MEMORY)) + return ENOMEM; + + if (streq(e->name, SD_BUS_ERROR_SERVICE_UNKNOWN)) + return EHOSTUNREACH; + + if (streq(e->name, SD_BUS_ERROR_NAME_HAS_NO_OWNER)) + return ENXIO; + + if (streq(e->name, SD_BUS_ERROR_NO_REPLY) || + streq(e->name, SD_BUS_ERROR_TIMEOUT) || + streq(e->name, "org.freedesktop.DBus.Error.TimedOut")) + return ETIMEDOUT; + + if (streq(e->name, SD_BUS_ERROR_IO_ERROR)) + return EIO; + + if (streq(e->name, SD_BUS_ERROR_BAD_ADDRESS)) + return EADDRNOTAVAIL; - if (streq(e->name, "org.freedesktop.DBus.Error.AuthFailed") || - streq(e->name, "org.freedesktop.DBus.Error.AccessDenied")) - return -EPERM; + if (streq(e->name, SD_BUS_ERROR_NOT_SUPPORTED)) + return ENOTSUP; - if (streq(e->name, "org.freedesktop.DBus.Error.InvalidArgs")) - return -EINVAL; + if (streq(e->name, SD_BUS_ERROR_LIMITS_EXCEEDED)) + return ENOBUFS; - if (streq(e->name, "org.freedesktop.DBus.Error.UnixProcessIdUnknown")) - return -ESRCH; + if (streq(e->name, SD_BUS_ERROR_ACCESS_DENIED) || + streq(e->name, SD_BUS_ERROR_AUTH_FAILED)) + return EACCES; - if (streq(e->name, "org.freedesktop.DBus.Error.FileNotFound")) - return -ENOENT; + if (streq(e->name, SD_BUS_ERROR_NO_SERVER)) + return EHOSTDOWN; - if (streq(e->name, "org.freedesktop.DBus.Error.FileExists")) - return -EEXIST; + if (streq(e->name, SD_BUS_ERROR_NO_NETWORK)) + return ENONET; - if (streq(e->name, "org.freedesktop.DBus.Error.Timeout")) - return -ETIMEDOUT; + if (streq(e->name, SD_BUS_ERROR_ADDRESS_IN_USE)) + return EADDRINUSE; - if (streq(e->name, "org.freedesktop.DBus.Error.IOError")) - return -EIO; + if (streq(e->name, SD_BUS_ERROR_DISCONNECTED)) + return ECONNRESET; - if (streq(e->name, "org.freedesktop.DBus.Error.Disconnected")) - return -ECONNRESET; + if (streq(e->name, SD_BUS_ERROR_INVALID_ARGS) || + streq(e->name, SD_BUS_ERROR_INVALID_SIGNATURE) || + streq(e->name, "org.freedesktop.DBus.Error.MatchRuleInvalid") || + streq(e->name, "org.freedesktop.DBus.Error.InvalidFileContent")) + return EINVAL; - if (streq(e->name, "org.freedesktop.DBus.Error.NotSupported")) - return -ENOTSUP; + if (streq(e->name, SD_BUS_ERROR_FILE_NOT_FOUND) || + streq(e->name, "org.freedesktop.DBus.Error.MatchRuleNotFound")) + return ENOENT; - return -EIO; + if (streq(e->name, SD_BUS_ERROR_FILE_EXISTS)) + return EEXIST; + + if (streq(e->name, SD_BUS_ERROR_UNKNOWN_METHOD) || + streq(e->name, SD_BUS_ERROR_UNKNOWN_OBJECT) || + streq(e->name, SD_BUS_ERROR_UNKNOWN_INTERFACE) || + streq(e->name, SD_BUS_ERROR_UNKNOWN_PROPERTY)) + return EBADR; + + if (streq(e->name, SD_BUS_ERROR_PROPERTY_READ_ONLY)) + return EROFS; + + if (streq(e->name, SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN) || + streq(e->name, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown")) + return ESRCH; + + if (streq(e->name, SD_BUS_ERROR_INCONSISTENT_MESSAGE)) + return EBADMSG; + + if (streq(e->name, "org.freedesktop.DBus.Error.ObjectPathInUse")) + return EBUSY; + + return EIO; } -int bus_error_from_errno(sd_bus_error *e, int error) { +static int bus_error_set_strerror_or_const(sd_bus_error *e, const char *name, int error, const char *fallback) { + size_t k = 64; + char *n = NULL, *m = NULL; + + if (error < 0) + error = -error; + if (!e) - return error; + return -error; - switch (error) { + assert_return(!bus_error_is_dirty(e), -EINVAL); + assert_return(name, -EINVAL); - case -ENOMEM: - sd_bus_error_set_const(e, "org.freedesktop.DBus.Error.NoMemory", "Out of memory"); - break; + for (;;) { + char *x; - case -EPERM: - case -EACCES: - sd_bus_error_set_const(e, "org.freedesktop.DBus.Error.AccessDenied", "Access denied"); - break; + m = new(char, k); + if (!m) + goto use_fallback; + + errno = 0; + x = strerror_r(error, m, k); + if (errno == ERANGE || strlen(x) >= k - 1) { + free(m); + k *= 2; + continue; + } - case -EINVAL: - sd_bus_error_set_const(e, "org.freedesktop.DBus.Error.InvalidArgs", "Invalid argument"); - break; + if (!x || errno) { + free(m); + goto use_fallback; + } - case -ESRCH: - sd_bus_error_set_const(e, "org.freedesktop.DBus.Error.UnixProcessIdUnknown", "No such process"); - break; - case -ENOENT: - sd_bus_error_set_const(e, "org.freedesktop.DBus.Error.FileNotFound", "File not found"); - break; + if (x != m) { + free(m); + sd_bus_error_set_const(e, name, x); + return -error; + } - case -EEXIST: - sd_bus_error_set_const(e, "org.freedesktop.DBus.Error.FileExists", "File exists"); break; + } - case -ETIMEDOUT: - case -ETIME: - sd_bus_error_set_const(e, "org.freedesktop.DBus.Error.Timeout", "Timed out"); - break; - case -EIO: - sd_bus_error_set_const(e, "org.freedesktop.DBus.Error.IOError", "Input/output error"); - break; + n = strdup(name); + if (!n) { + free(m); + goto use_fallback; + } - case -ENETRESET: - case -ECONNABORTED: - case -ECONNRESET: - sd_bus_error_set_const(e, "org.freedesktop.DBus.Error.Disconnected", "Disconnected"); - break; + e->name = n; + e->message = m; + e->need_free = true; - case -ENOTSUP: - sd_bus_error_set_const(e, "org.freedesktop.DBus.Error.NotSupported", "Not supported"); - break; + return -error; + +use_fallback: + sd_bus_error_set_const(e, name, fallback); + return -error; +} + +static sd_bus_error map_from_errno(int error) { + + if (error < 0) + error = -error; + + switch (error) { + + case ENOMEM: + return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_NO_NETWORK, "Out of memory"); + + case EPERM: + case EACCES: + return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_ACCESS_DENIED, "Access denied"); + + case EINVAL: + return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument"); + + case ESRCH: + return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process"); + + case ENOENT: + return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found"); + + case EEXIST: + return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_FILE_EXISTS, "File exists"); + + case ETIMEDOUT: + case ETIME: + return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_TIMEOUT, "Timed out"); + + case EIO: + return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_IO_ERROR, "Input/output error"); + + case ENETRESET: + case ECONNABORTED: + case ECONNRESET: + return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_DISCONNECTED, "Disconnected"); + + case ENOTSUP: + return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported"); + + case EADDRNOTAVAIL: + return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_BAD_ADDRESS, "Address not available"); + + case ENOBUFS: + return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded"); + + case EADDRINUSE: + return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use"); + + case EBADMSG: + return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message"); + } + + return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_FAILED, "Operation failed"); +} + +int sd_bus_error_set_errno(sd_bus_error *e, int error) { + sd_bus_error x; + + x = map_from_errno(error); + + return bus_error_set_strerror_or_const(e, x.name, error, x.message); +} + +int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) { + sd_bus_error x; + int r; + + if (error < 0) + error = -error; + + if (!e) + return 0; + + assert_return(!bus_error_is_dirty(e), -EINVAL); + + x = map_from_errno(error); + + if (format) { + char *n, *m; + + r = vasprintf(&m, format, ap); + if (r < 0) + goto fallback; + + n = strdup(x.name); + if (!n) { + free(m); + goto fallback; + } + + e->name = n; + e->message = m; + e->need_free = true; + return -error; } - sd_bus_error_set_const(e, "org.freedesktop.DBus.Error.Failed", "Operation failed"); - return error; +fallback: + return bus_error_set_strerror_or_const(e, x.name, error, x.message); +} + +int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) { + int r; + + if (!e) + return 0; + + assert_return(!bus_error_is_dirty(e), -EINVAL); + + if (format) { + va_list ap; + + va_start(ap, format); + r = bus_error_set_errnofv(e, error, format, ap); + va_end(ap); + + return r; + } + + return sd_bus_error_set_errno(e, error); } const char *bus_error_message(const sd_bus_error *e, int error) { + + if (error < 0) + error = -error; + if (e && e->message) return e->message; diff --git a/src/libsystemd-bus/bus-error.h b/src/libsystemd-bus/bus-error.h index ceca7d678f..5474c8c5ec 100644 --- a/src/libsystemd-bus/bus-error.h +++ b/src/libsystemd-bus/bus-error.h @@ -23,9 +23,9 @@ #include "sd-bus.h" -int bus_error_to_errno(const sd_bus_error *e); -int bus_error_from_errno(sd_bus_error *e, int error); - bool bus_error_is_dirty(sd_bus_error *e); const char *bus_error_message(const sd_bus_error *e, int error); + +int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap); +int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap); diff --git a/src/libsystemd-bus/bus-internal.c b/src/libsystemd-bus/bus-internal.c index afff7bd7c1..942ac2b953 100644 --- a/src/libsystemd-bus/bus-internal.c +++ b/src/libsystemd-bus/bus-internal.c @@ -252,13 +252,13 @@ bool path_simple_pattern(const char *pattern, const char *value) { int bus_message_type_from_string(const char *s, uint8_t *u) { if (streq(s, "signal")) - *u = SD_BUS_MESSAGE_TYPE_SIGNAL; + *u = SD_BUS_MESSAGE_SIGNAL; else if (streq(s, "method_call")) - *u = SD_BUS_MESSAGE_TYPE_METHOD_CALL; + *u = SD_BUS_MESSAGE_METHOD_CALL; else if (streq(s, "error")) - *u = SD_BUS_MESSAGE_TYPE_METHOD_ERROR; + *u = SD_BUS_MESSAGE_METHOD_ERROR; else if (streq(s, "method_return")) - *u = SD_BUS_MESSAGE_TYPE_METHOD_RETURN; + *u = SD_BUS_MESSAGE_METHOD_RETURN; else return -EINVAL; @@ -266,13 +266,13 @@ int bus_message_type_from_string(const char *s, uint8_t *u) { } const char *bus_message_type_to_string(uint8_t u) { - if (u == SD_BUS_MESSAGE_TYPE_SIGNAL) + if (u == SD_BUS_MESSAGE_SIGNAL) return "signal"; - else if (u == SD_BUS_MESSAGE_TYPE_METHOD_CALL) + else if (u == SD_BUS_MESSAGE_METHOD_CALL) return "method_call"; - else if (u == SD_BUS_MESSAGE_TYPE_METHOD_ERROR) + else if (u == SD_BUS_MESSAGE_METHOD_ERROR) return "error"; - else if (u == SD_BUS_MESSAGE_TYPE_METHOD_RETURN) + else if (u == SD_BUS_MESSAGE_METHOD_RETURN) return "method_return"; else return NULL; diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h index 31e10b2c27..1726b61841 100644 --- a/src/libsystemd-bus/bus-internal.h +++ b/src/libsystemd-bus/bus-internal.h @@ -240,14 +240,12 @@ struct sd_bus { uint64_t hello_flags; uint64_t match_cookie; -}; - -static inline void bus_unrefp(sd_bus **b) { - sd_bus_unref(*b); -} -#define _cleanup_bus_unref_ __attribute__((cleanup(bus_unrefp))) -#define _cleanup_bus_error_free_ __attribute__((cleanup(sd_bus_error_free))) + sd_event_source *input_io_event_source; + sd_event_source *output_io_event_source; + sd_event_source *time_event_source; + sd_event *event; +}; #define BUS_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC)) diff --git a/src/libsystemd-bus/bus-message.c b/src/libsystemd-bus/bus-message.c index 3f68435d21..bb339f5a82 100644 --- a/src/libsystemd-bus/bus-message.c +++ b/src/libsystemd-bus/bus-message.c @@ -461,7 +461,7 @@ int sd_bus_message_new_signal( if (bus && bus->state == BUS_UNSET) return -ENOTCONN; - t = message_new(bus, SD_BUS_MESSAGE_TYPE_SIGNAL); + t = message_new(bus, SD_BUS_MESSAGE_SIGNAL); if (!t) return -ENOMEM; @@ -509,7 +509,7 @@ int sd_bus_message_new_method_call( if (bus && bus->state == BUS_UNSET) return -ENOTCONN; - t = message_new(bus, SD_BUS_MESSAGE_TYPE_METHOD_CALL); + t = message_new(bus, SD_BUS_MESSAGE_METHOD_CALL); if (!t) return -ENOMEM; @@ -553,7 +553,7 @@ static int message_new_reply( return -EINVAL; if (!call->sealed) return -EPERM; - if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL) + if (call->header->type != SD_BUS_MESSAGE_METHOD_CALL) return -EINVAL; if (!m) return -EINVAL; @@ -592,7 +592,7 @@ int sd_bus_message_new_method_return( sd_bus_message *call, sd_bus_message **m) { - return message_new_reply(bus, call, SD_BUS_MESSAGE_TYPE_METHOD_RETURN, m); + return message_new_reply(bus, call, SD_BUS_MESSAGE_METHOD_RETURN, m); } int sd_bus_message_new_method_error( @@ -609,7 +609,7 @@ int sd_bus_message_new_method_error( if (!m) return -EINVAL; - r = message_new_reply(bus, call, SD_BUS_MESSAGE_TYPE_METHOD_ERROR, &t); + r = message_new_reply(bus, call, SD_BUS_MESSAGE_METHOD_ERROR, &t); if (r < 0) return r; @@ -639,46 +639,60 @@ int sd_bus_message_new_method_errorf( const char *format, ...) { - sd_bus_message *t; + _cleanup_free_ sd_bus_error error = SD_BUS_ERROR_NULL; va_list ap; int r; - if (!name) - return -EINVAL; - if (!m) - return -EINVAL; + assert_return(name, -EINVAL); + assert_return(m, -EINVAL); + + va_start(ap, format); + r = bus_error_setfv(&error, name, format, ap); + va_end(ap); - r = message_new_reply(bus, call, SD_BUS_MESSAGE_TYPE_METHOD_ERROR, &t); if (r < 0) return r; - r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, name, &t->error.name); - if (r < 0) - goto fail; + return sd_bus_message_new_method_error(bus, call, &error, m); +} - if (format) { - _cleanup_free_ char *message = NULL; +int sd_bus_message_new_method_errno( + sd_bus *bus, + sd_bus_message *call, + int error, + const sd_bus_error *p, + sd_bus_message **m) { - va_start(ap, format); - r = vasprintf(&message, format, ap); - va_end(ap); + _cleanup_free_ sd_bus_error berror = SD_BUS_ERROR_NULL; - if (r < 0) { - r = -ENOMEM; - goto fail; - } + if (sd_bus_error_is_set(p)) + return sd_bus_message_new_method_error(bus, call, p, m); - r = message_append_basic(t, SD_BUS_TYPE_STRING, message, (const void**) &t->error.message); - if (r < 0) - goto fail; - } + sd_bus_error_set_errno(&berror, error); - *m = t; - return 0; + return sd_bus_message_new_method_error(bus, call, &berror, m); +} -fail: - message_free(t); - return r; +int sd_bus_message_new_method_errnof( + sd_bus *bus, + sd_bus_message *call, + sd_bus_message **m, + int error, + const char *format, + ...) { + + _cleanup_free_ sd_bus_error berror = SD_BUS_ERROR_NULL; + va_list ap; + int r; + + va_start(ap, format); + r = bus_error_set_errnofv(&berror, error, format, ap); + va_end(ap); + + if (r < 0) + return r; + + return sd_bus_message_new_method_error(bus, call, &berror, m); } int bus_message_new_synthetic_error( @@ -693,7 +707,7 @@ int bus_message_new_synthetic_error( assert(sd_bus_error_is_set(e)); assert(m); - t = message_new(bus, SD_BUS_MESSAGE_TYPE_METHOD_ERROR); + t = message_new(bus, SD_BUS_MESSAGE_METHOD_ERROR); if (!t) return -ENOMEM; @@ -789,7 +803,7 @@ int sd_bus_message_get_no_reply(sd_bus_message *m) { if (!m) return -EINVAL; - return m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL ? !!(m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) : 0; + return m->header->type == SD_BUS_MESSAGE_METHOD_CALL ? !!(m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) : 0; } const char *sd_bus_message_get_path(sd_bus_message *m) { @@ -1126,7 +1140,7 @@ int sd_bus_message_is_signal(sd_bus_message *m, const char *interface, const cha if (!m) return -EINVAL; - if (m->header->type != SD_BUS_MESSAGE_TYPE_SIGNAL) + if (m->header->type != SD_BUS_MESSAGE_SIGNAL) return 0; if (interface && (!m->interface || !streq(m->interface, interface))) @@ -1142,7 +1156,7 @@ int sd_bus_message_is_method_call(sd_bus_message *m, const char *interface, cons if (!m) return -EINVAL; - if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL) + if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) return 0; if (interface && (!m->interface || !streq(m->interface, interface))) @@ -1158,7 +1172,7 @@ int sd_bus_message_is_method_error(sd_bus_message *m, const char *name) { if (!m) return -EINVAL; - if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_ERROR) + if (m->header->type != SD_BUS_MESSAGE_METHOD_ERROR) return 0; if (name && (!m->error.name || !streq(m->error.name, name))) @@ -1172,7 +1186,7 @@ int sd_bus_message_set_no_reply(sd_bus_message *m, int b) { return -EINVAL; if (m->sealed) return -EPERM; - if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL) + if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) return -EPERM; if (b) @@ -3876,25 +3890,25 @@ int bus_message_parse_fields(sd_bus_message *m) { switch (m->header->type) { - case SD_BUS_MESSAGE_TYPE_SIGNAL: + case SD_BUS_MESSAGE_SIGNAL: if (!m->path || !m->interface || !m->member) return -EBADMSG; break; - case SD_BUS_MESSAGE_TYPE_METHOD_CALL: + case SD_BUS_MESSAGE_METHOD_CALL: if (!m->path || !m->member) return -EBADMSG; break; - case SD_BUS_MESSAGE_TYPE_METHOD_RETURN: + case SD_BUS_MESSAGE_METHOD_RETURN: if (m->reply_serial == 0) return -EBADMSG; break; - case SD_BUS_MESSAGE_TYPE_METHOD_ERROR: + case SD_BUS_MESSAGE_METHOD_ERROR: if (m->reply_serial == 0 || !m->error.name) return -EBADMSG; @@ -3902,7 +3916,7 @@ int bus_message_parse_fields(sd_bus_message *m) { } /* Try to read the error message, but if we can't it's a non-issue */ - if (m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_ERROR) + if (m->header->type == SD_BUS_MESSAGE_METHOD_ERROR) sd_bus_message_read(m, "s", &m->error.message); return 0; @@ -4363,24 +4377,21 @@ int bus_header_message_size(struct bus_header *h, size_t *sum) { return 0; } -int bus_message_to_errno(sd_bus_message *m) { - assert(m); +int sd_bus_message_get_errno(sd_bus_message *m) { + assert_return(m, -EINVAL); - if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_ERROR) + if (m->header->type != SD_BUS_MESSAGE_METHOD_ERROR) return 0; - return bus_error_to_errno(&m->error); + return sd_bus_error_get_errno(&m->error); } -int sd_bus_message_get_signature(sd_bus_message *m, int complete, const char **signature) { +const char* sd_bus_message_get_signature(sd_bus_message *m, int complete) { struct bus_container *c; if (!m) - return -EINVAL; - if (!signature) - return -EINVAL; + return NULL; c = complete ? &m->root_container : message_get_container(m); - *signature = c->signature ?: ""; - return 0; + return c->signature ?: ""; } diff --git a/src/libsystemd-bus/bus-message.h b/src/libsystemd-bus/bus-message.h index 2fb11ea3b1..c79ff1bc93 100644 --- a/src/libsystemd-bus/bus-message.h +++ b/src/libsystemd-bus/bus-message.h @@ -189,12 +189,6 @@ static inline void* BUS_MESSAGE_FIELDS(sd_bus_message *m) { return (uint8_t*) m->header + sizeof(struct bus_header); } -static inline void bus_message_unrefp(sd_bus_message **m) { - sd_bus_message_unref(*m); -} - -#define _cleanup_bus_message_unref_ __attribute__((cleanup(bus_message_unrefp))) - int bus_message_seal(sd_bus_message *m, uint64_t serial); int bus_message_dump(sd_bus_message *m); int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz); diff --git a/src/libsystemd-bus/bus-objects.c b/src/libsystemd-bus/bus-objects.c index 4091490c5e..0c935e947d 100644 --- a/src/libsystemd-bus/bus-objects.c +++ b/src/libsystemd-bus/bus-objects.c @@ -27,6 +27,7 @@ #include "bus-signature.h" #include "bus-introspect.h" #include "bus-objects.h" +#include "bus-util.h" static int node_vtable_get_userdata( sd_bus *bus, @@ -275,13 +276,13 @@ static int method_callbacks_run( if (r < 0) return r; - r = sd_bus_message_get_signature(m, true, &signature); - if (r < 0) - return r; + signature = sd_bus_message_get_signature(m, true); + if (!signature) + return -EINVAL; if (!streq(strempty(c->vtable->x.method.signature), signature)) { r = sd_bus_reply_method_errorf(bus, m, - "org.freedesktop.DBus.Error.InvalidArgs", + SD_BUS_ERROR_INVALID_ARGS, "Invalid arguments '%s' to call %s:%s, expecting '%s'.", signature, c->interface, c->member, strempty(c->vtable->x.method.signature)); if (r < 0) @@ -475,7 +476,7 @@ static int property_get_set_callbacks_run( } else { if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY) - sd_bus_error_setf(&error, "org.freedesktop.DBus.Error.PropertyReadOnly", "Property '%s' is not writable.", c->member); + sd_bus_error_setf(&error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member); else { /* Avoid that we call the set routine more * than once if the processing of this message @@ -634,7 +635,7 @@ static int property_get_all_callbacks_run( if (!found_interface) { r = sd_bus_reply_method_errorf( bus, m, - "org.freedesktop.DBus.Error.UnknownInterface", + SD_BUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface '%s'.", iface); if (r < 0) return r; @@ -1164,7 +1165,7 @@ int bus_process_object(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 (!m->path) @@ -1203,12 +1204,12 @@ int bus_process_object(sd_bus *bus, sd_bus_message *m) { sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set")) r = sd_bus_reply_method_errorf( bus, m, - "org.freedesktop.DBus.Error.UnknownProperty", + SD_BUS_ERROR_UNKNOWN_PROPERTY, "Unknown property or interface."); else r = sd_bus_reply_method_errorf( bus, m, - "org.freedesktop.DBus.Error.UnknownMethod", + SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method '%s' or interface '%s'.", m->member, m->interface); if (r < 0) @@ -1875,8 +1876,6 @@ static int emit_properties_changed_on_interface( r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u)); if (r < 0) return r; - if (sd_bus_error_is_set(&error)) - return bus_error_to_errno(&error); if (bus->nodes_modified) return 0; @@ -2047,8 +2046,6 @@ static int interfaces_added_append_one_prefix( r = vtable_append_all_properties(bus, m,path, c, u, &error); if (r < 0) return r; - if (sd_bus_error_is_set(&error)) - return bus_error_to_errno(&error); if (bus->nodes_modified) return 0; diff --git a/src/libsystemd-bus/bus-util.c b/src/libsystemd-bus/bus-util.c new file mode 100644 index 0000000000..5a70fae77f --- /dev/null +++ b/src/libsystemd-bus/bus-util.c @@ -0,0 +1,378 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "sd-event.h" +#include "sd-bus.h" + +#include "util.h" +#include "macro.h" +#include "def.h" + +#include "bus-util.h" + +static int quit_callback(sd_bus *bus, sd_bus_message *m, void *userdata) { + sd_event *e = userdata; + + assert(bus); + assert(m); + assert(e); + + sd_event_request_quit(e); + return 1; +} + +int bus_async_unregister_and_quit(sd_event *e, sd_bus *bus, const char *name) { + _cleanup_free_ char *match = NULL; + int r; + + assert(e); + assert(bus); + assert(name); + + r = asprintf(&match, "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameLost',arg0='%s'", name); + if (r < 0) + return r; + + r = sd_bus_add_match(bus, match, quit_callback, e); + if (r < 0) + return r; + + r = sd_bus_release_name(bus, name); + if (r < 0) + return r; + + if (r != SD_BUS_NAME_RELEASED) + return -EIO; + + return 0; +} + +int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout) { + bool exiting = false; + int r; + + assert(e); + assert(bus); + assert(name); + + for (;;) { + r = sd_event_get_state(e); + if (r < 0) + return r; + + if (r == SD_EVENT_FINISHED) + break; + + r = sd_event_run(e, exiting ? (uint64_t) -1 : 5 * USEC_PER_SEC /* DEFAULT_EXIT_USEC */); + if (r < 0) + return r; + + if (r == 0 && !exiting) { + r = bus_async_unregister_and_quit(e, bus, name); + if (r < 0) + return r; + + exiting = true; + } + } + + return 0; +} + +int bus_property_get_tristate( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + sd_bus_error *error, + void *userdata) { + + int *tristate = userdata; + int r; + + r = sd_bus_message_append(reply, "b", *tristate > 0); + if (r < 0) + return r; + + return 1; +} + +int bus_verify_polkit( + sd_bus *bus, + sd_bus_message *m, + const char *action, + bool interactive, + bool *_challenge, + sd_bus_error *e) { + + const char *sender; + uid_t uid; + int r; + + assert(bus); + assert(m); + assert(action); + + sender = sd_bus_message_get_sender(m); + if (!sender) + return -EBADMSG; + + r = sd_bus_get_owner_uid(bus, sender, &uid); + if (r < 0) + return r; + + if (uid == 0) + return 1; + +#ifdef ENABLE_POLKIT + else { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + bool authorized = false, challenge = false; + + r = sd_bus_call_method( + bus, + "org.freedesktop.PolicyKit1", + "/org/freedesktop/PolicyKit1/Authority", + "org.freedesktop.PolicyKit1.Authority", + "CheckAuthorization", + e, + &reply, + "(sa{sv})sa{ss}us", + "system-bus-name", 1, "name", "s", sender, + action, + 0, + interactive ? 1 : 0, + ""); + + if (r < 0) { + /* Treat no PK available as access denied */ + if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) { + sd_bus_error_free(e); + return -EACCES; + } + + return r; + } + + r = sd_bus_message_read(reply, "(bb)", &authorized, &challenge); + if (r < 0) + return r; + + if (authorized) + return 1; + + if (_challenge) { + *_challenge = challenge; + return 0; + } + } +#endif + + return -EACCES; +} + +#ifdef ENABLE_POLKIT + +typedef struct AsyncPolkitQuery { + sd_bus_message *request, *reply; + sd_bus_message_handler_t callback; + void *userdata; + uint64_t serial; +} AsyncPolkitQuery; + +static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata) { + AsyncPolkitQuery *q = userdata; + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + int r; + + assert(bus); + assert(reply); + assert(q); + + q->reply = sd_bus_message_ref(reply); + q->serial = 0; + + m = sd_bus_message_ref(q->request); + + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + r = q->callback(bus, m, q->userdata); + if (r < 0) + return r; + + return 1; +} + +static void async_polkit_query_free(sd_bus *b, AsyncPolkitQuery *q) { + + if (!q) + return; + + if (q->serial > 0 && b) + sd_bus_send_with_reply_cancel(b, q->serial); + + sd_bus_message_unref(q->request); + sd_bus_message_unref(q->reply); + free(q); +} + +#endif + +int bus_verify_polkit_async( + sd_bus *bus, + Hashmap **registry, + sd_bus_message *m, + const char *action, + bool interactive, + sd_bus_error *error, + sd_bus_message_handler_t callback, + void *userdata) { + +#ifdef ENABLE_POLKIT + _cleanup_bus_message_unref_ sd_bus_message *pk = NULL; + AsyncPolkitQuery *q; +#endif + const char *sender; + uid_t uid; + int r; + + assert(bus); + assert(registry); + assert(m); + assert(action); + +#ifdef ENABLE_POLKIT + q = hashmap_remove(*registry, m); + if (q) { + bool authorized, challenge; + + /* This is the second invocation of this function, and + * there's already a response from polkit, let's + * process it */ + assert(q->reply); + + if (sd_bus_message_is_method_error(q->reply, NULL)) { + const sd_bus_error *e; + + /* Treat no PK available as access denied */ + if (sd_bus_message_is_method_error(q->reply, SD_BUS_ERROR_SERVICE_UNKNOWN)) { + async_polkit_query_free(bus, q); + return -EACCES; + } + + e = sd_bus_message_get_error(q->reply); + sd_bus_error_copy(error, e); + r = sd_bus_error_get_errno(e); + + async_polkit_query_free(bus, q); + return r; + } + + r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}"); + if (r >= 0) + r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge); + + async_polkit_query_free(bus, q); + + if (r < 0) + return r; + + if (authorized) + return 1; + + return -EACCES; + } +#endif + + sender = sd_bus_message_get_sender(m); + if (!sender) + return -EBADMSG; + + r = sd_bus_get_owner_uid(bus, sender, &uid); + if (r < 0) + return r; + + if (uid == 0) + return 1; +#ifdef ENABLE_POLKIT + + r = hashmap_ensure_allocated(registry, trivial_hash_func, trivial_compare_func); + if (r < 0) + return r; + + r = sd_bus_message_new_method_call( + bus, + "org.freedesktop.PolicyKit1", + "/org/freedesktop/PolicyKit1/Authority", + "org.freedesktop.PolicyKit1.Authority", + "CheckAuthorization", + &pk); + if (r < 0) + return r; + + r = sd_bus_message_append( + pk, + "(sa{sv})sa{ss}us", + "system-bus-name", 1, "name", "s", sender, + action, + 0, + interactive ? 1 : 0, + ""); + if (r < 0) + return r; + + q = new0(AsyncPolkitQuery, 1); + if (!q) + return -ENOMEM; + + q->request = sd_bus_message_ref(m); + q->callback = callback; + q->userdata = userdata; + + r = hashmap_put(*registry, m, q); + if (r < 0) { + async_polkit_query_free(bus, q); + return r; + } + + r = sd_bus_send_with_reply(bus, pk, async_polkit_callback, q, 0, &q->serial); + if (r < 0) + return r; + + return 0; +#endif + + return -EACCES; +} + +void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry) { +#ifdef ENABLE_POLKIT + AsyncPolkitQuery *q; + + while ((q = hashmap_steal_first(registry))) + async_polkit_query_free(bus, q); + + hashmap_free(registry); +#endif +} diff --git a/src/libsystemd-bus/bus-util.h b/src/libsystemd-bus/bus-util.h new file mode 100644 index 0000000000..354183f690 --- /dev/null +++ b/src/libsystemd-bus/bus-util.h @@ -0,0 +1,45 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "sd-event.h" +#include "sd-bus.h" +#include "hashmap.h" +#include "time-util.h" +#include "util.h" + +int bus_async_unregister_and_quit(sd_event *e, sd_bus *bus, const char *name); + +int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout); +int bus_property_get_tristate(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, sd_bus_error *error, void *userdata); + +int bus_verify_polkit(sd_bus *bus, sd_bus_message *m, const char *action, bool interactive, bool *_challenge, sd_bus_error *e); + +int bus_verify_polkit_async(sd_bus *bus, Hashmap **registry, sd_bus_message *m, const char *action, bool interactive, sd_bus_error *error, sd_bus_message_handler_t callback, void *userdata); +void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry); + +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus*, sd_bus_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_message*, sd_bus_message_unref); + +#define _cleanup_bus_unref_ _cleanup_(sd_bus_unrefp) +#define _cleanup_bus_error_free_ _cleanup_(sd_bus_error_free) +#define _cleanup_bus_message_unref_ _cleanup_(sd_bus_message_unrefp) diff --git a/src/libsystemd-bus/busctl.c b/src/libsystemd-bus/busctl.c index 220c1eb34e..b15b41ae77 100644 --- a/src/libsystemd-bus/busctl.c +++ b/src/libsystemd-bus/busctl.c @@ -30,6 +30,7 @@ #include "sd-bus.h" #include "bus-message.h" #include "bus-internal.h" +#include "bus-util.h" static bool arg_no_pager = false; static char *arg_address = NULL; diff --git a/src/libsystemd-bus/event-util.h b/src/libsystemd-bus/event-util.h new file mode 100644 index 0000000000..e58020dedf --- /dev/null +++ b/src/libsystemd-bus/event-util.h @@ -0,0 +1,30 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "util.h" + +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event*, sd_event_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event_source*, sd_event_source_unref); + +#define _cleanup_event_unref_ _cleanup_(sd_event_unrefp) +#define _cleanup_event_source_unref_ _cleanup_(sd_event_source_unrefp) diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c index 5d1fcd90a0..665f1e6c8b 100644 --- a/src/libsystemd-bus/sd-bus.c +++ b/src/libsystemd-bus/sd-bus.c @@ -45,6 +45,7 @@ #include "bus-introspect.h" #include "bus-signature.h" #include "bus-objects.h" +#include "bus-util.h" static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec); @@ -104,6 +105,8 @@ static void bus_free(sd_bus *b) { assert(b); + sd_bus_detach_event(b); + bus_close_fds(b); if (b->kdbus_buffer) @@ -350,9 +353,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) @@ -1038,6 +1043,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); @@ -1318,7 +1325,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 +1435,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,7 +1482,7 @@ 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; @@ -1485,7 +1492,7 @@ int sd_bus_send_with_reply_and_block( 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 +1501,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); @@ -1623,7 +1630,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; @@ -1649,8 +1656,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) @@ -1666,8 +1673,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); @@ -1748,7 +1755,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")) @@ -1775,7 +1782,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); } @@ -1797,6 +1804,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; @@ -1855,11 +1868,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; @@ -2123,3 +2136,143 @@ 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; +} + +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; + + 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->event) + bus->event = sd_event_unref(bus->event); + + return 0; +} diff --git a/src/libsystemd-bus/test-bus-chat.c b/src/libsystemd-bus/test-bus-chat.c index aefe8f1b3b..efc19c68b3 100644 --- a/src/libsystemd-bus/test-bus-chat.c +++ b/src/libsystemd-bus/test-bus-chat.c @@ -34,6 +34,7 @@ #include "bus-error.h" #include "bus-match.h" #include "bus-internal.h" +#include "bus-util.h" static int match_callback(sd_bus *bus, sd_bus_message *m, void *userdata) { log_info("Match triggered! interface=%s member=%s", strna(sd_bus_message_get_interface(m)), strna(sd_bus_message_get_member(m))); @@ -243,7 +244,7 @@ static int server(sd_bus *bus) { r = sd_bus_reply_method_error( bus, m, - &SD_BUS_ERROR_MAKE("org.freedesktop.DBus.Error.UnknownMethod", "Unknown method.")); + &SD_BUS_ERROR_MAKE(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method.")); if (r < 0) { log_error("Failed to send reply: %s", strerror(-r)); goto fail; @@ -359,7 +360,7 @@ finish: static int quit_callback(sd_bus *b, sd_bus_message *m, void *userdata) { bool *x = userdata; - log_error("Quit callback: %s", strerror(-bus_message_to_errno(m))); + log_error("Quit callback: %s", strerror(sd_bus_message_get_errno(m))); *x = 1; return 1; diff --git a/src/libsystemd-bus/test-bus-kernel-benchmark.c b/src/libsystemd-bus/test-bus-kernel-benchmark.c index 2e84cd9244..23e4d67980 100644 --- a/src/libsystemd-bus/test-bus-kernel-benchmark.c +++ b/src/libsystemd-bus/test-bus-kernel-benchmark.c @@ -31,6 +31,7 @@ #include "bus-error.h" #include "bus-kernel.h" #include "bus-internal.h" +#include "bus-util.h" #define MAX_SIZE (4*1024*1024) diff --git a/src/libsystemd-bus/test-bus-kernel-bloom.c b/src/libsystemd-bus/test-bus-kernel-bloom.c index 5445d3488f..dcf048338f 100644 --- a/src/libsystemd-bus/test-bus-kernel-bloom.c +++ b/src/libsystemd-bus/test-bus-kernel-bloom.c @@ -26,6 +26,7 @@ #include "bus-message.h" #include "bus-error.h" #include "bus-kernel.h" +#include "bus-util.h" static void test_one( const char *path, diff --git a/src/libsystemd-bus/test-bus-kernel.c b/src/libsystemd-bus/test-bus-kernel.c index 680dcde5b4..075903c2f4 100644 --- a/src/libsystemd-bus/test-bus-kernel.c +++ b/src/libsystemd-bus/test-bus-kernel.c @@ -28,6 +28,7 @@ #include "bus-message.h" #include "bus-error.h" #include "bus-kernel.h" +#include "bus-util.h" int main(int argc, char *argv[]) { _cleanup_close_ int bus_ref = -1; diff --git a/src/libsystemd-bus/test-bus-marshal.c b/src/libsystemd-bus/test-bus-marshal.c index ef1a77f5fc..1e2caa0620 100644 --- a/src/libsystemd-bus/test-bus-marshal.c +++ b/src/libsystemd-bus/test-bus-marshal.c @@ -34,6 +34,7 @@ #include "sd-bus.h" #include "bus-message.h" +#include "bus-util.h" int main(int argc, char *argv[]) { _cleanup_bus_message_unref_ sd_bus_message *m = NULL; diff --git a/src/libsystemd-bus/test-bus-match.c b/src/libsystemd-bus/test-bus-match.c index db977f726e..8df491a60a 100644 --- a/src/libsystemd-bus/test-bus-match.c +++ b/src/libsystemd-bus/test-bus-match.c @@ -27,6 +27,7 @@ #include "bus-match.h" #include "bus-message.h" +#include "bus-util.h" static bool mask[32]; diff --git a/src/libsystemd-bus/test-bus-objects.c b/src/libsystemd-bus/test-bus-objects.c index 543063f361..8758404189 100644 --- a/src/libsystemd-bus/test-bus-objects.c +++ b/src/libsystemd-bus/test-bus-objects.c @@ -33,6 +33,7 @@ #include "sd-bus.h" #include "bus-internal.h" #include "bus-message.h" +#include "bus-util.h" struct context { int fds[2]; @@ -269,13 +270,13 @@ static int client(struct context *c) { r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Doesntexist", &error, &reply, ""); assert_se(r < 0); - assert_se(sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.UnknownMethod")); + assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)); sd_bus_error_free(&error); r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "as", 1, "hallo"); assert_se(r < 0); - assert_se(sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.InvalidArgs")); + assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)); sd_bus_error_free(&error); @@ -372,12 +373,12 @@ static int client(struct context *c) { r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "org.freedesktop.systemd.ValueTest2"); assert_se(r < 0); - assert_se(sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.UnknownInterface")); + assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_INTERFACE)); sd_bus_error_free(&error); r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, ""); assert_se(r < 0); - assert_se(sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.UnknownMethod")); + assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)); sd_bus_error_free(&error); r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, ""); diff --git a/src/libsystemd-bus/test-bus-server.c b/src/libsystemd-bus/test-bus-server.c index ef26a65d87..478a81e5cb 100644 --- a/src/libsystemd-bus/test-bus-server.c +++ b/src/libsystemd-bus/test-bus-server.c @@ -32,6 +32,7 @@ #include "sd-bus.h" #include "bus-internal.h" #include "bus-message.h" +#include "bus-util.h" struct context { int fds[2]; @@ -98,7 +99,7 @@ static void *server(void *p) { } else if (sd_bus_message_is_method_call(m, NULL, NULL)) { r = sd_bus_message_new_method_error( bus, m, - &SD_BUS_ERROR_MAKE("org.freedesktop.DBus.Error.UnknownMethod", "Unknown method."), + &SD_BUS_ERROR_MAKE(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method."), &reply); if (r < 0) { log_error("Failed to allocate return: %s", strerror(-r)); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 15e48739b9..ad0287dbf4 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -61,8 +61,7 @@ #include "fdset.h" #include "build.h" #include "fileio.h" -#include "bus-internal.h" -#include "bus-message.h" +#include "bus-util.h" #ifndef TTY_GID #define TTY_GID 5 @@ -1187,7 +1186,7 @@ static int register_machine(void) { NULL, "sayssusa(sv)", arg_machine, - SD_BUS_APPEND_ID128(arg_uuid), + SD_BUS_MESSAGE_APPEND_ID128(arg_uuid), "nspawn", "container", (uint32_t) 0, diff --git a/src/run/run.c b/src/run/run.c index 18a4920f03..49a34fcbd4 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -23,8 +23,7 @@ #include <getopt.h> #include "sd-bus.h" -#include "bus-internal.h" -#include "bus-message.h" +#include "bus-util.h" #include "strv.h" #include "build.h" #include "unit-name.h" diff --git a/src/stdio-bridge/stdio-bridge.c b/src/stdio-bridge/stdio-bridge.c index ab1a43ab1a..07218e90c5 100644 --- a/src/stdio-bridge/stdio-bridge.c +++ b/src/stdio-bridge/stdio-bridge.c @@ -36,6 +36,7 @@ #include "sd-bus.h" #include "bus-internal.h" #include "bus-message.h" +#include "bus-util.h" int main(int argc, char *argv[]) { _cleanup_bus_unref_ sd_bus *a = NULL, *b = NULL; diff --git a/src/systemd/sd-bus-protocol.h b/src/systemd/sd-bus-protocol.h index 8b40386cf2..ff2bcc93b8 100644 --- a/src/systemd/sd-bus-protocol.h +++ b/src/systemd/sd-bus-protocol.h @@ -32,10 +32,10 @@ extern "C" { enum { _SD_BUS_MESSAGE_TYPE_INVALID = 0, - SD_BUS_MESSAGE_TYPE_METHOD_CALL, - SD_BUS_MESSAGE_TYPE_METHOD_RETURN, - SD_BUS_MESSAGE_TYPE_METHOD_ERROR, - SD_BUS_MESSAGE_TYPE_SIGNAL, + SD_BUS_MESSAGE_METHOD_CALL, + SD_BUS_MESSAGE_METHOD_RETURN, + SD_BUS_MESSAGE_METHOD_ERROR, + SD_BUS_MESSAGE_SIGNAL, _SD_BUS_MESSAGE_TYPE_MAX }; diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index 9b7ba5ad1d..4c2c06afa6 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -25,7 +25,9 @@ #include <inttypes.h> #include <sys/types.h> -#include <sd-id128.h> +#include "sd-id128.h" +#include "sd-event.h" +#include "sd-memfd.h" #ifdef __cplusplus extern "C" { @@ -64,7 +66,6 @@ typedef int (*sd_bus_node_enumerator_t) (sd_bus *bus, const char *path, char *** #include "sd-bus-protocol.h" #include "sd-bus-vtable.h" -#include "sd-memfd.h" /* Connections */ @@ -109,6 +110,9 @@ int sd_bus_process(sd_bus *bus, sd_bus_message **r); int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec); int sd_bus_flush(sd_bus *bus); +int sd_bus_attach_event(sd_bus *bus, sd_event *e, int priority); +int sd_bus_detach_event(sd_bus *bus); + int sd_bus_add_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata); int sd_bus_remove_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata); @@ -139,6 +143,8 @@ int sd_bus_message_new_method_call(sd_bus *bus, const char *destination, const c int sd_bus_message_new_method_return(sd_bus *bus, sd_bus_message *call, sd_bus_message **m); int sd_bus_message_new_method_error(sd_bus *bus, sd_bus_message *call, const sd_bus_error *e, sd_bus_message **m); int sd_bus_message_new_method_errorf(sd_bus *bus, sd_bus_message *call, sd_bus_message **m, const char *name, const char *format, ...) _sd_printf_attr_(5, 0); +int sd_bus_message_new_method_errno(sd_bus *bus, sd_bus_message *call, int error, const sd_bus_error *e, sd_bus_message **m); +int sd_bus_message_new_method_errnof(sd_bus *bus, sd_bus_message *call, sd_bus_message **m, int error, const char *format, ...) _sd_printf_attr_(5, 0); sd_bus_message* sd_bus_message_ref(sd_bus_message *m); sd_bus_message* sd_bus_message_unref(sd_bus_message *m); @@ -147,14 +153,15 @@ int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type); int sd_bus_message_get_serial(sd_bus_message *m, uint64_t *serial); int sd_bus_message_get_reply_serial(sd_bus_message *m, uint64_t *serial); int sd_bus_message_get_no_reply(sd_bus_message *m); -int sd_bus_message_get_signature(sd_bus_message *m, int complete, const char **signature); +const char *sd_bus_message_get_signature(sd_bus_message *m, int complete); const char *sd_bus_message_get_path(sd_bus_message *m); const char *sd_bus_message_get_interface(sd_bus_message *m); const char *sd_bus_message_get_member(sd_bus_message *m); const char *sd_bus_message_get_destination(sd_bus_message *m); const char *sd_bus_message_get_sender(sd_bus_message *m); const sd_bus_error *sd_bus_message_get_error(sd_bus_message *m); +int sd_bus_message_get_errno(sd_bus_message *m); int sd_bus_message_get_monotonic_timestamp(sd_bus_message *m, uint64_t *usec); int sd_bus_message_get_realtime_timestamp(sd_bus_message *m, uint64_t *usec); @@ -211,6 +218,8 @@ int sd_bus_set_property(sd_bus *bus, const char *destination, const char *path, int sd_bus_reply_method_return(sd_bus *bus, sd_bus_message *call, const char *types, ...); int sd_bus_reply_method_error(sd_bus *bus, sd_bus_message *call, const sd_bus_error *e); int sd_bus_reply_method_errorf(sd_bus *bus, sd_bus_message *call, const char *name, const char *format, ...) _sd_printf_attr_(4, 0); +int sd_bus_reply_method_errno(sd_bus *bus, sd_bus_message *call, int error, const sd_bus_error *e); +int sd_bus_reply_method_errnof(sd_bus *bus, sd_bus_message *call, int error, const char *format, ...) _sd_printf_attr_(4, 0); int sd_bus_emit_signal(sd_bus *bus, const char *path, const char *interface, const char *member, const char *types, ...); @@ -239,16 +248,19 @@ int sd_bus_get_owner_machine_id(sd_bus *bus, const char *name, sd_id128_t *machi #define SD_BUS_ERROR_NULL SD_BUS_ERROR_MAKE(NULL, NULL) void sd_bus_error_free(sd_bus_error *e); -int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) _sd_printf_attr_(3, 0); int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message); -void sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message); +int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) _sd_printf_attr_(3, 0); +int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message); +int sd_bus_error_set_errno(sd_bus_error *e, int error); +int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) _sd_printf_attr_(3, 0); +int sd_bus_error_get_errno(const sd_bus_error *e); int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e); int sd_bus_error_is_set(const sd_bus_error *e); int sd_bus_error_has_name(const sd_bus_error *e, const char *name); -#define SD_BUS_APPEND_ID128(x) 16, \ - (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], \ - (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], \ +#define SD_BUS_MESSAGE_APPEND_ID128(x) 16, \ + (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], \ + (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], \ (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], \ (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15] diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h index d160520ffc..6ac2de88fa 100644 --- a/src/systemd/sd-event.h +++ b/src/systemd/sd-event.h @@ -81,7 +81,6 @@ int sd_event_get_quit(sd_event *e); int sd_event_request_quit(sd_event *e); int sd_event_get_now_realtime(sd_event *e, uint64_t *usec); int sd_event_get_now_monotonic(sd_event *e, uint64_t *usec); - sd_event *sd_event_get(sd_event_source *s); sd_event_source* sd_event_source_ref(sd_event_source *s); diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index 525c72e497..7b22b20719 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -19,90 +19,52 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <dbus/dbus.h> - #include <errno.h> #include <string.h> #include <unistd.h> -#include "systemd/sd-id128.h" -#include "systemd/sd-messages.h" +#include "sd-id128.h" +#include "sd-messages.h" +#include "sd-event.h" +#include "sd-bus.h" + #include "util.h" #include "strv.h" -#include "dbus-common.h" -#include "polkit.h" #include "def.h" #include "hwclock.h" #include "conf-files.h" #include "path-util.h" #include "fileio-label.h" #include "label.h" +#include "bus-util.h" +#include "event-util.h" #define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n" #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n" -#define INTERFACE \ - " <interface name=\"org.freedesktop.timedate1\">\n" \ - " <property name=\"Timezone\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"LocalRTC\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"CanNTP\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"NTP\" type=\"b\" access=\"read\"/>\n" \ - " <method name=\"SetTime\">\n" \ - " <arg name=\"usec_utc\" type=\"x\" direction=\"in\"/>\n" \ - " <arg name=\"relative\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \ - " </method>\n" \ - " <method name=\"SetTimezone\">\n" \ - " <arg name=\"timezone\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \ - " </method>\n" \ - " <method name=\"SetLocalRTC\">\n" \ - " <arg name=\"local_rtc\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"fix_system\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \ - " </method>\n" \ - " <method name=\"SetNTP\">\n" \ - " <arg name=\"use_ntp\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \ - " </method>\n" \ - " </interface>\n" - -#define INTROSPECTION \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "<node>\n" \ - INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE \ - BUS_PEER_INTERFACE \ - "</node>\n" - -#define INTERFACES_LIST \ - BUS_GENERIC_INTERFACES_LIST \ - "org.freedesktop.timedate1\0" - -const char timedate_interface[] _introspect_("timedate1") = INTERFACE; - -typedef struct TZ { +typedef struct Context { char *zone; bool local_rtc; int can_ntp; int use_ntp; -} TZ; + Hashmap *polkit_registry; +} Context; -static TZ tz = { - .zone = NULL, - .local_rtc = false, - .can_ntp = -1, - .use_ntp = -1, -}; +static void context_reset(Context *c) { + assert(c); -static usec_t remain_until; + free(c->zone); + c->zone = NULL; -static void free_data(void) { - free(tz.zone); - tz.zone = NULL; + c->local_rtc = false; + c->can_ntp = c->use_ntp = -1; +} + +static void context_free(Context *c, sd_bus *bus) { + assert(c); - tz.local_rtc = false; + free(c->zone); + bus_verify_polkit_async_registry_free(bus, c->polkit_registry); } static bool valid_timezone(const char *name) { @@ -153,11 +115,13 @@ static bool valid_timezone(const char *name) { return true; } -static int read_data(void) { - int r; +static int context_read_data(Context *c) { _cleanup_free_ char *t = NULL; + int r; + + assert(c); - free_data(); + context_reset(c); r = readlink_malloc("/etc/localtime", &t); if (r < 0) { @@ -175,8 +139,8 @@ static int read_data(void) { if (!e) log_warning("/etc/localtime should be a symbolic link to a timezone data file in /usr/share/zoneinfo/."); else { - tz.zone = strdup(e); - if (!tz.zone) + c->zone = strdup(e); + if (!c->zone) return log_oom(); goto have_timezone; @@ -184,28 +148,30 @@ static int read_data(void) { } have_timezone: - if (isempty(tz.zone)) { - free(tz.zone); - tz.zone = NULL; + if (isempty(c->zone)) { + free(c->zone); + c->zone = NULL; } - tz.local_rtc = hwclock_is_localtime() > 0; + c->local_rtc = hwclock_is_localtime() > 0; return 0; } -static int write_data_timezone(void) { - int r = 0; +static int context_write_data_timezone(Context *c) { _cleanup_free_ char *p = NULL; + int r = 0; + + assert(c); - if (!tz.zone) { + if (isempty(c->zone)) { if (unlink("/etc/localtime") < 0 && errno != ENOENT) r = -errno; return r; } - p = strappend("../usr/share/zoneinfo/", tz.zone); + p = strappend("../usr/share/zoneinfo/", c->zone); if (!p) return log_oom(); @@ -216,16 +182,18 @@ static int write_data_timezone(void) { return 0; } -static int write_data_local_rtc(void) { +static int context_write_data_local_rtc(Context *c) { int r; _cleanup_free_ char *s = NULL, *w = NULL; + assert(c); + r = read_full_file("/etc/adjtime", &s, NULL); if (r < 0) { if (r != -ENOENT) return r; - if (!tz.local_rtc) + if (!c->local_rtc) return 0; w = strdup(NULL_ADJTIME_LOCAL); @@ -251,11 +219,11 @@ static int write_data_local_rtc(void) { a = p - s; b = strlen(e); - w = new(char, a + (tz.local_rtc ? 5 : 3) + b + 1); + w = new(char, a + (c->local_rtc ? 5 : 3) + b + 1); if (!w) return -ENOMEM; - *(char*) mempcpy(stpcpy(mempcpy(w, s, a), tz.local_rtc ? "LOCAL" : "UTC"), e, b) = 0; + *(char*) mempcpy(stpcpy(mempcpy(w, s, a), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0; if (streq(w, NULL_ADJTIME_UTC)) { if (unlink("/etc/adjtime") < 0) @@ -265,12 +233,13 @@ static int write_data_local_rtc(void) { return 0; } } + label_init("/etc"); return write_string_file_atomic_label("/etc/adjtime", w); } static char** get_ntp_services(void) { - _cleanup_strv_free_ char **r = NULL, **files; + _cleanup_strv_free_ char **r = NULL, **files = NULL; char **i; int k; @@ -318,709 +287,533 @@ static char** get_ntp_services(void) { return strv_uniq(i); } -static int read_ntp(DBusConnection *bus) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; +static int context_read_ntp(Context *c, sd_bus *bus) { + _cleanup_strv_free_ char **l; + char **i; int r; - char **i, **l; + assert(c); assert(bus); - dbus_error_init(&error); - l = get_ntp_services(); STRV_FOREACH(i, l) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus_message *reply = NULL; const char *s; - if (m) - dbus_message_unref(m); - m = dbus_message_new_method_call( + r = sd_bus_call_method( + bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "GetUnitFileState"); - if (!m) { - r = log_oom(); - goto finish; - } + "GetUnitFileState", + &error, + &reply, + "s", + *i); - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, i, - DBUS_TYPE_INVALID)) { - r = log_oom(); - goto finish; - } - - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - if (streq(error.name, "org.freedesktop.DBus.Error.FileNotFound")) { - /* This implementation does not exist, try next one */ - dbus_error_free(&error); + if (r < 0) { + /* This implementation does not exist, try next one */ + if (sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND)) continue; - } - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; + return r; } - if (!dbus_message_get_args(reply, &error, - DBUS_TYPE_STRING, &s, - DBUS_TYPE_INVALID)) { - log_error("Failed to parse reply: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } + r = sd_bus_message_read(reply, "s", &s); + if (r < 0) + return r; - tz.can_ntp = 1; - tz.use_ntp = + c->can_ntp = 1; + c->use_ntp = streq(s, "enabled") || streq(s, "enabled-runtime"); - r = 0; - goto finish; + + return 0; } /* NTP is not installed. */ - tz.can_ntp = 0; - tz.use_ntp = 0; - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); + c->can_ntp = 0; + c->use_ntp = 0; - strv_free(l); - - dbus_error_free(&error); - - return r; + return 0; } -static int start_ntp(DBusConnection *bus, DBusError *error) { - DBusMessage *m = NULL, *reply = NULL; - const char *mode = "replace"; - char **i, **l; +static int context_start_ntp(Context *c, sd_bus *bus, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + char **i; int r; + assert(c); assert(bus); assert(error); l = get_ntp_services(); STRV_FOREACH(i, l) { - if (m) - dbus_message_unref(m); - m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - tz.use_ntp ? "StartUnit" : "StopUnit"); - if (!m) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, i, - DBUS_TYPE_STRING, &mode, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } + if (c->use_ntp) + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + error, + NULL, + "ss", *i, "replace"); + else + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StopUnit", + error, + NULL, + "ss", *i, "replace"); - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); - if (!reply) { - if (streq(error->name, "org.freedesktop.DBus.Error.FileNotFound") || - streq(error->name, "org.freedesktop.systemd1.LoadFailed") || - streq(error->name, "org.freedesktop.systemd1.NoSuchUnit")) { + if (r < 0) { + if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND) || + sd_bus_error_has_name(error, "org.freedesktop.systemd1.LoadFailed") || + sd_bus_error_has_name(error, "org.freedesktop.systemd1.NoSuchUnit")) { /* This implementation does not exist, try next one */ - dbus_error_free(error); + sd_bus_error_free(error); continue; } - log_error("Failed to issue method call: %s", bus_error_message(error)); - r = -EIO; - goto finish; + return r; } - r = 0; - goto finish; + return 1; } - /* No implementaiton available... */ - r = -ENOENT; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - strv_free(l); - - return r; + sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported."); + return -ENOTSUP; } -static int enable_ntp(DBusConnection *bus, DBusError *error) { - DBusMessage *m = NULL, *reply = NULL; +static int context_enable_ntp(Context*c, sd_bus *bus, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + char **i; int r; - DBusMessageIter iter; - dbus_bool_t f = FALSE, t = TRUE; - char **i, **l; + assert(c); assert(bus); assert(error); l = get_ntp_services(); STRV_FOREACH(i, l) { - char* k[2]; - - if (m) - dbus_message_unref(m); - m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - tz.use_ntp ? "EnableUnitFiles" : "DisableUnitFiles"); - if (!m) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - dbus_message_iter_init_append(m, &iter); - - k[0] = *i; - k[1] = NULL; + if (c->use_ntp) + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "EnableUnitFiles", + error, + NULL, + "asbb", 1, *i, false, true); + else + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "DisableUnitFiles", + error, + NULL, + "asb", 1, *i, false); - r = bus_append_strv_iter(&iter, k); if (r < 0) { - log_error("Failed to append unit files."); - goto finish; - } - - /* send runtime bool */ - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &f)) { - log_error("Failed to append runtime boolean."); - r = -ENOMEM; - goto finish; - } - - if (tz.use_ntp) { - /* send force bool */ - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &t)) { - log_error("Failed to append force boolean."); - r = -ENOMEM; - goto finish; - } - } - - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); - if (!reply) { - if (streq(error->name, "org.freedesktop.DBus.Error.FileNotFound")) { + if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND)) { /* This implementation does not exist, try next one */ - dbus_error_free(error); + sd_bus_error_free(error); continue; } - log_error("Failed to issue method call: %s", bus_error_message(error)); - r = -EIO; - goto finish; + return r; } - dbus_message_unref(m); - m = dbus_message_new_method_call( + r = sd_bus_call_method( + bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "Reload"); - if (!m) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - dbus_message_unref(reply); - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); - if (!reply) { - log_error("Failed to issue method call: %s", bus_error_message(error)); - r = -EIO; - goto finish; - } + "Reload", + error, + NULL, + NULL); + if (r < 0) + return r; - r = 0; - goto finish; + return 1; } - r = -ENOENT; + sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported."); + return -ENOTSUP; +} -finish: - if (m) - dbus_message_unref(m); +static int method_set_timezone(sd_bus *bus, sd_bus_message *m, void *userdata) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + Context *c = userdata; + const char *z; + bool interactive; + char *t; + int r; - if (reply) - dbus_message_unref(reply); + r = sd_bus_message_read(m, "sb", &z, &interactive); + if (r < 0) + return sd_bus_reply_method_errno(bus, m, r, NULL); - strv_free(l); + if (!valid_timezone(z)) + return sd_bus_reply_method_errorf(bus, m, SD_BUS_ERROR_INVALID_ARGS, "Invalid time zone '%s'", z); - return r; -} + if (streq_ptr(z, c->zone)) + return sd_bus_reply_method_return(bus, m, NULL); -static int property_append_can_ntp(DBusMessageIter *i, const char *property, void *data) { - dbus_bool_t db; + r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-timezone", interactive, &error, method_set_timezone, c); + if (r < 0) + return sd_bus_reply_method_errno(bus, m, r, &error); + if (r == 0) + return 1; - assert(i); - assert(property); + t = strdup(z); + if (!t) + return log_oom(); - db = tz.can_ntp > 0; + free(c->zone); + c->zone = t; - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db)) - return -ENOMEM; + /* 1. Write new configuration file */ + r = context_write_data_timezone(c); + if (r < 0) { + log_error("Failed to set timezone: %s", strerror(-r)); + return sd_bus_reply_method_errnof(bus, m, r, "Failed to set timezone: %s", strerror(-r)); + } - return 0; -} + /* 2. Tell the kernel our timezone */ + hwclock_set_timezone(NULL); -static int property_append_ntp(DBusMessageIter *i, const char *property, void *data) { - dbus_bool_t db; + if (c->local_rtc) { + struct timespec ts; + struct tm *tm; - assert(i); - assert(property); + /* 3. Sync RTC from system clock, with the new delta */ + assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); + assert_se(tm = localtime(&ts.tv_sec)); + hwclock_set_time(tm); + } - db = tz.use_ntp > 0; + log_struct(LOG_INFO, + MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE), + "TIMEZONE=%s", c->zone, + "MESSAGE=Changed timezone to '%s'.", c->zone, + NULL); - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db)) - return -ENOMEM; + sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone", NULL); - return 0; + return sd_bus_reply_method_return(bus, m, NULL); } -static const BusProperty bus_timedate_properties[] = { - { "Timezone", bus_property_append_string, "s", offsetof(TZ, zone), true }, - { "LocalRTC", bus_property_append_bool, "b", offsetof(TZ, local_rtc) }, - { "CanNTP", property_append_can_ntp, "b", offsetof(TZ, can_ntp) }, - { "NTP", property_append_ntp, "b", offsetof(TZ, use_ntp) }, - { NULL, } -}; - -static const BusBoundProperties bps[] = { - { "org.freedesktop.timedate1", bus_timedate_properties, &tz }, - { NULL, } -}; - -static DBusHandlerResult timedate_message_handler( - DBusConnection *connection, - DBusMessage *message, - void *userdata) { - - DBusMessage *reply = NULL, *changed = NULL; - DBusError error; +static int method_set_local_rtc(sd_bus *bus, sd_bus_message *m, void *userdata) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + bool lrtc, fix_system, interactive; + Context *c = userdata; + struct timespec ts; int r; - assert(connection); - assert(message); - - dbus_error_init(&error); + assert(bus); + assert(m); + assert(c); - if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTimezone")) { - const char *z; - dbus_bool_t interactive; + r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive); + if (r < 0) + return sd_bus_reply_method_errno(bus, m, r, NULL); - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &z, - DBUS_TYPE_BOOLEAN, &interactive, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); + if (lrtc == c->local_rtc) + return sd_bus_reply_method_return(bus, m, NULL); - if (!valid_timezone(z)) - return bus_send_error_reply(connection, message, NULL, -EINVAL); + r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-local-rtc", interactive, &error, method_set_local_rtc, c); + if (r < 0) + return sd_bus_reply_method_errno(bus, m, r, &error); + if (r == 0) + return 1; - if (!streq_ptr(z, tz.zone)) { - char *t; + c->local_rtc = lrtc; - r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-timezone", interactive, NULL, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); + /* 1. Write new configuration file */ + r = context_write_data_local_rtc(c); + if (r < 0) { + log_error("Failed to set RTC to local/UTC: %s", strerror(-r)); + return sd_bus_reply_method_errnof(bus, m, r, "Failed to set RTC to local/UTC: %s", strerror(-r)); + } - t = strdup(z); - if (!t) - goto oom; + /* 2. Tell the kernel our timezone */ + hwclock_set_timezone(NULL); - free(tz.zone); - tz.zone = t; + /* 3. Synchronize clocks */ + assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); - /* 1. Write new configuration file */ - r = write_data_timezone(); - if (r < 0) { - log_error("Failed to set timezone: %s", strerror(-r)); - return bus_send_error_reply(connection, message, NULL, r); - } + if (fix_system) { + struct tm tm; - /* 2. Tell the kernel our timezone */ - hwclock_set_timezone(NULL); + /* Sync system clock from RTC; first, + * initialize the timezone fields of + * struct tm. */ + if (c->local_rtc) + tm = *localtime(&ts.tv_sec); + else + tm = *gmtime(&ts.tv_sec); - if (tz.local_rtc) { - struct timespec ts; - struct tm *tm; + /* Override the main fields of + * struct tm, but not the timezone + * fields */ + if (hwclock_get_time(&tm) >= 0) { - /* 3. Sync RTC from system clock, with the new delta */ - assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); - assert_se(tm = localtime(&ts.tv_sec)); - hwclock_set_time(tm); - } + /* And set the system clock + * with this */ + if (c->local_rtc) + ts.tv_sec = mktime(&tm); + else + ts.tv_sec = timegm(&tm); - log_struct(LOG_INFO, - MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE), - "TIMEZONE=%s", tz.zone, - "MESSAGE=Changed timezone to '%s'.", tz.zone, - NULL); - - changed = bus_properties_changed_new( - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "Timezone\0"); - if (!changed) - goto oom; + clock_settime(CLOCK_REALTIME, &ts); } - } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetLocalRTC")) { - dbus_bool_t lrtc; - dbus_bool_t fix_system; - dbus_bool_t interactive; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_BOOLEAN, &lrtc, - DBUS_TYPE_BOOLEAN, &fix_system, - DBUS_TYPE_BOOLEAN, &interactive, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - if (lrtc != tz.local_rtc) { - struct timespec ts; - - r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, NULL, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - tz.local_rtc = lrtc; - - /* 1. Write new configuration file */ - r = write_data_local_rtc(); - if (r < 0) { - log_error("Failed to set RTC to local/UTC: %s", strerror(-r)); - return bus_send_error_reply(connection, message, NULL, r); - } + } else { + struct tm *tm; - /* 2. Tell the kernel our timezone */ - hwclock_set_timezone(NULL); + /* Sync RTC from system clock */ + if (c->local_rtc) + tm = localtime(&ts.tv_sec); + else + tm = gmtime(&ts.tv_sec); - /* 3. Synchronize clocks */ - assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); + hwclock_set_time(tm); + } - if (fix_system) { - struct tm tm; + log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC"); - /* Sync system clock from RTC; first, - * initialize the timezone fields of - * struct tm. */ - if (tz.local_rtc) - tm = *localtime(&ts.tv_sec); - else - tm = *gmtime(&ts.tv_sec); + sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC", NULL); - /* Override the main fields of - * struct tm, but not the timezone - * fields */ - if (hwclock_get_time(&tm) >= 0) { + return sd_bus_reply_method_return(bus, m, NULL); +} - /* And set the system clock - * with this */ - if (tz.local_rtc) - ts.tv_sec = mktime(&tm); - else - ts.tv_sec = timegm(&tm); +static int method_set_time(sd_bus *bus, sd_bus_message *m, void *userdata) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + bool relative, interactive; + Context *c = userdata; + int64_t utc; + struct timespec ts; + struct tm* tm; + int r; - clock_settime(CLOCK_REALTIME, &ts); - } + assert(bus); + assert(m); + assert(c); - } else { - struct tm *tm; + r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive); + if (r < 0) + return sd_bus_reply_method_errno(bus, m, r, NULL); - /* Sync RTC from system clock */ - if (tz.local_rtc) - tm = localtime(&ts.tv_sec); - else - tm = gmtime(&ts.tv_sec); + if (!relative && utc <= 0) + return sd_bus_reply_method_errorf(bus, m, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time"); - hwclock_set_time(tm); - } + if (relative && utc == 0) + return sd_bus_reply_method_return(bus, m, NULL); - log_info("RTC configured to %s time.", tz.local_rtc ? "local" : "UTC"); + if (relative) { + usec_t n, x; - changed = bus_properties_changed_new( - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "LocalRTC\0"); - if (!changed) - goto oom; - } + n = now(CLOCK_REALTIME); + x = n + utc; - } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTime")) { - int64_t utc; - dbus_bool_t relative; - dbus_bool_t interactive; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_INT64, &utc, - DBUS_TYPE_BOOLEAN, &relative, - DBUS_TYPE_BOOLEAN, &interactive, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - if (!relative && utc <= 0) - return bus_send_error_reply(connection, message, NULL, -EINVAL); - - if (!relative || utc != 0) { - struct timespec ts; - struct tm* tm; - - if (relative) { - usec_t n, x; - - n = now(CLOCK_REALTIME); - x = n + utc; - - if ((utc > 0 && x < n) || - (utc < 0 && x > n)) - return bus_send_error_reply(connection, message, NULL, -EOVERFLOW); - - timespec_store(&ts, x); - } else - timespec_store(&ts, (usec_t) utc); - - r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, NULL, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - /* Set system clock */ - if (clock_settime(CLOCK_REALTIME, &ts) < 0) { - log_error("Failed to set local time: %m"); - return bus_send_error_reply(connection, message, NULL, -errno); - } + if ((utc > 0 && x < n) || + (utc < 0 && x > n)) + return sd_bus_reply_method_errorf(bus, m, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow"); - /* Sync down to RTC */ - if (tz.local_rtc) - tm = localtime(&ts.tv_sec); - else - tm = gmtime(&ts.tv_sec); + timespec_store(&ts, x); + } else + timespec_store(&ts, (usec_t) utc); - hwclock_set_time(tm); + r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-time", interactive, &error, method_set_time, c); + if (r < 0) + return sd_bus_reply_method_errno(bus, m, r, &error); + if (r == 0) + return 1; + + /* Set system clock */ + if (clock_settime(CLOCK_REALTIME, &ts) < 0) { + log_error("Failed to set local time: %m"); + return sd_bus_reply_method_errnof(bus, m, errno, "Failed to set local time: %m"); + } - log_struct(LOG_INFO, - MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), - "REALTIME=%llu", (unsigned long long) timespec_load(&ts), - "MESSAGE=Changed local time to %s", ctime(&ts.tv_sec), - NULL); - } - } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetNTP")) { - dbus_bool_t ntp; - dbus_bool_t interactive; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_BOOLEAN, &ntp, - DBUS_TYPE_BOOLEAN, &interactive, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - if (ntp != !!tz.use_ntp) { - - r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-ntp", interactive, NULL, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - tz.use_ntp = !!ntp; - - r = enable_ntp(connection, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - r = start_ntp(connection, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - log_info("Set NTP to %s", tz.use_ntp ? "enabled" : "disabled"); - - changed = bus_properties_changed_new( - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "NTP\0"); - if (!changed) - goto oom; - } + /* Sync down to RTC */ + if (c->local_rtc) + tm = localtime(&ts.tv_sec); + else + tm = gmtime(&ts.tv_sec); - } else - return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); + hwclock_set_time(tm); - if (!(reply = dbus_message_new_method_return(message))) - goto oom; + log_struct(LOG_INFO, + MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), + "REALTIME=%llu", (unsigned long long) timespec_load(&ts), + "MESSAGE=Changed local time to %s", ctime(&ts.tv_sec), + NULL); - if (!bus_maybe_send_reply(connection, message, reply)) - goto oom; + return sd_bus_reply_method_return(bus, m, NULL); +} - dbus_message_unref(reply); - reply = NULL; +static int method_set_ntp(sd_bus *bus, sd_bus_message *m, void *userdata) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + bool ntp, interactive; + Context *c = userdata; + int r; - if (changed) { + r = sd_bus_message_read(m, "bb", &ntp, &interactive); + if (r < 0) + return sd_bus_reply_method_errno(bus, m, r, NULL); - if (!dbus_connection_send(connection, changed, NULL)) - goto oom; + if (ntp == c->use_ntp) + return sd_bus_reply_method_return(bus, m, NULL); - dbus_message_unref(changed); - } + r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-ntp", interactive, &error, method_set_ntp, c); + if (r < 0) + return sd_bus_reply_method_errno(bus, m, r, &error); + if (r == 0) + return 1; - return DBUS_HANDLER_RESULT_HANDLED; + c->use_ntp = ntp; -oom: - if (reply) - dbus_message_unref(reply); + r = context_enable_ntp(c, bus, &error); + if (r < 0) + return sd_bus_reply_method_errno(bus, m, r, &error); + + r = context_start_ntp(c, bus, &error); + if (r < 0) + return sd_bus_reply_method_errno(bus, m, r, &error); - if (changed) - dbus_message_unref(changed); + log_info("Set NTP to %s", c->use_ntp ? "enabled" : "disabled"); - dbus_error_free(&error); + sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL); - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return sd_bus_reply_method_return(bus, m, NULL); } -static int connect_bus(DBusConnection **_bus) { - static const DBusObjectPathVTable timedate_vtable = { - .message_function = timedate_message_handler - }; - DBusError error; - DBusConnection *bus = NULL; +static const sd_bus_vtable timedate_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("LocalRTC", "b", NULL, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("CanNTP", "b", bus_property_get_tristate, offsetof(Context, can_ntp), 0), + SD_BUS_PROPERTY("NTP", "b", bus_property_get_tristate, offsetof(Context, use_ntp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, 0), + SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, 0), + SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, 0), + SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, 0), + SD_BUS_VTABLE_END, +}; + +static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { + _cleanup_bus_unref_ sd_bus *bus = NULL; int r; + assert(c); + assert(event); assert(_bus); - dbus_error_init(&error); - - bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); - if (!bus) { - log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error)); - r = -ECONNREFUSED; - goto fail2; + r = sd_bus_open_system(&bus); + if (r < 0) { + log_error("Failed to get system bus connection: %s", strerror(-r)); + return r; } - dbus_connection_set_exit_on_disconnect(bus, FALSE); - - if (!dbus_connection_register_object_path(bus, "/org/freedesktop/timedate1", &timedate_vtable, NULL) || - !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) { - r = log_oom(); - goto fail; + r = sd_bus_add_object_vtable(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c); + if (r < 0) { + log_error("Failed to register object: %s", strerror(-r)); + return r; } - r = dbus_bus_request_name(bus, "org.freedesktop.timedate1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error); - if (dbus_error_is_set(&error)) { - log_error("Failed to register name on bus: %s", bus_error_message(&error)); - r = -EEXIST; - goto fail; + r = sd_bus_request_name(bus, "org.freedesktop.timedate1", SD_BUS_NAME_DO_NOT_QUEUE); + if (r < 0) { + log_error("Failed to register name: %s", strerror(-r)); + return r; } - if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + if (r != SD_BUS_NAME_PRIMARY_OWNER) { log_error("Failed to acquire name."); - r = -EEXIST; - goto fail; + return -EEXIST; } - if (_bus) - *_bus = bus; - - return 0; + r = sd_bus_attach_event(bus, event, 0); + if (r < 0) { + log_error("Failed to attach bus to event loop: %s", strerror(-r)); + return r; + } -fail: - dbus_connection_close(bus); - dbus_connection_unref(bus); -fail2: - dbus_error_free(&error); + *_bus = bus; + bus = NULL; - return r; + return 0; } int main(int argc, char *argv[]) { + Context context = { + .zone = NULL, + .local_rtc = false, + .can_ntp = -1, + .use_ntp = -1, + }; + + _cleanup_event_unref_ sd_event *event = NULL; + _cleanup_bus_unref_ sd_bus *bus = NULL; int r; - DBusConnection *bus = NULL; - bool exiting = false; log_set_target(LOG_TARGET_AUTO); + log_set_max_level(LOG_DEBUG); log_parse_environment(); log_open(); umask(0022); - if (argc == 2 && streq(argv[1], "--introspect")) { - fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "<node>\n", stdout); - fputs(timedate_interface, stdout); - fputs("</node>\n", stdout); - return 0; - } - if (argc != 1) { log_error("This program takes no arguments."); r = -EINVAL; goto finish; } - r = read_data(); + r = sd_event_new(&event); if (r < 0) { - log_error("Failed to read timezone data: %s", strerror(-r)); + log_error("Failed to allocate event loop: %s", strerror(-r)); goto finish; } - r = connect_bus(&bus); + r = connect_bus(&context, event, &bus); if (r < 0) goto finish; - r = read_ntp(bus); + r = context_read_data(&context); if (r < 0) { - log_error("Failed to determine whether NTP is enabled: %s", strerror(-r)); + log_error("Failed to read timezone data: %s", strerror(-r)); goto finish; } - remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC; - for (;;) { - - if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))) - break; + r = context_read_ntp(&context, bus); + if (r < 0) { + log_error("Failed to determine whether NTP is enabled: %s", strerror(-r)); + goto finish; + } - if (!exiting && remain_until < now(CLOCK_MONOTONIC)) { - exiting = true; - bus_async_unregister_and_exit(bus, "org.freedesktop.timedated1"); - } + r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC); + if (r < 0) { + log_error("Failed to run event loop: %s", strerror(-r)); + goto finish; } + sd_bus_flush(bus); r = 0; finish: - free_data(); - - if (bus) { - dbus_connection_flush(bus); - dbus_connection_close(bus); - dbus_connection_unref(bus); - } + context_free(&context, bus); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } |