summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2016-10-11 19:40:50 +0200
committerGitHub <noreply@github.com>2016-10-11 19:40:50 +0200
commitb744e8937ce603090a9bf64ac7d8cc2c1a29e4f0 (patch)
treed6873059fd870c78dae268671ad0e12ac8c493db /src/core
parentMerge pull request #4115 from yuwata/completion-fix (diff)
parentupdate TODO (diff)
downloadsystemd-b744e8937ce603090a9bf64ac7d8cc2c1a29e4f0.tar.xz
systemd-b744e8937ce603090a9bf64ac7d8cc2c1a29e4f0.zip
Merge pull request #4067 from poettering/invocation-id
Add an "invocation ID" concept to the service manager
Diffstat (limited to 'src/core')
-rw-r--r--src/core/automount.c4
-rw-r--r--src/core/busname.c4
-rw-r--r--src/core/cgroup.c21
-rw-r--r--src/core/dbus-manager.c59
-rw-r--r--src/core/dbus-unit.c1
-rw-r--r--src/core/device.c4
-rw-r--r--src/core/execute.c10
-rw-r--r--src/core/manager.c24
-rw-r--r--src/core/manager.h4
-rw-r--r--src/core/mount.c11
-rw-r--r--src/core/org.freedesktop.systemd1.conf4
-rw-r--r--src/core/path.c4
-rw-r--r--src/core/scope.c4
-rw-r--r--src/core/service.c4
-rw-r--r--src/core/slice.c5
-rw-r--r--src/core/socket.c5
-rw-r--r--src/core/swap.c5
-rw-r--r--src/core/target.c5
-rw-r--r--src/core/timer.c6
-rw-r--r--src/core/unit.c88
-rw-r--r--src/core/unit.h10
21 files changed, 273 insertions, 9 deletions
diff --git a/src/core/automount.c b/src/core/automount.c
index bdc0e06965..7d7a0a6e46 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -800,6 +800,10 @@ static int automount_start(Unit *u) {
return r;
}
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
a->result = AUTOMOUNT_SUCCESS;
automount_enter_waiting(a);
return 1;
diff --git a/src/core/busname.c b/src/core/busname.c
index a69e3831f6..b96ec09e67 100644
--- a/src/core/busname.c
+++ b/src/core/busname.c
@@ -639,6 +639,10 @@ static int busname_start(Unit *u) {
return r;
}
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
n->result = BUSNAME_SUCCESS;
busname_enter_making(n);
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 7873f88785..20bdbc39d0 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -1361,6 +1361,26 @@ int unit_attach_pids_to_cgroup(Unit *u) {
return 0;
}
+static void cgroup_xattr_apply(Unit *u) {
+ char ids[SD_ID128_STRING_MAX];
+ int r;
+
+ assert(u);
+
+ if (!MANAGER_IS_SYSTEM(u->manager))
+ return;
+
+ if (sd_id128_is_null(u->invocation_id))
+ return;
+
+ r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
+ "trusted.invocation_id",
+ sd_id128_to_string(u->invocation_id, ids), 32,
+ 0);
+ if (r < 0)
+ log_unit_warning_errno(u, r, "Failed to set invocation ID on control group %s, ignoring: %m", u->cgroup_path);
+}
+
static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask, CGroupMask enable_mask) {
assert(u);
@@ -1404,6 +1424,7 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
/* Finally, apply the necessary attributes. */
cgroup_context_apply(u, target_mask, state);
+ cgroup_xattr_apply(u);
return 0;
}
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index ea7ced2fd0..12eb55cb7f 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -464,6 +464,64 @@ static int method_get_unit_by_pid(sd_bus_message *message, void *userdata, sd_bu
return sd_bus_reply_method_return(message, "o", path);
}
+static int method_get_unit_by_invocation_id(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ char *path = NULL;
+ Manager *m = userdata;
+ sd_id128_t id;
+ const void *a;
+ Unit *u;
+ size_t sz;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ /* Anyone can call this method */
+
+ r = sd_bus_message_read_array(message, 'y', &a, &sz);
+ if (r < 0)
+ return r;
+ if (sz == 0)
+ id = SD_ID128_NULL;
+ else if (sz == 16)
+ memcpy(&id, a, sz);
+ else
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid invocation ID");
+
+ if (sd_id128_is_null(id)) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ pid_t pid;
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_pid(creds, &pid);
+ if (r < 0)
+ return r;
+
+ u = manager_get_unit_by_pid(m, pid);
+ if (!u)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Client " PID_FMT " not member of any unit.", pid);
+ } else {
+ u = hashmap_get(m->units_by_invocation_id, &id);
+ if (!u)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_UNIT_FOR_INVOCATION_ID, "No unit with the specified invocation ID " SD_ID128_FORMAT_STR " known.", SD_ID128_FORMAT_VAL(id));
+ }
+
+ r = mac_selinux_unit_access_check(u, message, "status", error);
+ if (r < 0)
+ return r;
+
+ /* So here's a special trick: the bus path we return actually references the unit by its invocation ID instead
+ * of the unit name. This means it stays valid only as long as the invocation ID stays the same. */
+ path = unit_dbus_path_invocation_id(u);
+ if (!path)
+ return -ENOMEM;
+
+ return sd_bus_reply_method_return(message, "o", path);
+}
+
static int method_load_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *path = NULL;
Manager *m = userdata;
@@ -2254,6 +2312,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("GetUnit", "s", "o", method_get_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUnitByPID", "u", "o", method_get_unit_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetUnitByInvocationID", "ay", "o", method_get_unit_by_invocation_id, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("LoadUnit", "s", "o", method_load_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("StartUnit", "ss", "o", method_start_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("StartUnitReplace", "sss", "o", method_start_unit_replace, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 5020dfba4b..245912fc0f 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -764,6 +764,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("InvocationID", "ay", bus_property_get_id128, offsetof(Unit, invocation_id), 0),
SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/core/device.c b/src/core/device.c
index 16e56efcc3..8a3e888e5e 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -464,6 +464,10 @@ static void device_update_found_one(Device *d, bool add, DeviceFound found, bool
if (!now)
return;
+ /* Didn't exist before, but does now? if so, generate a new invocation ID for it */
+ if (previous == DEVICE_NOT_FOUND && d->found != DEVICE_NOT_FOUND)
+ (void) unit_acquire_invocation_id(UNIT(d));
+
if (d->found & DEVICE_FOUND_UDEV)
/* When the device is known to udev we consider it
* plugged. */
diff --git a/src/core/execute.c b/src/core/execute.c
index d5c4e60796..7079aeed6e 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -1553,10 +1553,11 @@ static int build_environment(
unsigned n_env = 0;
char *x;
+ assert(u);
assert(c);
assert(ret);
- our_env = new0(char*, 13);
+ our_env = new0(char*, 14);
if (!our_env)
return -ENOMEM;
@@ -1627,6 +1628,13 @@ static int build_environment(
our_env[n_env++] = x;
}
+ if (!sd_id128_is_null(u->invocation_id)) {
+ if (asprintf(&x, "INVOCATION_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id)) < 0)
+ return -ENOMEM;
+
+ our_env[n_env++] = x;
+ }
+
if (exec_context_needs_term(c)) {
const char *tty_path, *term = NULL;
diff --git a/src/core/manager.c b/src/core/manager.c
index c1dce62a18..3569249788 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -522,6 +522,7 @@ static void manager_clean_environment(Manager *m) {
"LISTEN_FDNAMES",
"WATCHDOG_PID",
"WATCHDOG_USEC",
+ "INVOCATION_ID",
NULL);
}
@@ -582,9 +583,15 @@ int manager_new(UnitFileScope scope, bool test_run, Manager **_m) {
if (MANAGER_IS_SYSTEM(m)) {
m->unit_log_field = "UNIT=";
m->unit_log_format_string = "UNIT=%s";
+
+ m->invocation_log_field = "INVOCATION_ID=";
+ m->invocation_log_format_string = "INVOCATION_ID=" SD_ID128_FORMAT_STR;
} else {
m->unit_log_field = "USER_UNIT=";
m->unit_log_format_string = "USER_UNIT=%s";
+
+ m->invocation_log_field = "USER_INVOCATION_ID=";
+ m->invocation_log_format_string = "USER_INVOCATION_ID=" SD_ID128_FORMAT_STR;
}
m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
@@ -1062,6 +1069,7 @@ Manager* manager_free(Manager *m) {
hashmap_free(m->dynamic_users);
hashmap_free(m->units);
+ hashmap_free(m->units_by_invocation_id);
hashmap_free(m->jobs);
hashmap_free(m->watch_pids1);
hashmap_free(m->watch_pids2);
@@ -2268,6 +2276,7 @@ int manager_loop(Manager *m) {
int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, Unit **_u) {
_cleanup_free_ char *n = NULL;
+ sd_id128_t invocation_id;
Unit *u;
int r;
@@ -2279,12 +2288,25 @@ int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e,
if (r < 0)
return r;
+ /* Permit addressing units by invocation ID: if the passed bus path is suffixed by a 128bit ID then we use it
+ * as invocation ID. */
+ r = sd_id128_from_string(n, &invocation_id);
+ if (r >= 0) {
+ u = hashmap_get(m->units_by_invocation_id, &invocation_id);
+ if (u) {
+ *_u = u;
+ return 0;
+ }
+
+ return sd_bus_error_setf(e, BUS_ERROR_NO_UNIT_FOR_INVOCATION_ID, "No unit with the specified invocation ID " SD_ID128_FORMAT_STR " known.", SD_ID128_FORMAT_VAL(invocation_id));
+ }
+
+ /* If this didn't work, we use the suffix as unit name. */
r = manager_load_unit(m, n, NULL, e, &u);
if (r < 0)
return r;
*_u = u;
-
return 0;
}
diff --git a/src/core/manager.h b/src/core/manager.h
index 495440b446..29fe14e10b 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -89,6 +89,7 @@ struct Manager {
/* Active jobs and units */
Hashmap *units; /* name string => Unit object n:1 */
+ Hashmap *units_by_invocation_id;
Hashmap *jobs; /* job id => Job object 1:1 */
/* To make it easy to iterate through the units of a specific
@@ -319,6 +320,9 @@ struct Manager {
const char *unit_log_field;
const char *unit_log_format_string;
+ const char *invocation_log_field;
+ const char *invocation_log_format_string;
+
int first_boot; /* tri-state */
};
diff --git a/src/core/mount.c b/src/core/mount.c
index f5e67b1d78..15619dffe3 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -1006,6 +1006,10 @@ static int mount_start(Unit *u) {
return r;
}
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
m->result = MOUNT_SUCCESS;
m->reload_result = MOUNT_SUCCESS;
m->reset_cpu_usage = true;
@@ -1746,9 +1750,10 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
case MOUNT_DEAD:
case MOUNT_FAILED:
- /* This has just been mounted by
- * somebody else, follow the state
- * change. */
+
+ /* This has just been mounted by somebody else, follow the state change, but let's
+ * generate a new invocation ID for this implicitly and automatically. */
+ (void) unit_acquire_invocation_id(UNIT(mount));
mount_enter_mounted(mount, MOUNT_SUCCESS);
break;
diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf
index 647e5f736c..6caa15b0b8 100644
--- a/src/core/org.freedesktop.systemd1.conf
+++ b/src/core/org.freedesktop.systemd1.conf
@@ -54,6 +54,10 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="GetUnitByInvocationID"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="LoadUnit"/>
<allow send_destination="org.freedesktop.systemd1"
diff --git a/src/core/path.c b/src/core/path.c
index 10f9b06974..83f794be89 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -577,6 +577,10 @@ static int path_start(Unit *u) {
return r;
}
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
path_mkdir(p);
p->result = PATH_SUCCESS;
diff --git a/src/core/scope.c b/src/core/scope.c
index 65fa65493b..e7583f6d89 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -298,6 +298,10 @@ static int scope_start(Unit *u) {
if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
return -ENOENT;
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
(void) unit_realize_cgroup(u);
(void) unit_reset_cpu_usage(u);
diff --git a/src/core/service.c b/src/core/service.c
index 98edc437a2..63045ede55 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -2033,6 +2033,10 @@ static int service_start(Unit *u) {
return r;
}
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
s->result = SERVICE_SUCCESS;
s->reload_result = SERVICE_SUCCESS;
s->main_pid_known = false;
diff --git a/src/core/slice.c b/src/core/slice.c
index c7700b8857..03fe797f27 100644
--- a/src/core/slice.c
+++ b/src/core/slice.c
@@ -187,10 +187,15 @@ static void slice_dump(Unit *u, FILE *f, const char *prefix) {
static int slice_start(Unit *u) {
Slice *t = SLICE(u);
+ int r;
assert(t);
assert(t->state == SLICE_DEAD);
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
(void) unit_realize_cgroup(u);
(void) unit_reset_cpu_usage(u);
diff --git a/src/core/socket.c b/src/core/socket.c
index 1b4a1b3dc3..0b1c4acfec 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -2354,11 +2354,14 @@ static int socket_start(Unit *u) {
return r;
}
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
s->result = SOCKET_SUCCESS;
s->reset_cpu_usage = true;
socket_enter_start_pre(s);
-
return 1;
}
diff --git a/src/core/swap.c b/src/core/swap.c
index fee9e7b0e6..b592abb9fb 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -861,6 +861,10 @@ static int swap_start(Unit *u) {
return r;
}
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
s->result = SWAP_SUCCESS;
s->reset_cpu_usage = true;
@@ -1189,6 +1193,7 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v
case SWAP_DEAD:
case SWAP_FAILED:
+ (void) unit_acquire_invocation_id(UNIT(swap));
swap_enter_active(swap, SWAP_SUCCESS);
break;
diff --git a/src/core/target.c b/src/core/target.c
index 61a91aad07..765c1f3fa4 100644
--- a/src/core/target.c
+++ b/src/core/target.c
@@ -124,10 +124,15 @@ static void target_dump(Unit *u, FILE *f, const char *prefix) {
static int target_start(Unit *u) {
Target *t = TARGET(u);
+ int r;
assert(t);
assert(t->state == TARGET_DEAD);
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
target_set_state(t, TARGET_ACTIVE);
return 1;
}
diff --git a/src/core/timer.c b/src/core/timer.c
index e2b43f02f8..9538059c13 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -616,6 +616,10 @@ static int timer_start(Unit *u) {
return r;
}
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
t->last_trigger = DUAL_TIMESTAMP_NULL;
/* Reenable all timers that depend on unit activation time */
@@ -632,7 +636,7 @@ static int timer_start(Unit *u) {
/* The timer has never run before,
* make sure a stamp file exists.
*/
- touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
+ (void) touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
}
t->result = TIMER_SUCCESS;
diff --git a/src/core/unit.c b/src/core/unit.c
index 693f75c928..690f7f7dd9 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -37,6 +37,7 @@
#include "execute.h"
#include "fileio-label.h"
#include "formats-util.h"
+#include "id128-util.h"
#include "load-dropin.h"
#include "load-fragment.h"
#include "log.h"
@@ -521,6 +522,9 @@ void unit_free(Unit *u) {
SET_FOREACH(t, u->names, i)
hashmap_remove_value(u->manager->units, t, u);
+ if (!sd_id128_is_null(u->invocation_id))
+ hashmap_remove_value(u->manager->units_by_invocation_id, &u->invocation_id, u);
+
if (u->job) {
Job *j = u->job;
job_uninstall(j);
@@ -953,6 +957,10 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
SET_FOREACH(t, u->names, i)
fprintf(f, "%s\tName: %s\n", prefix, t);
+ if (!sd_id128_is_null(u->invocation_id))
+ fprintf(f, "%s\tInvocation ID: " SD_ID128_FORMAT_STR "\n",
+ prefix, SD_ID128_FORMAT_VAL(u->invocation_id));
+
STRV_FOREACH(j, u->documentation)
fprintf(f, "%s\tDocumentation: %s\n", prefix, *j);
@@ -1054,7 +1062,6 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
if (u->nop_job)
job_dump(u->nop_job, f, prefix2);
-
}
/* Common implementation for multiple backends */
@@ -2392,6 +2399,15 @@ char *unit_dbus_path(Unit *u) {
return unit_dbus_path_from_name(u->id);
}
+char *unit_dbus_path_invocation_id(Unit *u) {
+ assert(u);
+
+ if (sd_id128_is_null(u->invocation_id))
+ return NULL;
+
+ return unit_dbus_path_from_name(u->invocation_id_string);
+}
+
int unit_set_slice(Unit *u, Unit *slice) {
assert(u);
assert(slice);
@@ -2640,6 +2656,9 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
if (gid_is_valid(u->ref_gid))
unit_serialize_item_format(u, f, "ref-gid", GID_FMT, u->ref_gid);
+ if (!sd_id128_is_null(u->invocation_id))
+ unit_serialize_item_format(u, f, "invocation-id", SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id));
+
bus_track_serialize(u->bus_track, f, "ref");
if (serialize_jobs) {
@@ -2915,6 +2934,19 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
log_oom();
continue;
+ } else if (streq(l, "invocation-id")) {
+ sd_id128_t id;
+
+ r = sd_id128_from_string(v, &id);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse invocation id %s, ignoring.", v);
+ else {
+ r = unit_set_invocation_id(u, id);
+ if (r < 0)
+ log_unit_warning_errno(u, r, "Failed to set invocation ID for unit: %m");
+ }
+
+ continue;
}
if (unit_can_serialize(u)) {
@@ -4153,3 +4185,57 @@ void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid) {
if (r > 0)
bus_unit_send_change_signal(u);
}
+
+int unit_set_invocation_id(Unit *u, sd_id128_t id) {
+ int r;
+
+ assert(u);
+
+ /* Set the invocation ID for this unit. If we cannot, this will not roll back, but reset the whole thing. */
+
+ if (sd_id128_equal(u->invocation_id, id))
+ return 0;
+
+ if (!sd_id128_is_null(u->invocation_id))
+ (void) hashmap_remove_value(u->manager->units_by_invocation_id, &u->invocation_id, u);
+
+ if (sd_id128_is_null(id)) {
+ r = 0;
+ goto reset;
+ }
+
+ r = hashmap_ensure_allocated(&u->manager->units_by_invocation_id, &id128_hash_ops);
+ if (r < 0)
+ goto reset;
+
+ u->invocation_id = id;
+ sd_id128_to_string(id, u->invocation_id_string);
+
+ r = hashmap_put(u->manager->units_by_invocation_id, &u->invocation_id, u);
+ if (r < 0)
+ goto reset;
+
+ return 0;
+
+reset:
+ u->invocation_id = SD_ID128_NULL;
+ u->invocation_id_string[0] = 0;
+ return r;
+}
+
+int unit_acquire_invocation_id(Unit *u) {
+ sd_id128_t id;
+ int r;
+
+ assert(u);
+
+ r = sd_id128_randomize(&id);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to generate invocation ID for unit: %m");
+
+ r = unit_set_invocation_id(u, id);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to set invocation ID for unit: %m");
+
+ return 0;
+}
diff --git a/src/core/unit.h b/src/core/unit.h
index 3584c16d8c..a8dd3e602c 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -207,6 +207,10 @@ struct Unit {
/* How to start OnFailure units */
JobMode on_failure_job_mode;
+ /* The current invocation ID */
+ sd_id128_t invocation_id;
+ char invocation_id_string[SD_ID128_STRING_MAX]; /* useful when logging */
+
/* Garbage collect us we nobody wants or requires us anymore */
bool stop_when_unneeded;
@@ -546,6 +550,7 @@ bool unit_job_is_applicable(Unit *u, JobType j);
int set_unit_path(const char *p);
char *unit_dbus_path(Unit *u);
+char *unit_dbus_path_invocation_id(Unit *u);
int unit_load_related_unit(Unit *u, const char *type, Unit **_found);
@@ -643,12 +648,15 @@ void unit_unref_uid_gid(Unit *u, bool destroy_now);
void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid);
+int unit_set_invocation_id(Unit *u, sd_id128_t id);
+int unit_acquire_invocation_id(Unit *u);
+
/* Macros which append UNIT= or USER_UNIT= to the message */
#define log_unit_full(unit, level, error, ...) \
({ \
const Unit *_u = (unit); \
- _u ? log_object_internal(level, error, __FILE__, __LINE__, __func__, _u->manager->unit_log_field, _u->id, ##__VA_ARGS__) : \
+ _u ? log_object_internal(level, error, __FILE__, __LINE__, __func__, _u->manager->unit_log_field, _u->id, _u->manager->invocation_log_field, _u->invocation_id_string, ##__VA_ARGS__) : \
log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
})