Skip to content

Commit

Permalink
core: add "invocation ID" concept to service manager
Browse files Browse the repository at this point in the history
This adds a new invocation ID concept to the service manager. The invocation ID
identifies each runtime cycle of a unit uniquely. A new randomized 128bit ID is
generated each time a unit moves from and inactive to an activating or active
state.

The primary usecase for this concept is to connect the runtime data PID 1
maintains about a service with the offline data the journal stores about it.
Previously we'd use the unit name plus start/stop times, which however is
highly racy since the journal will generally process log data after the service
already ended.

The "invocation ID" kinda matches the "boot ID" concept of the Linux kernel,
except that it applies to an individual unit instead of the whole system.

The invocation ID is passed to the activated processes as environment variable.
It is additionally stored as extended attribute on the cgroup of the unit. The
latter is used by journald to automatically retrieve it for each log logged
message and attach it to the log entry. The environment variable is very easily
accessible, even for unprivileged services. OTOH the extended attribute is only
accessible to privileged processes (this is because cgroupfs only supports the
"trusted." xattr namespace, not "user."). The environment variable may be
altered by services, the extended attribute may not be, hence is the better
choice for the journal.

Note that reading the invocation ID off the extended attribute from journald is
racy, similar to the way reading the unit name for a logging process is.

This patch adds APIs to read the invocation ID to sd-id128:
sd_id128_get_invocation() may be used in a similar fashion to
sd_id128_get_boot().

PID1's own logging is updated to always include the invocation ID when it logs
information about a unit.

A new bus call GetUnitByInvocationID() is added that allows retrieving a bus
path to a unit by its invocation ID. The bus path is built using the invocation
ID, thus providing a path for referring to a unit that is valid only for the
current runtime cycleof it.

Outlook for the future: should the kernel eventually allow passing of cgroup
information along AF_UNIX/SOCK_DGRAM messages via a unique cgroup id, then we
can alter the invocation ID to be generated as hash from that rather than
entirely randomly. This way we can derive the invocation race-freely from the
messages.
  • Loading branch information
poettering committed Oct 7, 2016
1 parent e5d855d commit 4b58153
Show file tree
Hide file tree
Showing 40 changed files with 486 additions and 55 deletions.
5 changes: 5 additions & 0 deletions Makefile-man.am
Expand Up @@ -395,6 +395,7 @@ MANPAGES_ALIAS += \
man/sd_id128_equal.3 \
man/sd_id128_from_string.3 \
man/sd_id128_get_boot.3 \
man/sd_id128_get_invocation.3 \
man/sd_id128_t.3 \
man/sd_is_mq.3 \
man/sd_is_socket.3 \
Expand Down Expand Up @@ -745,6 +746,7 @@ man/sd_event_unrefp.3: man/sd_event_new.3
man/sd_id128_equal.3: man/sd-id128.3
man/sd_id128_from_string.3: man/sd_id128_to_string.3
man/sd_id128_get_boot.3: man/sd_id128_get_machine.3
man/sd_id128_get_invocation.3: man/sd_id128_get_machine.3
man/sd_id128_t.3: man/sd-id128.3
man/sd_is_mq.3: man/sd_is_fifo.3
man/sd_is_socket.3: man/sd_is_fifo.3
Expand Down Expand Up @@ -1519,6 +1521,9 @@ man/sd_id128_from_string.html: man/sd_id128_to_string.html
man/sd_id128_get_boot.html: man/sd_id128_get_machine.html
$(html-alias)

man/sd_id128_get_invocation.html: man/sd_id128_get_machine.html
$(html-alias)

man/sd_id128_t.html: man/sd-id128.html
$(html-alias)

Expand Down
34 changes: 22 additions & 12 deletions man/sd_id128_get_machine.xml
Expand Up @@ -45,6 +45,7 @@
<refnamediv>
<refname>sd_id128_get_machine</refname>
<refname>sd_id128_get_boot</refname>
<refname>sd_id128_get_invocation</refname>
<refpurpose>Retrieve 128-bit IDs</refpurpose>
</refnamediv>

Expand All @@ -62,6 +63,11 @@
<paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
</funcprototype>

<funcprototype>
<funcdef>int <function>sd_id128_get_invocation</function></funcdef>
<paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
</funcprototype>

</funcsynopsis>
</refsynopsisdiv>

Expand All @@ -83,11 +89,15 @@
for more information. This function also internally caches the
returned ID to make this call a cheap operation.</para>

<para>Note that <function>sd_id128_get_boot()</function> always
returns a UUID v4 compatible ID.
<function>sd_id128_get_machine()</function> will also return a
UUID v4-compatible ID on new installations but might not on older.
It is possible to convert the machine ID into a UUID v4-compatible
<para><function>sd_id128_get_invocation()</function> returns the invocation ID of the currently executed
service. In its current implementation, this reads and parses the <varname>$INVOCATION_ID</varname> environment
variable that the service manager sets when activating a service, see
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details. The
ID is cached internally. In future a different mechanism to determine the invocation ID may be added.</para>

