#include "path-util.h"
#include "missing.h"
#include "set.h"
+#include "signal-util.h"
#include "unit-name.h"
#include "sd-bus.h"
#include "bus-error.h"
+#include "bus-label.h"
#include "bus-message.h"
#include "bus-util.h"
#include "bus-internal.h"
-static int name_owner_change_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
sd_event *e = userdata;
- assert(bus);
assert(m);
assert(e);
- sd_bus_close(bus);
+ sd_bus_close(sd_bus_message_get_bus(m));
sd_event_exit(e, 0);
return 1;
if (r < 0)
return r;
+ /* Don't trust augmented credentials for authorization */
+ assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM);
+
r = sd_bus_creds_get_euid(creds, &sender_uid);
if (r < 0)
return r;
free(q);
}
-static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
_cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
AsyncPolkitQuery *q = userdata;
int r;
- assert(bus);
assert(reply);
assert(q);
goto finish;
}
- r = q->callback(bus, q->request, q->userdata, &error_buffer);
+ r = q->callback(q->request, q->userdata, &error_buffer);
r = bus_maybe_reply_error(q->request, r, &error_buffer);
finish:
return r;
}
-int bus_message_map_all_properties(sd_bus *bus,
- sd_bus_message *m,
- const struct bus_properties_map *map,
- void *userdata) {
+int bus_message_map_all_properties(
+ sd_bus_message *m,
+ const struct bus_properties_map *map,
+ void *userdata) {
+
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
- assert(bus);
assert(m);
assert(map);
v = (uint8_t *)userdata + prop->offset;
if (map[i].set)
- r = prop->set(bus, member, m, &error, v);
+ r = prop->set(sd_bus_message_get_bus(m), member, m, &error, v);
else
- r = map_basic(bus, member, m, &error, v);
+ r = map_basic(sd_bus_message_get_bus(m), member, m, &error, v);
if (r < 0)
return r;
if (r < 0)
return r;
}
+ if (r < 0)
+ return r;
return sd_bus_message_exit_container(m);
}
-int bus_message_map_properties_changed(sd_bus *bus,
- sd_bus_message *m,
- const struct bus_properties_map *map,
- void *userdata) {
+int bus_message_map_properties_changed(
+ sd_bus_message *m,
+ const struct bus_properties_map *map,
+ void *userdata) {
+
const char *member;
int r, invalidated, i;
- assert(bus);
assert(m);
assert(map);
- r = bus_message_map_all_properties(bus, m, map, userdata);
+ r = bus_message_map_all_properties(m, map, userdata);
if (r < 0)
return r;
++invalidated;
break;
}
+ if (r < 0)
+ return r;
r = sd_bus_message_exit_container(m);
if (r < 0)
return invalidated;
}
-int bus_map_all_properties(sd_bus *bus,
- const char *destination,
- const char *path,
- const struct bus_properties_map *map,
- void *userdata) {
+int bus_map_all_properties(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const struct bus_properties_map *map,
+ void *userdata) {
+
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
if (r < 0)
return r;
- return bus_message_map_all_properties(bus, m, map, userdata);
+ return bus_message_map_all_properties(m, map, userdata);
}
int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
sd_bus_slot *slot_disconnected;
} BusWaitForJobs;
-static int match_disconnected(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
- assert(bus);
+static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
assert(m);
log_error("Warning! D-Bus connection terminated.");
- sd_bus_close(bus);
+ sd_bus_close(sd_bus_message_get_bus(m));
return 0;
}
-static int match_job_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
const char *path, *unit, *result;
BusWaitForJobs *d = userdata;
uint32_t id;
char *found;
int r;
- assert(bus);
assert(m);
assert(d);
static const struct {
const char *result, *explanation;
} explanations [] = {
- { "resources", "configured resource limit was exceeded" },
- { "timeout", "timeout was exceeded" },
- { "exit-code", "control process exited with error code" },
- { "signal", "fatal signal was delivered to the control process" },
- { "core-dump", "fatal signal was delivered to the control process. Core dumped" },
- { "watchdog", "service failed to send watchdog ping" },
- { "start-limit", "start of the service was attempted too often too quickly" }
+ { "resources", "a configured resource limit was exceeded" },
+ { "timeout", "a timeout was exceeded" },
+ { "exit-code", "the control process exited with error code" },
+ { "signal", "a fatal signal was delivered to the control process" },
+ { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
+ { "watchdog", "the service failed to send watchdog ping" },
+ { "start-limit", "start of the service was attempted too often" }
};
static void log_job_error_with_service_result(const char* service, const char *result) {
- unsigned i;
_cleanup_free_ char *service_shell_quoted = NULL;
assert(service);
- assert(result);
service_shell_quoted = shell_maybe_quote(service);
- for (i = 0; i < ELEMENTSOF(explanations); ++i)
- if (streq(result, explanations[i].result))
- break;
+ if (!isempty(result)) {
+ unsigned i;
- if (i < ELEMENTSOF(explanations))
- log_error("Job for %s failed because %s. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
- service,
- explanations[i].explanation,
- strna(service_shell_quoted));
- else
- log_error("Job for %s failed. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
- service,
- strna(service_shell_quoted));
+ for (i = 0; i < ELEMENTSOF(explanations); ++i)
+ if (streq(result, explanations[i].result))
+ break;
+
+ if (i < ELEMENTSOF(explanations)) {
+ log_error("Job for %s failed because %s. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
+ service,
+ explanations[i].explanation,
+ strna(service_shell_quoted));
+
+ goto finish;
+ }
+ }
+
+ log_error("Job for %s failed. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
+ service,
+ strna(service_shell_quoted));
+finish:
/* For some results maybe additional explanation is required */
if (streq_ptr(result, "start-limit"))
- log_info("To force a start please invoke \"systemctl reset-failed %s\" followed by \"systemctl start %s\" again.",
- strna(service_shell_quoted),
+ log_info("To force a start use \"systemctl reset-failed %1$s\" followed by \"systemctl start %1$s\" again.",
strna(service_shell_quoted));
}
return set_put_strdup(d->jobs, path);
}
-int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet) {
+int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
const char *type, *path, *source;
int r;
else
log_info("Removed symlink %s.", path);
}
+
+ r = unit_file_changes_add(changes, n_changes, streq(type, "symlink") ? UNIT_FILE_SYMLINK : UNIT_FILE_UNLINK, path, source);
+ if (r < 0)
+ return r;
}
if (r < 0)
return bus_log_parse_error(r);
return 0;
}
+
+/**
+ * bus_path_encode_unique() - encode unique object path
+ * @b: bus connection or NULL
+ * @prefix: object path prefix
+ * @sender_id: unique-name of client, or NULL
+ * @external_id: external ID to be chosen by client, or NULL
+ * @ret_path: storage for encoded object path pointer
+ *
+ * Whenever we provide a bus API that allows clients to create and manage
+ * server-side objects, we need to provide a unique name for these objects. If
+ * we let the server choose the name, we suffer from a race condition: If a
+ * client creates an object asynchronously, it cannot destroy that object until
+ * it received the method reply. It cannot know the name of the new object,
+ * thus, it cannot destroy it. Furthermore, it enforces a round-trip.
+ *
+ * Therefore, many APIs allow the client to choose the unique name for newly
+ * created objects. There're two problems to solve, though:
+ * 1) Object names are usually defined via dbus object paths, which are
+ * usually globally namespaced. Therefore, multiple clients must be able
+ * to choose unique object names without interference.
+ * 2) If multiple libraries share the same bus connection, they must be
+ * able to choose unique object names without interference.
+ * The first problem is solved easily by prefixing a name with the
+ * unique-bus-name of a connection. The server side must enforce this and
+ * reject any other name. The second problem is solved by providing unique
+ * suffixes from within sd-bus.
+ *
+ * This helper allows clients to create unique object-paths. It uses the
+ * template '/prefix/sender_id/external_id' and returns the new path in
+ * @ret_path (must be freed by the caller).
+ * If @sender_id is NULL, the unique-name of @b is used. If @external_id is
+ * NULL, this function allocates a unique suffix via @b (by requesting a new
+ * cookie). If both @sender_id and @external_id are given, @b can be passed as
+ * NULL.
+ *
+ * Returns: 0 on success, negative error code on failure.
+ */
+int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path) {
+ _cleanup_free_ char *sender_label = NULL, *external_label = NULL;
+ char external_buf[DECIMAL_STR_MAX(uint64_t)], *p;
+ int r;
+
+ assert_return(b || (sender_id && external_id), -EINVAL);
+ assert_return(object_path_is_valid(prefix), -EINVAL);
+ assert_return(ret_path, -EINVAL);
+
+ if (!sender_id) {
+ r = sd_bus_get_unique_name(b, &sender_id);
+ if (r < 0)
+ return r;
+ }
+
+ if (!external_id) {
+ xsprintf(external_buf, "%"PRIu64, ++b->cookie);
+ external_id = external_buf;
+ }
+
+ sender_label = bus_label_escape(sender_id);
+ if (!sender_label)
+ return -ENOMEM;
+
+ external_label = bus_label_escape(external_id);
+ if (!external_label)
+ return -ENOMEM;
+
+ p = strjoin(prefix, "/", sender_label, "/", external_label, NULL);
+ if (!p)
+ return -ENOMEM;
+
+ *ret_path = p;
+ return 0;
+}
+
+/**
+ * bus_path_decode_unique() - decode unique object path
+ * @path: object path to decode
+ * @prefix: object path prefix
+ * @ret_sender: output parameter for sender-id label
+ * @ret_external: output parameter for external-id label
+ *
+ * This does the reverse of bus_path_encode_unique() (see its description for
+ * details). Both trailing labels, sender-id and external-id, are unescaped and
+ * returned in the given output parameters (the caller must free them).
+ *
+ * Note that this function returns 0 if the path does not match the template
+ * (see bus_path_encode_unique()), 1 if it matched.
+ *
+ * Returns: Negative error code on failure, 0 if the given object path does not
+ * match the template (return parameters are set to NULL), 1 if it was
+ * parsed successfully (return parameters contain allocated labels).
+ */
+int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external) {
+ const char *p, *q;
+ char *sender, *external;
+
+ assert(object_path_is_valid(path));
+ assert(object_path_is_valid(prefix));
+ assert(ret_sender);
+ assert(ret_external);
+
+ p = object_path_startswith(path, prefix);
+ if (!p) {
+ *ret_sender = NULL;
+ *ret_external = NULL;
+ return 0;
+ }
+
+ q = strchr(p, '/');
+ if (!q) {
+ *ret_sender = NULL;
+ *ret_external = NULL;
+ return 0;
+ }
+
+ sender = bus_label_unescape_n(p, q - p);
+ external = bus_label_unescape(q + 1);
+ if (!sender || !external) {
+ free(sender);
+ free(external);
+ return -ENOMEM;
+ }
+
+ *ret_sender = sender;
+ *ret_external = external;
+ return 1;
+}
+
+bool is_kdbus_wanted(void) {
+ _cleanup_free_ char *value = NULL;
+ int r;
+
+ if (get_proc_cmdline_key("kdbus", NULL) <= 0) {
+ r = get_proc_cmdline_key("kdbus=", &value);
+ if (r <= 0 || parse_boolean(value) != 1)
+ return false;
+ }
+
+ return true;
+}
+
+bool is_kdbus_available(void) {
+ _cleanup_close_ int fd = -1;
+ struct kdbus_cmd cmd = { .size = sizeof(cmd), .flags = KDBUS_FLAG_NEGOTIATE };
+
+ if (!is_kdbus_wanted())
+ return false;
+
+ fd = open("/sys/fs/kdbus/control", O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY);
+ if (fd < 0)
+ return false;
+
+ return ioctl(fd, KDBUS_CMD_BUS_MAKE, &cmd) >= 0;
+}