<para>Note that <function>sd_id128_get_boot()</function> and <function>sd_id128_get_invocation()</function> always
return UUID v4 compatible IDs. <function>sd_id128_get_machine()</function> will also return a UUID v4-compatible
ID on new installations but might not on older. It is possible to convert the machine ID into a UUID v4-compatible
one. For more information, see
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>

Expand All @@ -107,11 +117,10 @@
<refsect1>
<title>Notes</title>

<para>The <function>sd_id128_get_machine()</function> and
<function>sd_id128_get_boot()</function> interfaces are available
as a shared library, which can be compiled and linked to with the
<literal>libsystemd</literal> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
file.</para>
<para>The <function>sd_id128_get_machine()</function>, <function>sd_id128_get_boot()</function> and
<function>sd_id128_get_invocation()</function> interfaces are available as a shared library, which can be compiled
and linked to with the <literal>libsystemd</literal> <citerefentry
project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para>
</refsect1>

<refsect1>
Expand All @@ -121,8 +130,9 @@
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_id128_randomize</refentrytitle><manvolnum>3</manvolnum></citerefentry>
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_id128_randomize</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry>
</para>
</refsect1>

Expand Down
10 changes: 10 additions & 0 deletions man/systemd.exec.xml
Expand Up @@ -1513,6 +1513,16 @@
</para></listitem>
</varlistentry>

<varlistentry>
<term><varname>$INVOCATION_ID</varname></term>

<listitem><para>Contains a randomized, unique 128bit ID identifying each runtime cycle of the unit, formatted
as 32 character hexadecimal string. A new ID is assigned each time the unit changes from an inactive state into
an activating or active state, and may be used to identify this specific runtime cycle, in particular in data
stored offline, such as the journal. The same ID is passed to all processes run as part of the
unit.</para></listitem>
</varlistentry>

<varlistentry>
<term><varname>$XDG_RUNTIME_DIR</varname></term>

Expand Down
38 changes: 38 additions & 0 deletions src/basic/cgroup-util.c
Expand Up @@ -28,6 +28,7 @@
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/types.h>
#include <sys/xattr.h>
#include <unistd.h>

#include "alloc-util.h"
Expand Down Expand Up @@ -883,6 +884,43 @@ int cg_set_task_access(
return 0;
}

int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags) {
_cleanup_free_ char *fs = NULL;
int r;

assert(path);
assert(name);
assert(value || size <= 0);

r = cg_get_path(controller, path, NULL, &fs);
if (r < 0)
return r;

if (setxattr(fs, name, value, size, flags) < 0)
return -errno;

return 0;
}

int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size) {
_cleanup_free_ char *fs = NULL;
ssize_t n;
int r;

assert(path);
assert(name);

r = cg_get_path(controller, path, NULL, &fs);
if (r < 0)
return r;

n = getxattr(fs, name, value, size);
if (n < 0)
return -errno;

return (int) n;
}

int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
Expand Down
3 changes: 3 additions & 0 deletions src/basic/cgroup-util.h
Expand Up @@ -185,6 +185,9 @@ int cg_get_keyed_attribute(const char *controller, const char *path, const char
int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);
int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);

int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags);
int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size);

int cg_install_release_agent(const char *controller, const char *agent);
int cg_uninstall_release_agent(const char *controller);

Expand Down
45 changes: 26 additions & 19 deletions src/basic/log.c
Expand Up @@ -330,8 +330,6 @@ static int write_to_console(
const char *file,
int line,
const char *func,
const char *object_field,
const char *object,
const char *buffer) {

char location[256], prefix[1 + DECIMAL_STR_MAX(int) + 2];
Expand Down Expand Up @@ -390,8 +388,6 @@ static int write_to_syslog(
const char *file,
int line,
const char *func,
const char *object_field,
const char *object,
const char *buffer) {

char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
Expand Down Expand Up @@ -453,8 +449,6 @@ static int write_to_kmsg(
const char *file,
int line,
const char *func,
const char *object_field,
const char *object,
const char *buffer) {

char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
Expand Down Expand Up @@ -485,7 +479,8 @@ static int log_do_header(
int level,
int error,
const char *file, int line, const char *func,
const char *object_field, const char *object) {
const char *object_field, const char *object,
const char *extra_field, const char *extra) {

snprintf(header, size,
"PRIORITY=%i\n"
Expand All @@ -495,6 +490,7 @@ static int log_do_header(
"%s%s%s"
"%s%.*i%s"
"%s%s%s"
"%s%s%s"
"SYSLOG_IDENTIFIER=%s\n",
LOG_PRI(level),
LOG_FAC(level),
Expand All @@ -513,6 +509,9 @@ static int log_do_header(
isempty(object) ? "" : object_field,
isempty(object) ? "" : object,
isempty(object) ? "" : "\n",
isempty(extra) ? "" : extra_field,
isempty(extra) ? "" : extra,
isempty(extra) ? "" : "\n",
program_invocation_short_name);

return 0;
Expand All @@ -526,6 +525,8 @@ static int write_to_journal(
const char *func,
const char *object_field,
const char *object,
const char *extra_field,
const char *extra,
const char *buffer) {

char header[LINE_MAX];
Expand All @@ -535,7 +536,7 @@ static int write_to_journal(
if (journal_fd < 0)
return 0;

log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object);
log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object, extra_field, extra);

IOVEC_SET_STRING(iovec[0], header);
IOVEC_SET_STRING(iovec[1], "MESSAGE=");
Expand All @@ -559,6 +560,8 @@ static int log_dispatch(
const char *func,
const char *object_field,
const char *object,
const char *extra,
const char *extra_field,
char *buffer) {

assert(buffer);
Expand Down Expand Up @@ -589,7 +592,7 @@ static int log_dispatch(
log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
log_target == LOG_TARGET_JOURNAL) {

k = write_to_journal(level, error, file, line, func, object_field, object, buffer);
k = write_to_journal(level, error, file, line, func, object_field, object, extra_field, extra, buffer);
if (k < 0) {
if (k != -EAGAIN)
log_close_journal();
Expand All @@ -600,7 +603,7 @@ static int log_dispatch(
if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
log_target == LOG_TARGET_SYSLOG) {

k = write_to_syslog(level, error, file, line, func, object_field, object, buffer);
k = write_to_syslog(level, error, file, line, func, buffer);
if (k < 0) {
if (k != -EAGAIN)
log_close_syslog();
Expand All @@ -615,15 +618,15 @@ static int log_dispatch(
log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
log_target == LOG_TARGET_KMSG)) {

k = write_to_kmsg(level, error, file, line, func, object_field, object, buffer);
k = write_to_kmsg(level, error, file, line, func, buffer);
if (k < 0) {
log_close_kmsg();
log_open_console();
}
}

if (k <= 0)
(void) write_to_console(level, error, file, line, func, object_field, object, buffer);
(void) write_to_console(level, error, file, line, func, buffer);

buffer = e;
} while (buffer);
Expand All @@ -649,7 +652,7 @@ int log_dump_internal(
if (_likely_(LOG_PRI(level) > log_max_level))
return -error;

return log_dispatch(level, error, file, line, func, NULL, NULL, buffer);
return log_dispatch(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer);
}

int log_internalv(
Expand All @@ -676,7 +679,7 @@ int log_internalv(

vsnprintf(buffer, sizeof(buffer), format, ap);

return log_dispatch(level, error, file, line, func, NULL, NULL, buffer);
return log_dispatch(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer);
}

int log_internal(
Expand Down Expand Up @@ -705,6 +708,8 @@ int log_object_internalv(
const char *func,
const char *object_field,
const char *object,
const char *extra_field,
const char *extra,
const char *format,
va_list ap) {

Expand Down Expand Up @@ -738,7 +743,7 @@ int log_object_internalv(

vsnprintf(b, l, format, ap);

return log_dispatch(level, error, file, line, func, object_field, object, buffer);
return log_dispatch(level, error, file, line, func, object_field, object, extra_field, extra, buffer);
}

int log_object_internal(
Expand All @@ -749,13 +754,15 @@ int log_object_internal(
const char *func,
const char *object_field,
const char *object,
const char *extra_field,
const char *extra,
const char *format, ...) {

va_list ap;
int r;

va_start(ap, format);
r = log_object_internalv(level, error, file, line, func, object_field, object, format, ap);
r = log_object_internalv(level, error, file, line, func, object_field, object, extra_field, extra, format, ap);
va_end(ap);

return r;
Expand All @@ -780,7 +787,7 @@ static void log_assert(

log_abort_msg = buffer;

log_dispatch(level, 0, file, line, func, NULL, NULL, buffer);
log_dispatch(level, 0, file, line, func, NULL, NULL, NULL, NULL, buffer);
}

noreturn void log_assert_failed(const char *text, const char *file, int line, const char *func) {
Expand Down Expand Up @@ -888,7 +895,7 @@ int log_struct_internal(
bool fallback = false;

/* If the journal is available do structured logging */
log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL);
log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL);
IOVEC_SET_STRING(iovec[n++], header);

va_start(ap, format);
Expand Down Expand Up @@ -935,7 +942,7 @@ int log_struct_internal(
if (!found)
return -error;

return log_dispatch(level, error, file, line, func, NULL, NULL, buf + 8);
return log_dispatch(level, error, file, line, func, NULL, NULL, NULL, NULL, buf + 8);
}

int log_set_target_from_string(const char *e) {
Expand Down
10 changes: 7 additions & 3 deletions src/basic/log.h
Expand Up @@ -100,18 +100,22 @@ int log_object_internal(
const char *func,
const char *object_field,
const char *object,
const char *format, ...) _printf_(8,9);
const char *extra_field,
const char *extra,
const char *format, ...) _printf_(10,11);

int log_object_internalv(
int level,
int error,
const char*file,
const char *file,
int line,
const char *func,
const char *object_field,
const char *object,
const char *extra_field,
const char *extra,
const char *format,
va_list ap) _printf_(8,0);
va_list ap) _printf_(9,0);

int log_struct_internal(
int level,
Expand Down

0 comments on commit 4b58153

Please sign in to comment.