Merge pull request #14178 from poettering/journal-namespace

journal: add concept of "journal namespaces"
This commit is contained in:
Lennart Poettering 2020-02-01 11:25:48 +01:00 committed by GitHub
commit 5ee69e144e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
59 changed files with 1385 additions and 470 deletions

5
TODO
View file

@ -894,11 +894,6 @@ Features:
- journald: when we drop syslog messages because the syslog socket is
full, make sure to write how many messages are lost as first thing
to syslog when it works again.
- change systemd-journal-flush into a service that stays around during
boot, and causes the journal to be moved back to /run on shutdown,
so that we do not keep /var busy. This needs to happen synchronously,
hence doing this via signals is not going to work.
- optionally support running journald from the command line for testing purposes in external projects
- journald: allow per-priority and per-service retention times when rotating/vacuuming
- journald: make use of uid-range.h to managed uid ranges to split
journals in.

View file

@ -749,6 +749,18 @@
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--namespace=<replaceable>NAMESPACE</replaceable></option></term>
<listitem><para>Takes a journal namespace identifier string as argument. If not specified the data
collected by the default namespace is shown. If specified shows the log data of the specified
namespace instead. If the namespace is specified as <literal>*</literal> data from all namespaces is
shown, interleaved. If the namespace identifier is prefixed with <literal>+</literal> data from the
specified namespace and the default namespace is shown, interleaved, but no other. For details about
journal namespaces see
<citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--header</option></term>

View file

@ -18,6 +18,7 @@
<refnamediv>
<refname>journald.conf</refname>
<refname>journald.conf.d</refname>
<refname>journald@.conf</refname>
<refpurpose>Journal service configuration files</refpurpose>
</refnamediv>
@ -26,6 +27,7 @@
<para><filename>/etc/systemd/journald.conf.d/*.conf</filename></para>
<para><filename>/run/systemd/journald.conf.d/*.conf</filename></para>
<para><filename>/usr/lib/systemd/journald.conf.d/*.conf</filename></para>
<para><filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf</filename></para>
</refsynopsisdiv>
<refsect1>
@ -37,6 +39,12 @@
<citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for a general description of the syntax.</para>
<para>The <command>systemd-journald</command> instance managing the default namespace is configured by
<filename>/etc/systemd/journald.conf</filename> and associated drop-ins. Instances managing other
namespaces read <filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf</filename> with
the namespace identifier filled in. This allows each namespace to carry a distinct configuration. See
<citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for details about journal namespaces.</para>
</refsect1>
<xi:include href="standard-conf.xml" xpointer="main-conf" />
@ -52,29 +60,19 @@
<varlistentry>
<term><varname>Storage=</varname></term>
<listitem><para>Controls where to store journal data. One of
<literal>volatile</literal>,
<literal>persistent</literal>,
<literal>auto</literal> and
<literal>none</literal>. If
<literal>volatile</literal>, journal
log data will be stored only in memory, i.e. below the
<filename>/run/log/journal</filename> hierarchy (which is
created if needed). If <literal>persistent</literal>, data
will be stored preferably on disk, i.e. below the
<filename>/var/log/journal</filename> hierarchy (which is
created if needed), with a fallback to
<filename>/run/log/journal</filename> (which is created if
needed), during early boot and if the disk is not writable.
<literal>auto</literal> is similar to
<literal>persistent</literal> but the directory
<filename>/var/log/journal</filename> is not created if
needed, so that its existence controls where log data goes.
<literal>none</literal> turns off all storage, all log data
received will be dropped. Forwarding to other targets, such as
the console, the kernel log buffer, or a syslog socket will
still work however. Defaults to
<literal>auto</literal>.</para></listitem>
<listitem><para>Controls where to store journal data. One of <literal>volatile</literal>,
<literal>persistent</literal>, <literal>auto</literal> and <literal>none</literal>. If
<literal>volatile</literal>, journal log data will be stored only in memory, i.e. below the
<filename>/run/log/journal</filename> hierarchy (which is created if needed). If
<literal>persistent</literal>, data will be stored preferably on disk, i.e. below the
<filename>/var/log/journal</filename> hierarchy (which is created if needed), with a fallback to
<filename>/run/log/journal</filename> (which is created if needed), during early boot and if the disk
is not writable. <literal>auto</literal> is similar to <literal>persistent</literal> but the
directory <filename>/var/log/journal</filename> is not created if needed, so that its existence
controls where log data goes. <literal>none</literal> turns off all storage, all log data received
will be dropped. Forwarding to other targets, such as the console, the kernel log buffer, or a syslog
socket will still work however. Defaults to <literal>auto</literal> in the default journal namespace,
and <literal>persistent</literal> in all others.</para></listitem>
</varlistentry>
<varlistentry>
@ -399,9 +397,9 @@
<varlistentry>
<term><varname>ReadKMsg=</varname></term>
<listitem><para>Takes a boolean value. If enabled (the
default), journal reads <filename>/dev/kmsg</filename>
messages generated by the kernel.</para></listitem>
<listitem><para>Takes a boolean value. If enabled <command>systemd-journal</command> processes
<filename>/dev/kmsg</filename> messages generated by the kernel. In the default journal namespace
this option is enabled by default, it is disabled in all others.</para></listitem>
</varlistentry>
<varlistentry>

View file

@ -24,7 +24,7 @@ manpages = [
['journal-remote.conf', '5', ['journal-remote.conf.d'], 'HAVE_MICROHTTPD'],
['journal-upload.conf', '5', ['journal-upload.conf.d'], 'HAVE_MICROHTTPD'],
['journalctl', '1', [], ''],
['journald.conf', '5', ['journald.conf.d'], ''],
['journald.conf', '5', ['journald.conf.d', 'journald@.conf'], ''],
['kernel-command-line', '7', [], ''],
['kernel-install', '8', [], ''],
['libudev', '3', [], ''],
@ -550,7 +550,9 @@ manpages = [
''],
['sd_journal_open',
'3',
['SD_JOURNAL_CURRENT_USER',
['SD_JOURNAL_ALL_NAMESPACES',
'SD_JOURNAL_CURRENT_USER',
'SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE',
'SD_JOURNAL_LOCAL_ONLY',
'SD_JOURNAL_OS_ROOT',
'SD_JOURNAL_RUNTIME_ONLY',
@ -737,7 +739,10 @@ manpages = [
['systemd-journald',
'systemd-journald-audit.socket',
'systemd-journald-dev-log.socket',
'systemd-journald.socket'],
'systemd-journald-varlink@.socket',
'systemd-journald.socket',
'systemd-journald@.service',
'systemd-journald@.socket'],
''],
['systemd-localed.service', '8', ['systemd-localed'], 'ENABLE_LOCALED'],
['systemd-logind.service', '8', ['systemd-logind'], 'ENABLE_LOGIND'],

View file

@ -29,6 +29,8 @@
<refname>SD_JOURNAL_SYSTEM</refname>
<refname>SD_JOURNAL_CURRENT_USER</refname>
<refname>SD_JOURNAL_OS_ROOT</refname>
<refname>SD_JOURNAL_ALL_NAMESPACES</refname>
<refname>SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE</refname>
<refpurpose>Open the system journal for reading</refpurpose>
</refnamediv>
@ -42,6 +44,13 @@
<paramdef>int <parameter>flags</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_journal_open_namespace</function></funcdef>
<paramdef>sd_journal **<parameter>ret</parameter></paramdef>
<paramdef>const char *<parameter>namespace</parameter></paramdef>
<paramdef>int <parameter>flags</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_journal_open_directory</function></funcdef>
<paramdef>sd_journal **<parameter>ret</parameter></paramdef>
@ -101,6 +110,19 @@
<constant>SD_JOURNAL_CURRENT_USER</constant> are specified, all
journal file types will be opened.</para>
<para><function>sd_journal_open_namespace()</function> is similar to
<function>sd_journal_open()</function> but takes an additional <parameter>namespace</parameter> parameter
that specifies which journal namespace to operate on. If specified as <constant>NULL</constant> the call
is identical to <function>sd_journal_open()</function>. If non-<constant>NULL</constant> only data from
the namespace identified by the specified parameter is accessed. This call understands two additional
flags: if <constant>SD_JOURNAL_ALL_NAMESPACES</constant> is specified the
<parameter>namespace</parameter> parameter is ignored and all defined namespaces are accessed
simultaneously; if <constant>SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE</constant> the specified namespace and
the default namespace are accessed but no others (this flag has no effect when
<parameter>namespace</parameter> is passed as <constant>NULL</constant>). For details about journal
namespaces see
<citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para><function>sd_journal_open_directory()</function> is similar to <function>sd_journal_open()</function> but
takes an absolute directory path as argument. All journal files in this directory will be opened and interleaved
automatically. This call also takes a flags argument. The flags parameters accepted by this call are

View file

@ -20,6 +20,9 @@
<refname>systemd-journald.socket</refname>
<refname>systemd-journald-dev-log.socket</refname>
<refname>systemd-journald-audit.socket</refname>
<refname>systemd-journald@.service</refname>
<refname>systemd-journald@.socket</refname>
<refname>systemd-journald-varlink@.socket</refname>
<refname>systemd-journald</refname>
<refpurpose>Journal service</refpurpose>
</refnamediv>
@ -29,6 +32,9 @@
<para><filename>systemd-journald.socket</filename></para>
<para><filename>systemd-journald-dev-log.socket</filename></para>
<para><filename>systemd-journald-audit.socket</filename></para>
<para><filename>systemd-journald@.service</filename></para>
<para><filename>systemd-journald@.socket</filename></para>
<para><filename>systemd-journald-varlink@.socket</filename></para>
<para><filename>/usr/lib/systemd/systemd-journald</filename></para>
</refsynopsisdiv>
@ -129,6 +135,40 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
<constant>EPIPE</constant> right from the beginning.</para>
</refsect1>
<refsect1>
<title>Journal Namespaces</title>
<para>Journal 'namespaces' are both a mechanism for logically isolating the log stream of projects
consisting of one or more services from the rest of the system and a mechanism for improving
performance. Multiple journal namespaces may exist simultaneously, each defining its own, independent log
stream managed by its own instance of <command>systemd-journald</command>. Namespaces are independent of
each other, both in the data store and in the IPC interface. By default only a single 'default' namespace
exists, managed by <filename>systemd-journald.service</filename> (and its associated socket
units). Additional namespaces are created by starting an instance of the
<filename>systemd-journald@.service</filename> service template. The instance name is the namespace
identifier, which is a short string used for referencing the journal namespace. Service units may be
assigned to a specific journal namespace through the <varname>LogNamespace=</varname> unit file setting,
see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details. The <option>--namespace=</option> switch of
<citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> may be
used to view the log stream of a specific namespace. If the switch is not used the log stream of the
default namespace is shown, i.e. log data from other namespaces is not visible.</para>
<para>Services associated with a specific log namespace may log via syslog, the native logging protocol
of the journal and via stdout/stderr; the logging from all three transports is associated with the
namespace.</para>
<para>By default only the default namespace will collect kernel and audit log messages.</para>
<para>The <command>systemd-journald</command> instance of the default namespace is configured through
<filename>/etc/systemd/journald.conf</filename> (see below), while the other instances are configured
through <filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf</filename>. The journal
log data for the default namespace is placed in
<filename>/var/log/journal/<replaceable>MACHINE_ID</replaceable></filename> (see below) while the data
for the other namespaces is located in
<filename>/var/log/journal/<replaceable>MACHINE_ID</replaceable>.<replaceable>NAMESPACE</replaceable></filename>.</para>
</refsect1>
<refsect1>
<title>Signals</title>
@ -190,6 +230,9 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
</varlistentry>
</variablelist>
<para>Note that these kernel command line options are only honoured by the default namespace, see
above.</para>
</refsect1>
<refsect1>
@ -279,12 +322,14 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
<term><filename>/run/systemd/journal/socket</filename></term>
<term><filename>/run/systemd/journal/stdout</filename></term>
<listitem><para>Sockets and other paths that
<command>systemd-journald</command> will listen on that are
visible in the file system. In addition to these, journald can
listen for audit events using netlink.</para></listitem>
<listitem><para>Sockets and other file node paths that <command>systemd-journald</command> will
listen on and are visible in the file system. In addition to these,
<command>systemd-journald</command> can listen for audit events using <citerefentry
project='man-pages'><refentrytitle>netlink</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para></listitem>
</varlistentry>
</variablelist>
<para>If journal namespacing is used these paths are slightly altered to include a namespace identifier, see above.</para>
</refsect1>
<refsect1>
@ -296,7 +341,7 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
<citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-journal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-coredump</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='die-net'><refentrytitle>setfacl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>setfacl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<command>pydoc systemd.journal</command>
</para>

View file

@ -70,6 +70,10 @@
<option>syslog</option> or <option>kmsg</option> (or their combinations with console output, see below)
automatically acquire dependencies of type <varname>After=</varname> on
<filename>systemd-journald.socket</filename>.</para></listitem>
<listitem><para>Units using <varname>LogNamespace=</varname> will automatically gain ordering and
requirement dependencies on the two socket units associated with
<filename>systemd-journald@.service</filename> instances.</para></listitem>
</itemizedlist>
</refsect1>
@ -2254,6 +2258,36 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>LogNamespace=</varname></term>
<listitem><para>Run the unit's processes in the specified journal namespace. Expects a short
user-defined string identifying the namespace. If not used the processes of the service are run in
the default journal namespace, i.e. their log stream is collected and processed by
<filename>systemd-journald.service</filename>. If this option is used any log data generated by
processes of this unit (regardless if via the <function>syslog()</function>, journal native logging
or stdout/stderr logging) is collected and processed by an instance of the
<filename>systemd-journald@.service</filename> template unit, which manages the specified
namespace. The log data is stored in a data store independent from the default log namespace's data
store. See
<citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for details about journal namespaces.</para>
<para>Internally, journal namespaces are implemented through Linux mount namespacing and
over-mounting the directory that contains the relevant <constant>AF_UNIX</constant> sockets used for
logging in the unit's mount namespace. Since mount namespaces are used this setting disconnects
propagation of mounts from the unit's processes to the host, similar to how
<varname>ReadOnlyPaths=</varname> and similar settings (see above) work. Journal namespaces may hence
not be used for services that need to establish mount points on the host.</para>
<para>When this option is used the unit will automatically gain ordering and requirement dependencies
on the two socket units associated with the <filename>systemd-journald@.service</filename> instance
so that they are automatically established prior to the unit starting up. Note that when this option
is used log output of this service does not appear in the regular
<citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
output, unless the <option>--namespace=</option> option is used.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>SyslogIdentifier=</varname></term>

View file

@ -358,6 +358,15 @@
marking the log line end.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>_NAMESPACE=</varname></term>
<listitem><para>If this file was written by a <command>systemd-journald</command> instance managing a
journal namespace that is not the default, this field contains the namespace identifier. See
<citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for details about journal namespaces.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View file

@ -1054,6 +1054,8 @@ bool string_is_safe(const char *p) {
if (!p)
return false;
/* Checks if the specified string contains no quotes or control characters */
for (t = p; *t; t++) {
if (*t > 0 && *t < ' ') /* no control characters */
return false;

View file

@ -2,10 +2,15 @@
#include <syslog.h>
#include "sd-id128.h"
#include "glob-util.h"
#include "hexdecoct.h"
#include "macro.h"
#include "path-util.h"
#include "string-table.h"
#include "syslog-util.h"
#include "unit-name.h"
int syslog_parse_priority(const char **p, int *priority, bool with_facility) {
int a = 0, b = 0, c = 0;
@ -96,3 +101,31 @@ DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_level, int, LOG_DEBUG);
bool log_level_is_valid(int level) {
return level >= 0 && level <= LOG_DEBUG;
}
/* The maximum size for a log namespace length. This is the file name size limit 255 minus the size of a
* formatted machine ID minus a separator char */
#define LOG_NAMESPACE_MAX (NAME_MAX - (SD_ID128_STRING_MAX - 1) - 1)
bool log_namespace_name_valid(const char *s) {
/* Let's make sure the namespace fits in a filename that is prefixed with the machine ID and a dot
* (so that /var/log/journal/<machine-id>.<namespace> can be created based on it). Also make sure it
* is suitable as unit instance name, and does not contain fishy characters. */
if (!filename_is_valid(s))
return false;
if (strlen(s) > LOG_NAMESPACE_MAX)
return false;
if (!unit_instance_is_valid(s))
return false;
if (!string_is_safe(s))
return false;
/* Let's avoid globbing for now */
if (string_is_glob(s))
return false;
return true;
}

View file

@ -12,3 +12,5 @@ int log_level_from_string(const char *s);
bool log_level_is_valid(int level);
int syslog_parse_priority(const char **p, int *priority, bool with_facility);
bool log_namespace_name_valid(const char *s);

View file

@ -766,6 +766,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("LogRateLimitIntervalUSec", "t", bus_property_get_usec, offsetof(ExecContext, log_ratelimit_interval_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogRateLimitBurst", "u", bus_property_get_unsigned, offsetof(ExecContext, log_ratelimit_burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogExtraFields", "aay", property_get_log_extra_fields, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogNamespace", "s", NULL, offsetof(ExecContext, log_namespace), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CapabilityBoundingSet", "t", NULL, offsetof(ExecContext, capability_bounding_set), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("AmbientCapabilities", "t", NULL, offsetof(ExecContext, capability_ambient_set), SD_BUS_VTABLE_PROPERTY_CONST),
@ -1436,6 +1437,32 @@ int bus_exec_context_set_transient_property(
return 1;
} else if (streq(name, "LogNamespace")) {
const char *n;
r = sd_bus_message_read(message, "s", &n);
if (r < 0)
return r;
if (!isempty(n) && !log_namespace_name_valid(n))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log namespace name not valid");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (isempty(n)) {
c->log_namespace = mfree(c->log_namespace);
unit_write_settingf(u, flags, name, "%s=", name);
} else {
r = free_and_strdup(&c->log_namespace, n);
if (r < 0)
return r;
unit_write_settingf(u, flags, name, "%s=%s", name, n);
}
}
return 1;
} else if (streq(name, "LogExtraFields")) {
size_t n = 0;

View file

@ -265,15 +265,27 @@ static int open_null_as(int flags, int nfd) {
return move_fd(fd, nfd, false);
}
static int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
static const union sockaddr_union sa = {
static int connect_journal_socket(
int fd,
const char *log_namespace,
uid_t uid,
gid_t gid) {
union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/journal/stdout",
};
uid_t olduid = UID_INVALID;
gid_t oldgid = GID_INVALID;
const char *j;
int r;
j = log_namespace ?
strjoina("/run/systemd/journal.", log_namespace, "/stdout") :
"/run/systemd/journal/stdout";
r = sockaddr_un_set_path(&sa.un, j);
if (r < 0)
return r;
if (gid_is_valid(gid)) {
oldgid = getgid();
@ -328,7 +340,7 @@ static int connect_logger_as(
if (fd < 0)
return -errno;
r = connect_journal_socket(fd, uid, gid);
r = connect_journal_socket(fd, context->log_namespace, uid, gid);
if (r < 0)
return r;
@ -1686,7 +1698,7 @@ static int build_environment(
assert(p);
assert(ret);
our_env = new0(char*, 14 + _EXEC_DIRECTORY_TYPE_MAX);
our_env = new0(char*, 15 + _EXEC_DIRECTORY_TYPE_MAX);
if (!our_env)
return -ENOMEM;
@ -1795,6 +1807,14 @@ static int build_environment(
our_env[n_env++] = x;
}
if (c->log_namespace) {
x = strjoin("LOG_NAMESPACE=", c->log_namespace);
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
}
for (t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
_cleanup_free_ char *pre = NULL, *joined = NULL;
const char *n;
@ -1919,6 +1939,9 @@ static bool exec_needs_mount_namespace(
!strv_isempty(context->directories[EXEC_DIRECTORY_LOGS].paths)))
return true;
if (context->log_namespace)
return true;
return false;
}
@ -2517,6 +2540,9 @@ static bool insist_on_sandboxing(
if (!path_equal(bind_mounts[i].source, bind_mounts[i].destination))
return true;
if (context->log_namespace)
return true;
return false;
}
@ -2600,6 +2626,7 @@ static int apply_mount_namespace(
context->n_temporary_filesystems,
tmp,
var,
context->log_namespace,
needs_sandboxing ? context->protect_home : PROTECT_HOME_NO,
needs_sandboxing ? context->protect_system : PROTECT_SYSTEM_NO,
context->mount_flags,
@ -4140,6 +4167,8 @@ void exec_context_done(ExecContext *c) {
c->stdin_data_size = 0;
c->network_namespace_path = mfree(c->network_namespace_path);
c->log_namespace = mfree(c->log_namespace);
}
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
@ -4675,6 +4704,9 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
}
}
if (c->log_namespace)
fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
if (c->secure_bits) {
_cleanup_free_ char *str = NULL;

View file

@ -250,6 +250,8 @@ struct ExecContext {
int log_level_max;
char *log_namespace;
bool private_tmp;
bool private_network;
bool private_devices;

View file

@ -119,6 +119,7 @@ $1.ProtectKernelLogs, config_parse_bool, 0,
$1.ProtectClock, config_parse_bool, 0, offsetof($1, exec_context.protect_clock)
$1.ProtectControlGroups, config_parse_bool, 0, offsetof($1, exec_context.protect_control_groups)
$1.NetworkNamespacePath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.network_namespace_path)
$1.LogNamespace, config_parse_log_namespace, 0, offsetof($1, exec_context)
$1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network)
$1.PrivateUsers, config_parse_bool, 0, offsetof($1, exec_context.private_users)
$1.PrivateMounts, config_parse_bool, 0, offsetof($1, exec_context.private_mounts)

View file

@ -52,10 +52,11 @@
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "syslog-util.h"
#include "time-util.h"
#include "unit-name.h"
#include "unit-printf.h"
#include "user-util.h"
#include "time-util.h"
#include "web-util.h"
static int parse_socket_protocol(const char *s) {
@ -2519,6 +2520,48 @@ int config_parse_log_extra_fields(
}
}
int config_parse_log_namespace(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_free_ char *k = NULL;
ExecContext *c = data;
const Unit *u = userdata;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(c);
if (isempty(rvalue)) {
c->log_namespace = mfree(c->log_namespace);
return 0;
}
r = unit_full_printf(u, rvalue, &k);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
return 0;
}
if (!log_namespace_name_valid(k)) {
log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Specified log namespace name is not valid: %s", k);
return 0;
}
free_and_replace(c->log_namespace, k);
return 0;
}
int config_parse_unit_condition_path(
const char *unit,
const char *filename,

View file

@ -107,6 +107,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_keyring_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_job_timeout_sec);
CONFIG_PARSER_PROTOTYPE(config_parse_job_running_timeout_sec);
CONFIG_PARSER_PROTOTYPE(config_parse_log_extra_fields);
CONFIG_PARSER_PROTOTYPE(config_parse_log_namespace);
CONFIG_PARSER_PROTOTYPE(config_parse_collect_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_pid_file);
CONFIG_PARSER_PROTOTYPE(config_parse_exit_status);

View file

@ -1132,6 +1132,7 @@ static size_t namespace_calculate_mounts(
size_t n_temporary_filesystems,
const char* tmp_dir,
const char* var_tmp_dir,
const char* log_namespace,
ProtectHome protect_home,
ProtectSystem protect_system) {
@ -1166,7 +1167,8 @@ static size_t namespace_calculate_mounts(
(ns_info->protect_control_groups ? 1 : 0) +
protect_home_cnt + protect_system_cnt +
(ns_info->protect_hostname ? 2 : 0) +
(namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0);
(namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0) +
!!log_namespace;
}
static void normalize_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) {
@ -1247,6 +1249,7 @@ int setup_namespace(
size_t n_temporary_filesystems,
const char* tmp_dir,
const char* var_tmp_dir,
const char *log_namespace,
ProtectHome protect_home,
ProtectSystem protect_system,
unsigned long mount_flags,
@ -1323,6 +1326,7 @@ int setup_namespace(
n_bind_mounts,
n_temporary_filesystems,
tmp_dir, var_tmp_dir,
log_namespace,
protect_home, protect_system);
if (n_mounts > 0) {
@ -1428,6 +1432,23 @@ int setup_namespace(
};
}
if (log_namespace) {
_cleanup_free_ char *q;
q = strjoin("/run/systemd/journal.", log_namespace);
if (!q) {
r = -ENOMEM;
goto finish;
}
*(m++) = (MountEntry) {
.path_const = "/run/systemd/journal",
.mode = BIND_MOUNT_RECURSIVE,
.read_only = true,
.source_malloc = TAKE_PTR(q),
};
}
assert(mounts + n_mounts == m);
/* Prepend the root directory where that's necessary */

View file

@ -84,6 +84,7 @@ int setup_namespace(
size_t n_temporary_filesystems,
const char *tmp_dir,
const char *var_tmp_dir,
const char *log_namespace,
ProtectHome protect_home,
ProtectSystem protect_system,
unsigned long mount_flags,

View file

@ -1059,13 +1059,33 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
!IN_SET(c->std_error,
EXEC_OUTPUT_JOURNAL, EXEC_OUTPUT_JOURNAL_AND_CONSOLE,
EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE,
EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE))
EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE) &&
!c->log_namespace)
return 0;
/* If syslog or kernel logging is requested, make sure our own
* logging daemon is run first. */
/* If syslog or kernel logging is requested (or log namespacing is), make sure our own logging daemon
* is run first. */
r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE);
if (c->log_namespace) {
_cleanup_free_ char *socket_unit = NULL, *varlink_socket_unit = NULL;
r = unit_name_build_from_type("systemd-journald", c->log_namespace, UNIT_SOCKET, &socket_unit);
if (r < 0)
return r;
r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, socket_unit, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
r = unit_name_build_from_type("systemd-journald-varlink", c->log_namespace, UNIT_SOCKET, &varlink_socket_unit);
if (r < 0)
return r;
r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, varlink_socket_unit, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
} else
r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;

View file

@ -754,9 +754,13 @@ static int open_journal(sd_journal **j) {
r = sd_journal_open_directory(j, arg_directory, arg_journal_type);
else if (arg_file)
r = sd_journal_open_files(j, (const char**) arg_file, 0);
else if (arg_machine)
else if (arg_machine) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
/* FIXME: replace with D-Bus call OpenMachineRootDirectory() so that things also work with raw disk images */
r = sd_journal_open_container(j, arg_machine, 0);
else
#pragma GCC diagnostic pop
} else
r = sd_journal_open(j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
if (r < 0)
log_error_errno(r, "Failed to open %s: %m",

View file

@ -69,6 +69,7 @@ struct sd_journal {
char *path;
char *prefix;
char *namespace;
OrderedHashmap *files;
IteratedCache *files_cache;

View file

@ -70,36 +70,8 @@
#include "varlink.h"
#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
#define PROCESS_INOTIFY_INTERVAL 1024 /* Every 1,024 messages processed */
#if HAVE_PCRE2
DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, pcre2_match_data_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, pcre2_code_free);
static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) {
int errorcode, r;
PCRE2_SIZE erroroffset;
pcre2_code *p;
p = pcre2_compile((PCRE2_SPTR8) pattern,
PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
if (!p) {
unsigned char buf[LINE_MAX];
r = pcre2_get_error_message(errorcode, buf, sizeof buf);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Bad pattern \"%s\": %s", pattern,
r < 0 ? "unknown error" : (char *)buf);
}
*out = p;
return 0;
}
#endif
enum {
/* Special values for arg_lines */
ARG_LINES_DEFAULT = -2,
@ -143,13 +115,14 @@ static const char *arg_field = NULL;
static bool arg_catalog = false;
static bool arg_reverse = false;
static int arg_journal_type = 0;
static int arg_namespace_flags = 0;
static char *arg_root = NULL;
static const char *arg_machine = NULL;
static const char *arg_namespace = NULL;
static uint64_t arg_vacuum_size = 0;
static uint64_t arg_vacuum_n_files = 0;
static usec_t arg_vacuum_time = 0;
static char **arg_output_fields = NULL;
#if HAVE_PCRE2
static const char *arg_pattern = NULL;
static pcre2_code *arg_compiled_pattern = NULL;
@ -184,6 +157,33 @@ typedef struct BootId {
LIST_FIELDS(struct BootId, boot_list);
} BootId;
#if HAVE_PCRE2
DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, pcre2_match_data_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, pcre2_code_free);
static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) {
int errorcode, r;
PCRE2_SIZE erroroffset;
pcre2_code *p;
p = pcre2_compile((PCRE2_SPTR8) pattern,
PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
if (!p) {
unsigned char buf[LINE_MAX];
r = pcre2_get_error_message(errorcode, buf, sizeof buf);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Bad pattern \"%s\": %s", pattern,
r < 0 ? "unknown error" : (char *)buf);
}
*out = p;
return 0;
}
#endif
static int add_matches_for_device(sd_journal *j, const char *devpath) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
sd_device *d = NULL;
@ -313,9 +313,9 @@ static int help(void) {
if (r < 0)
return log_oom();
printf("%s [OPTIONS...] [MATCHES...]\n\n"
"%sQuery the journal.%s\n\n"
"Options:\n"
printf("%1$s [OPTIONS...] [MATCHES...]\n\n"
"%5$sQuery the journal.%6$s\n\n"
"%3$sOptions:%4$s\n"
" --system Show the system journal\n"
" --user Show the user journal for the current user\n"
" -M --machine=CONTAINER Operate on local container\n"
@ -356,10 +356,11 @@ static int help(void) {
" -D --directory=PATH Show journal files from directory\n"
" --file=PATH Show journal file\n"
" --root=ROOT Operate on files below a root directory\n"
" --namespace=NAMESPACE Show journal data from specified namespace\n"
" --interval=TIME Time interval for changing the FSS sealing key\n"
" --verify-key=KEY Specify FSS verification key\n"
" --force Override of the FSS key pair with --setup-keys\n"
"\nCommands:\n"
"\n%3$sCommands:%4$s\n"
" -h --help Show this help text\n"
" --version Show package version\n"
" -N --fields List all field names currently used\n"
@ -379,10 +380,11 @@ static int help(void) {
" --dump-catalog Show entries in the message catalog\n"
" --update-catalog Update the message catalog database\n"
" --setup-keys Generate a new FSS key pair\n"
"\nSee the %s for details.\n"
"\nSee the %2$s for details.\n"
, program_invocation_short_name
, ansi_highlight(), ansi_normal()
, link
, ansi_underline(), ansi_normal()
, ansi_highlight(), ansi_normal()
);
return 0;
@ -428,6 +430,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VACUUM_TIME,
ARG_NO_HOSTNAME,
ARG_OUTPUT_FIELDS,
ARG_NAMESPACE,
};
static const struct option options[] = {
@ -492,6 +495,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME },
{ "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME },
{ "output-fields", required_argument, NULL, ARG_OUTPUT_FIELDS },
{ "namespace", required_argument, NULL, ARG_NAMESPACE },
{}
};
@ -533,10 +537,8 @@ static int parse_argv(int argc, char *argv[]) {
}
arg_output = output_mode_from_string(optarg);
if (arg_output < 0) {
log_error("Unknown output format '%s'.", optarg);
return -EINVAL;
}
if (arg_output < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown output format '%s'.", optarg);
if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_JSON_SEQ, OUTPUT_CAT))
arg_quiet = true;
@ -561,10 +563,8 @@ static int parse_argv(int argc, char *argv[]) {
arg_lines = ARG_LINES_ALL;
else {
r = safe_atoi(optarg, &arg_lines);
if (r < 0 || arg_lines < 0) {
log_error("Failed to parse lines '%s'", optarg);
return -EINVAL;
}
if (r < 0 || arg_lines < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse lines '%s'", optarg);
}
} else {
arg_lines = 10;
@ -656,6 +656,23 @@ static int parse_argv(int argc, char *argv[]) {
arg_machine = optarg;
break;
case ARG_NAMESPACE:
if (streq(optarg, "*")) {
arg_namespace_flags = SD_JOURNAL_ALL_NAMESPACES;
arg_namespace = NULL;
} else if (startswith(optarg, "+")) {
arg_namespace_flags = SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE;
arg_namespace = optarg + 1;
} else if (isempty(optarg)) {
arg_namespace_flags = 0;
arg_namespace = NULL;
} else {
arg_namespace_flags = 0;
arg_namespace = optarg;
}
break;
case 'D':
arg_directory = optarg;
break;
@ -710,30 +727,24 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_VACUUM_SIZE:
r = parse_size(optarg, 1024, &arg_vacuum_size);
if (r < 0) {
log_error("Failed to parse vacuum size: %s", optarg);
return r;
}
if (r < 0)
return log_error_errno(r, "Failed to parse vacuum size: %s", optarg);
arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
case ARG_VACUUM_FILES:
r = safe_atou64(optarg, &arg_vacuum_n_files);
if (r < 0) {
log_error("Failed to parse vacuum files: %s", optarg);
return r;
}
if (r < 0)
return log_error_errno(r, "Failed to parse vacuum files: %s", optarg);
arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
case ARG_VACUUM_TIME:
r = parse_sec(optarg, &arg_vacuum_time);
if (r < 0) {
log_error("Failed to parse vacuum time: %s", optarg);
return r;
}
if (r < 0)
return log_error_errno(r, "Failed to parse vacuum time: %s", optarg);
arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
@ -748,7 +759,6 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_VERIFY_KEY:
arg_action = ACTION_VERIFY;
r = free_and_strdup(&arg_verify_key, optarg);
if (r < 0)
return r;
@ -756,23 +766,23 @@ static int parse_argv(int argc, char *argv[]) {
* in ps or htop output. */
memset(optarg, 'x', strlen(optarg));
arg_action = ACTION_VERIFY;
arg_merge = false;
break;
case ARG_INTERVAL:
r = parse_sec(optarg, &arg_interval);
if (r < 0 || arg_interval <= 0) {
log_error("Failed to parse sealing key change interval: %s", optarg);
return -EINVAL;
}
if (r < 0 || arg_interval <= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Failed to parse sealing key change interval: %s", optarg);
break;
#else
case ARG_SETUP_KEYS:
case ARG_VERIFY_KEY:
case ARG_INTERVAL:
case ARG_FORCE:
log_error("Compiled without forward-secure sealing support.");
return -EOPNOTSUPP;
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Compiled without forward-secure sealing support.");
#endif
case 'p': {
@ -780,7 +790,7 @@ static int parse_argv(int argc, char *argv[]) {
dots = strstr(optarg, "..");
if (dots) {
char *a;
_cleanup_free_ char *a = NULL;
int from, to, i;
/* a range */
@ -790,12 +800,10 @@ static int parse_argv(int argc, char *argv[]) {
from = log_level_from_string(a);
to = log_level_from_string(dots + 2);
free(a);
if (from < 0 || to < 0) {
log_error("Failed to parse log level range %s", optarg);
return -EINVAL;
}
if (from < 0 || to < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Failed to parse log level range %s", optarg);
arg_priorities = 0;
@ -811,10 +819,9 @@ static int parse_argv(int argc, char *argv[]) {
int p, i;
p = log_level_from_string(optarg);
if (p < 0) {
log_error("Unknown log level %s", optarg);
return -EINVAL;
}
if (p < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Unknown log level %s", optarg);
arg_priorities = 0;
@ -848,19 +855,17 @@ static int parse_argv(int argc, char *argv[]) {
case 'S':
r = parse_timestamp(optarg, &arg_since);
if (r < 0) {
log_error("Failed to parse timestamp: %s", optarg);
return -EINVAL;
}
if (r < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Failed to parse timestamp: %s", optarg);
arg_since_set = true;
break;
case 'U':
r = parse_timestamp(optarg, &arg_until);
if (r < 0) {
log_error("Failed to parse timestamp: %s", optarg);
return -EINVAL;
}
if (r < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Failed to parse timestamp: %s", optarg);
arg_until_set = true;
break;
@ -1417,7 +1422,6 @@ static int add_boot(sd_journal *j) {
* so take the slow path if log location is specified. */
if (arg_boot_offset == 0 && sd_id128_is_null(arg_boot_id) &&
!arg_directory && !arg_file && !arg_root)
return add_match_this_boot(j, arg_machine);
boot_id = arg_boot_id;
@ -1944,15 +1948,19 @@ static int verify(sd_journal *j) {
static int simple_varlink_call(const char *option, const char *method) {
_cleanup_(varlink_flush_close_unrefp) Varlink *link = NULL;
const char *error;
const char *error, *fn;
int r;
if (arg_machine)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "%s is not supported in conjunction with --machine=.", option);
r = varlink_connect_address(&link, "/run/systemd/journal/io.systemd.journal");
fn = arg_namespace ?
strjoina("/run/systemd/journal.", arg_namespace, "/io.systemd.journal") :
"/run/systemd/journal/io.systemd.journal";
r = varlink_connect_address(&link, fn);
if (r < 0)
return log_error_errno(r, "Failed to connect to /run/systemd/journal/io.systemd.journal: %m");
return log_error_errno(r, "Failed to connect to %s: %m", fn);
(void) varlink_set_description(link, "journal");
(void) varlink_set_relative_timeout(link, USEC_INFINITY);
@ -2122,10 +2130,9 @@ int main(int argc, char *argv[]) {
r = sd_journal_open_directory(&j, arg_directory, arg_journal_type);
else if (arg_root)
r = sd_journal_open_directory(&j, arg_root, arg_journal_type | SD_JOURNAL_OS_ROOT);
else if (arg_file_stdin) {
int ifd = STDIN_FILENO;
r = sd_journal_open_files_fd(&j, &ifd, 1, 0);
} else if (arg_file)
else if (arg_file_stdin)
r = sd_journal_open_files_fd(&j, (int[]) { STDIN_FILENO }, 1, 0);
else if (arg_file)
r = sd_journal_open_files(&j, (const char**) arg_file, 0);
else if (arg_machine) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@ -2136,8 +2143,7 @@ int main(int argc, char *argv[]) {
if (geteuid() != 0) {
/* The file descriptor returned by OpenMachineRootDirectory() will be owned by users/groups of
* the container, thus we need root privileges to override them. */
log_error("Using the --machine= switch requires root privileges.");
r = -EPERM;
r = log_error_errno(SYNTHETIC_ERRNO(EPERM), "Using the --machine= switch requires root privileges.");
goto finish;
}
@ -2177,7 +2183,11 @@ int main(int argc, char *argv[]) {
if (r < 0)
safe_close(fd);
} else
r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
r = sd_journal_open_namespace(
&j,
arg_namespace,
(arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY) |
arg_namespace_flags | arg_journal_type);
if (r < 0) {
log_error_errno(r, "Failed to open %s: %m", arg_directory ?: arg_file ? "files" : "journal");
goto finish;

View file

@ -117,23 +117,24 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
if (r < 0)
return r;
c = new0(ClientContext, 1);
c = new(ClientContext, 1);
if (!c)
return -ENOMEM;
c->pid = pid;
c->uid = UID_INVALID;
c->gid = GID_INVALID;
c->auditid = AUDIT_SESSION_INVALID;
c->loginuid = UID_INVALID;
c->owner_uid = UID_INVALID;
c->lru_index = PRIOQ_IDX_NULL;
c->timestamp = USEC_INFINITY;
c->extra_fields_mtime = NSEC_INFINITY;
c->log_level_max = -1;
c->log_ratelimit_interval = s->ratelimit_interval;
c->log_ratelimit_burst = s->ratelimit_burst;
*c = (ClientContext) {
.pid = pid,
.uid = UID_INVALID,
.gid = GID_INVALID,
.auditid = AUDIT_SESSION_INVALID,
.loginuid = UID_INVALID,
.owner_uid = UID_INVALID,
.lru_index = PRIOQ_IDX_NULL,
.timestamp = USEC_INFINITY,
.extra_fields_mtime = NSEC_INFINITY,
.log_level_max = -1,
.log_ratelimit_interval = s->ratelimit_interval,
.log_ratelimit_burst = s->ratelimit_burst,
};
r = hashmap_put(s->client_contexts, PID_TO_PTR(pid), c);
if (r < 0) {
@ -779,7 +780,9 @@ void client_context_acquire_default(Server *s) {
log_warning_errno(r, "Failed to acquire our own context, ignoring: %m");
}
if (!s->pid1_context) {
if (!s->namespace && !s->pid1_context) {
/* Acquire PID1's context, but only if we are in non-namespaced mode, since PID 1 is only
* going to log to the non-namespaced journal instance. */
r = client_context_acquire(s, 1, NULL, NULL, 0, NULL, &s->pid1_context);
if (r < 0)

View file

@ -24,11 +24,11 @@
#include "string-util.h"
void server_forward_kmsg(
Server *s,
int priority,
const char *identifier,
const char *message,
const struct ucred *ucred) {
Server *s,
int priority,
const char *identifier,
const char *message,
const struct ucred *ucred) {
_cleanup_free_ char *ident_buf = NULL;
struct iovec iovec[5];
@ -416,19 +416,23 @@ fail:
}
int server_open_kernel_seqnum(Server *s) {
_cleanup_close_ int fd;
_cleanup_close_ int fd = -1;
const char *fn;
uint64_t *p;
int r;
assert(s);
/* We store the seqnum we last read in an mmaped file. That
* way we can just use it like a variable, but it is
* persistent and automatically flushed at reboot. */
/* We store the seqnum we last read in an mmaped file. That way we can just use it like a variable,
* but it is persistent and automatically flushed at reboot. */
fd = open("/run/systemd/journal/kernel-seqnum", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644);
if (!s->read_kmsg)
return 0;
fn = strjoina(s->runtime_directory, "/kernel-seqnum");
fd = open(fn, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644);
if (fd < 0) {
log_error_errno(errno, "Failed to open /run/systemd/journal/kernel-seqnum, ignoring: %m");
log_error_errno(errno, "Failed to open %s, ignoring: %m", fn);
return 0;
}

View file

@ -450,17 +450,21 @@ void server_process_native_file(
}
}
int server_open_native_socket(Server *s) {
static const union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/journal/socket",
};
int server_open_native_socket(Server *s, const char *native_socket) {
int r;
assert(s);
assert(native_socket);
if (s->native_fd < 0) {
union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
};
r = sockaddr_un_set_path(&sa.un, native_socket);
if (r < 0)
return log_error_errno(r, "Unable to use namespace path %s for AF_UNIX socket: %m", native_socket);
s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (s->native_fd < 0)
return log_error_errno(errno, "socket() failed: %m");

View file

@ -20,4 +20,4 @@ void server_process_native_file(
const char *label,
size_t label_len);
int server_open_native_socket(Server *s);
int server_open_native_socket(Server *s, const char *native_socket);

View file

@ -99,7 +99,7 @@ void journal_ratelimit_free(JournalRateLimit *r) {
free(r);
}
_pure_ static bool journal_ratelimit_group_expired(JournalRateLimitGroup *g, usec_t ts) {
static bool journal_ratelimit_group_expired(JournalRateLimitGroup *g, usec_t ts) {
unsigned i;
assert(g);

File diff suppressed because it is too large Load diff

View file

@ -60,6 +60,8 @@ typedef struct JournalStorage {
} JournalStorage;
struct Server {
char *namespace;
int syslog_fd;
int native_fd;
int stdout_fd;
@ -84,6 +86,7 @@ struct Server {
sd_event_source *hostname_event_source;
sd_event_source *notify_event_source;
sd_event_source *watchdog_event_source;
sd_event_source *idle_event_source;
JournalFile *runtime_journal;
JournalFile *system_journal;
@ -147,6 +150,8 @@ struct Server {
char machine_id_field[sizeof("_MACHINE_ID=") + 32];
char boot_id_field[sizeof("_BOOT_ID=") + 32];
char *hostname_field;
char *namespace_field;
char *runtime_directory;
/* Cached cgroup root, so that we don't have to query that all the time */
char *cgroup_root;
@ -172,7 +177,7 @@ struct Server {
#define SERVER_MACHINE_ID(s) ((s)->machine_id_field + STRLEN("_MACHINE_ID="))
/* Extra fields for any log messages */
#define N_IOVEC_META_FIELDS 22
#define N_IOVEC_META_FIELDS 23
/* Extra fields for log messages that contain OBJECT_PID= (i.e. log about another process) */
#define N_IOVEC_OBJECT_FIELDS 18
@ -204,7 +209,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_split_mode);
const char *split_mode_to_string(SplitMode s) _const_;
SplitMode split_mode_from_string(const char *s) _pure_;
int server_init(Server *s);
int server_init(Server *s, const char *namespace);
void server_done(Server *s);
void server_sync(Server *s);
int server_vacuum(Server *s, bool verbose);
@ -214,3 +219,6 @@ int server_flush_to_var(Server *s, bool require_flag_file);
void server_maybe_append_tags(Server *s);
int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata);
void server_space_usage_message(Server *s, JournalStorage *storage);
int server_start_or_stop_idle_timer(Server *s);
int server_refresh_idle_timer(Server *s);

View file

@ -17,6 +17,7 @@
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "io-util.h"
#include "journald-console.h"
#include "journald-context.h"
@ -109,6 +110,8 @@ void stdout_stream_free(StdoutStream *s) {
if (s->in_notify_queue)
LIST_REMOVE(stdout_stream_notify_queue, s->server->stdout_streams_notify_queue, s);
(void) server_start_or_stop_idle_timer(s->server); /* Maybe we are idle now? */
}
if (s->event_source) {
@ -139,7 +142,7 @@ void stdout_stream_destroy(StdoutStream *s) {
}
static int stdout_stream_save(StdoutStream *s) {
_cleanup_free_ char *temp_path = NULL;
_cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
@ -156,11 +159,12 @@ static int stdout_stream_save(StdoutStream *s) {
return log_warning_errno(errno, "Failed to stat connected stream: %m");
/* We use device and inode numbers as identifier for the stream */
if (asprintf(&s->state_file, "/run/systemd/journal/streams/%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino) < 0)
r = asprintf(&s->state_file, "%s/streams/%lu:%lu", s->server->runtime_directory, (unsigned long) st.st_dev, (unsigned long) st.st_ino);
if (r < 0)
return log_oom();
}
(void) mkdir_p("/run/systemd/journal/streams", 0755);
(void) mkdir_parents(s->state_file, 0755);
r = fopen_temporary(s->state_file, &f, &temp_path);
if (r < 0)
@ -214,6 +218,8 @@ static int stdout_stream_save(StdoutStream *s) {
goto fail;
}
temp_path = mfree(temp_path);
if (!s->fdstore && !s->in_notify_queue) {
LIST_PREPEND(stdout_stream_notify_queue, s->server->stdout_streams_notify_queue, s);
s->in_notify_queue = true;
@ -229,10 +235,6 @@ static int stdout_stream_save(StdoutStream *s) {
fail:
(void) unlink(s->state_file);
if (temp_path)
(void) unlink(temp_path);
return log_error_errno(r, "Failed to save stream data %s: %m", s->state_file);
}
@ -590,12 +592,14 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
if (r < 0)
return log_error_errno(r, "Failed to generate stream ID: %m");
stream = new0(StdoutStream, 1);
stream = new(StdoutStream, 1);
if (!stream)
return log_oom();
stream->fd = -1;
stream->priority = LOG_INFO;
*stream = (StdoutStream) {
.fd = -1,
.priority = LOG_INFO,
};
xsprintf(stream->id_field, "_STREAM_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id));
@ -629,11 +633,12 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
LIST_PREPEND(stdout_stream, s->stdout_streams, stream);
s->n_stdout_streams++;
(void) server_start_or_stop_idle_timer(s); /* Maybe no longer idle? */
if (ret)
*ret = stream;
stream = NULL;
TAKE_PTR(stream);
return 0;
}
@ -676,7 +681,7 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
if (r < 0)
return r;
fd = -1;
TAKE_FD(fd);
return 0;
}
@ -694,7 +699,7 @@ static int stdout_stream_load(StdoutStream *stream, const char *fname) {
assert(fname);
if (!stream->state_file) {
stream->state_file = path_join("/run/systemd/journal/streams", fname);
stream->state_file = path_join(stream->server->runtime_directory, "streams", fname);
if (!stream->state_file)
return log_oom();
}
@ -783,14 +788,16 @@ static int stdout_stream_restore(Server *s, const char *fname, int fd) {
int server_restore_streams(Server *s, FDSet *fds) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
const char *path;
int r;
d = opendir("/run/systemd/journal/streams");
path = strjoina(s->runtime_directory, "/streams");
d = opendir(path);
if (!d) {
if (errno == ENOENT)
return 0;
return log_warning_errno(errno, "Failed to enumerate /run/systemd/journal/streams: %m");
return log_warning_errno(errno, "Failed to enumerate %s: %m", path);
}
FOREACH_DIRENT(de, d, goto fail) {
@ -818,8 +825,7 @@ int server_restore_streams(Server *s, FDSet *fds) {
/* No file descriptor? Then let's delete the state file */
log_debug("Cannot restore stream file %s", de->d_name);
if (unlinkat(dirfd(d), de->d_name, 0) < 0)
log_warning_errno(errno, "Failed to remove /run/systemd/journal/streams/%s: %m",
de->d_name);
log_warning_errno(errno, "Failed to remove %s%s: %m", path, de->d_name);
continue;
}
@ -836,16 +842,21 @@ fail:
return log_error_errno(errno, "Failed to read streams directory: %m");
}
int server_open_stdout_socket(Server *s) {
static const union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/journal/stdout",
};
int server_open_stdout_socket(Server *s, const char *stdout_socket) {
int r;
assert(s);
assert(stdout_socket);
if (s->stdout_fd < 0) {
union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
};
r = sockaddr_un_set_path(&sa.un, stdout_socket);
if (r < 0)
return log_error_errno(r, "Unable to use namespace path %s for AF_UNIX socket: %m", stdout_socket);
s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (s->stdout_fd < 0)
return log_error_errno(errno, "socket() failed: %m");

View file

@ -6,7 +6,7 @@ typedef struct StdoutStream StdoutStream;
#include "fdset.h"
#include "journald-server.h"
int server_open_stdout_socket(Server *s);
int server_open_stdout_socket(Server *s, const char *stdout_socket);
int server_restore_streams(Server *s, FDSet *fds);
void stdout_stream_free(StdoutStream *s);

View file

@ -25,11 +25,15 @@
/* Warn once every 30s if we missed syslog message */
#define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) {
static void forward_syslog_iovec(
Server *s,
const struct iovec *iovec,
unsigned n_iovec,
const struct ucred *ucred,
const struct timeval *tv) {
static const union sockaddr_union sa = {
union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/journal/syslog",
};
struct msghdr msghdr = {
.msg_iov = (struct iovec *) iovec,
@ -42,11 +46,20 @@ static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
} control;
const char *j;
int r;
assert(s);
assert(iovec);
assert(n_iovec > 0);
j = strjoina(s->runtime_directory, "/syslog");
r = sockaddr_un_set_path(&sa.un, j);
if (r < 0) {
log_debug_errno(r, "Forwarding socket path %s too long for AF_UNIX, not forwarding: %m", j);
return;
}
if (ucred) {
zero(control);
msghdr.msg_control = &control;
@ -441,17 +454,21 @@ void server_process_syslog_message(
server_dispatch_message(s, iovec, n, m, context, tv, priority, 0);
}
int server_open_syslog_socket(Server *s) {
static const union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/journal/dev-log",
};
int server_open_syslog_socket(Server *s, const char *syslog_socket) {
int r;
assert(s);
assert(syslog_socket);
if (s->syslog_fd < 0) {
union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
};
r = sockaddr_un_set_path(&sa.un, syslog_socket);
if (r < 0)
return log_error_errno(r, "Unable to use namespace path %s for AF_UNIX socket: %m", syslog_socket);
s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (s->syslog_fd < 0)
return log_error_errno(errno, "socket() failed: %m");

View file

@ -10,6 +10,6 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid);
void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv);
void server_process_syslog_message(Server *s, const char *buf, size_t buf_len, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len);
int server_open_syslog_socket(Server *s);
int server_open_syslog_socket(Server *s, const char *syslog_socket);
void server_maybe_warn_forward_syslog_missed(Server *s);

View file

@ -14,14 +14,17 @@
#include "sigbus.h"
int main(int argc, char *argv[]) {
const char *namespace;
Server server;
int r;
if (argc > 1) {
log_error("This program does not take arguments.");
if (argc > 2) {
log_error("This program takes one or no arguments.");
return EXIT_FAILURE;
}
namespace = argc > 1 ? empty_to_null(argv[1]) : NULL;
log_set_prohibit_ipc(true);
log_set_target(LOG_TARGET_AUTO);
log_set_facility(LOG_SYSLOG);
@ -32,7 +35,7 @@ int main(int argc, char *argv[]) {
sigbus_install();
r = server_init(&server);
r = server_init(&server, namespace);
if (r < 0)
goto finish;
@ -40,7 +43,11 @@ int main(int argc, char *argv[]) {
server_flush_to_var(&server, true);
server_flush_dev_kmsg(&server);
log_debug("systemd-journald running as pid "PID_FMT, getpid_cached());
if (server.namespace)
log_debug("systemd-journald running as PID "PID_FMT" for namespace '%s'.", getpid_cached(), server.namespace ?: "<system>");
else
log_debug("systemd-journald running as PID "PID_FMT" for the system.", getpid_cached());
server_driver_message(&server, 0,
"MESSAGE_ID=" SD_MESSAGE_JOURNAL_START_STR,
LOG_MESSAGE("Journal started"),
@ -55,8 +62,10 @@ int main(int argc, char *argv[]) {
usec_t t = USEC_INFINITY, n;
r = sd_event_get_state(server.event);
if (r < 0)
if (r < 0) {
log_error_errno(r, "Failed to get event loop state: %m");
goto finish;
}
if (r == SD_EVENT_FINISHED)
break;
@ -99,7 +108,11 @@ int main(int argc, char *argv[]) {
server_maybe_warn_forward_syslog_missed(&server);
}
log_debug("systemd-journald stopped as pid "PID_FMT, getpid_cached());
if (server.namespace)
log_debug("systemd-journald stopped as PID "PID_FMT" for namespace '%s'.", getpid_cached(), server.namespace ?: "<system>");
else
log_debug("systemd-journald stopped as PID "PID_FMT" for the system.", getpid_cached());
server_driver_message(&server, 0,
"MESSAGE_ID=" SD_MESSAGE_JOURNAL_STOP_STR,
LOG_MESSAGE("Journal stopped"),

View file

@ -39,6 +39,7 @@
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "syslog-util.h"
#define JOURNAL_FILES_MAX 7168
@ -204,16 +205,17 @@ static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
static Match *match_new(Match *p, MatchType t) {
Match *m;
m = new0(Match, 1);
m = new(Match, 1);
if (!m)
return NULL;
m->type = t;
*m = (Match) {
.type = t,
.parent = p,
};
if (p) {
m->parent = p;
if (p)
LIST_PREPEND(matches, p->matches, m);
}
return m;
}
@ -1434,22 +1436,63 @@ static void remove_file_real(sd_journal *j, JournalFile *f) {
static int dirname_is_machine_id(const char *fn) {
sd_id128_t id, machine;
const char *e;
int r;
/* Returns true if the specified directory name matches the local machine ID */
r = sd_id128_get_machine(&machine);
if (r < 0)
return r;
r = sd_id128_from_string(fn, &id);
e = strchr(fn, '.');
if (e) {
const char *k;
/* Looks like it has a namespace suffix. Verify that. */
if (!log_namespace_name_valid(e + 1))
return false;
k = strndupa(fn, e - fn);
r = sd_id128_from_string(k, &id);
} else
r = sd_id128_from_string(fn, &id);
if (r < 0)
return r;
return sd_id128_equal(id, machine);
}
static int dirname_has_namespace(const char *fn, const char *namespace) {
const char *e;
/* Returns true if the specified directory name matches the specified namespace */
e = strchr(fn, '.');
if (e) {
const char *k;
if (!namespace)
return false;
if (!streq(e + 1, namespace))
return false;
k = strndupa(fn, e - fn);
return id128_is_valid(k);
}
if (namespace)
return false;
return id128_is_valid(fn);
}
static bool dirent_is_journal_file(const struct dirent *de) {
assert(de);
/* Returns true if the specified directory entry looks like a journal file we might be interested in */
if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
return false;
@ -1457,13 +1500,26 @@ static bool dirent_is_journal_file(const struct dirent *de) {
endswith(de->d_name, ".journal~");
}
static bool dirent_is_id128_subdir(const struct dirent *de) {
static bool dirent_is_journal_subdir(const struct dirent *de) {
const char *e, *n;
assert(de);
/* returns true if the specified directory entry looks like a directory that might contain journal
* files we might be interested in, i.e. is either a 128bit ID or a 128bit ID suffixed by a
* namespace. */
if (!IN_SET(de->d_type, DT_DIR, DT_LNK, DT_UNKNOWN))
return false;
return id128_is_valid(de->d_name);
e = strchr(de->d_name, '.');
if (!e)
return id128_is_valid(de->d_name); /* No namespace */
n = strndupa(de->d_name, e - de->d_name);
if (!id128_is_valid(n))
return false;
return log_namespace_name_valid(e + 1);
}
static int directory_open(sd_journal *j, const char *path, DIR **ret) {
@ -1500,7 +1556,7 @@ static void directory_enumerate(sd_journal *j, Directory *m, DIR *d) {
if (dirent_is_journal_file(de))
(void) add_file_by_name(j, m->path, de->d_name);
if (m->is_root && dirent_is_id128_subdir(de))
if (m->is_root && dirent_is_journal_subdir(de))
(void) add_directory(j, m->path, de->d_name);
}
@ -1540,7 +1596,11 @@ static void directory_watch(sd_journal *j, Directory *m, int fd, uint32_t mask)
}
}
static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
static int add_directory(
sd_journal *j,
const char *prefix,
const char *dirname) {
_cleanup_free_ char *path = NULL;
_cleanup_closedir_ DIR *d = NULL;
Directory *m;
@ -1565,6 +1625,11 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
!((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run")))
return 0;
if (!(FLAGS_SET(j->flags, SD_JOURNAL_ALL_NAMESPACES) ||
dirname_has_namespace(dirname, j->namespace) > 0 ||
(FLAGS_SET(j->flags, SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE) && dirname_has_namespace(dirname, NULL) > 0)))
return 0;
r = directory_open(j, path, &d);
if (r < 0) {
log_debug_errno(r, "Failed to open directory '%s': %m", path);
@ -1573,14 +1638,16 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
m = hashmap_get(j->directories_by_path, path);
if (!m) {
m = new0(Directory, 1);
m = new(Directory, 1);
if (!m) {
r = -ENOMEM;
goto fail;
}
m->is_root = false;
m->path = path;
*m = (Directory) {
.is_root = false,
.path = path,
};
if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
free(m);
@ -1803,7 +1870,7 @@ static int allocate_inotify(sd_journal *j) {
return hashmap_ensure_allocated(&j->directories_by_wd, NULL);
}
static sd_journal *journal_new(int flags, const char *path) {
static sd_journal *journal_new(int flags, const char *path, const char *namespace) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
j = new0(sd_journal, 1);
@ -1829,6 +1896,12 @@ static sd_journal *journal_new(int flags, const char *path) {
j->path = t;
}
if (namespace) {
j->namespace = strdup(namespace);
if (!j->namespace)
return NULL;
}
j->files = ordered_hashmap_new(&path_hash_ops);
if (!j->files)
return NULL;
@ -1845,16 +1918,19 @@ static sd_journal *journal_new(int flags, const char *path) {
#define OPEN_ALLOWED_FLAGS \
(SD_JOURNAL_LOCAL_ONLY | \
SD_JOURNAL_RUNTIME_ONLY | \
SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)
SD_JOURNAL_SYSTEM | \
SD_JOURNAL_CURRENT_USER | \
SD_JOURNAL_ALL_NAMESPACES | \
SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE)
_public_ int sd_journal_open(sd_journal **ret, int flags) {
_public_ int sd_journal_open_namespace(sd_journal **ret, const char *namespace, int flags) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
int r;
assert_return(ret, -EINVAL);
assert_return((flags & ~OPEN_ALLOWED_FLAGS) == 0, -EINVAL);
j = journal_new(flags, NULL);
j = journal_new(flags, NULL, namespace);
if (!j)
return -ENOMEM;
@ -1866,6 +1942,10 @@ _public_ int sd_journal_open(sd_journal **ret, int flags) {
return 0;
}
_public_ int sd_journal_open(sd_journal **ret, int flags) {
return sd_journal_open_namespace(ret, NULL, flags);
}
#define OPEN_CONTAINER_ALLOWED_FLAGS \
(SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM)
@ -1875,7 +1955,7 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
char *p;
int r;
/* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in
/* This is deprecated, people should use machined's OpenMachineRootDirectory() call instead in
* combination with sd_journal_open_directory_fd(). */
assert_return(machine, -EINVAL);
@ -1897,7 +1977,7 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
if (!streq_ptr(class, "container"))
return -EIO;
j = journal_new(flags, root);
j = journal_new(flags, root, NULL);
if (!j)
return -ENOMEM;
@ -1921,7 +2001,7 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f
assert_return(path, -EINVAL);
assert_return((flags & ~OPEN_DIRECTORY_ALLOWED_FLAGS) == 0, -EINVAL);
j = journal_new(flags, path);
j = journal_new(flags, path, NULL);
if (!j)
return -ENOMEM;
@ -1944,7 +2024,7 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla
assert_return(ret, -EINVAL);
assert_return(flags == 0, -EINVAL);
j = journal_new(flags, NULL);
j = journal_new(flags, NULL, NULL);
if (!j)
return -ENOMEM;
@ -1979,7 +2059,7 @@ _public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
if (!S_ISDIR(st.st_mode))
return -EBADFD;
j = journal_new(flags, NULL);
j = journal_new(flags, NULL, NULL);
if (!j)
return -ENOMEM;
@ -2007,7 +2087,7 @@ _public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fd
assert_return(n_fds > 0, -EBADF);
assert_return(flags == 0, -EINVAL);
j = journal_new(flags, NULL);
j = journal_new(flags, NULL, NULL);
if (!j)
return -ENOMEM;
@ -2079,6 +2159,7 @@ _public_ void sd_journal_close(sd_journal *j) {
free(j->path);
free(j->prefix);
free(j->namespace);
free(j->unique_field);
free(j->fields_buffer);
free(j);
@ -2480,7 +2561,7 @@ static void process_q_overflow(sd_journal *j) {
log_debug("Reiteration complete.");
}
static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
static void process_inotify_event(sd_journal *j, const struct inotify_event *e) {
Directory *d;
assert(j);

View file

@ -693,4 +693,5 @@ global:
sd_event_source_get_child_process_own;
sd_event_source_set_child_process_own;
sd_event_source_send_child_signal;
sd_journal_open_namespace;
} LIBSYSTEMD_243;

View file

@ -576,6 +576,7 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li
show_journal_by_unit(
stdout,
i.scope,
NULL,
arg_output,
0,
i.timestamp.monotonic,
@ -660,6 +661,7 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line)
show_journal_by_unit(
stdout,
i.slice,
NULL,
arg_output,
0,
i.timestamp.monotonic,

View file

@ -626,6 +626,7 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
show_journal_by_unit(
stdout,
i->unit,
NULL,
arg_output,
0,
i->timestamp.monotonic,

View file

@ -833,7 +833,8 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
"RuntimeDirectoryPreserve",
"Personality",
"KeyringMode",
"NetworkNamespacePath"))
"NetworkNamespacePath",
"LogNamespace"))
return bus_append_string(m, field, eq);
if (STR_IN_SET(field, "IgnoreSIGPIPE",

View file

@ -1453,6 +1453,7 @@ int add_match_this_boot(sd_journal *j, const char *machine) {
int show_journal_by_unit(
FILE *f,
const char *unit,
const char *log_namespace,
OutputMode mode,
unsigned n_columns,
usec_t not_before,
@ -1473,7 +1474,7 @@ int show_journal_by_unit(
if (how_many <= 0)
return 0;
r = sd_journal_open(&j, journal_open_flags);
r = sd_journal_open_namespace(&j, log_namespace, journal_open_flags | SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE);
if (r < 0)
return log_error_errno(r, "Failed to open journal: %m");

View file

@ -46,6 +46,7 @@ int add_matches_for_user_unit(
int show_journal_by_unit(
FILE *f,
const char *unit,
const char *namespace,
OutputMode mode,
unsigned n_columns,
usec_t not_before,

View file

@ -168,6 +168,7 @@ struct VarlinkServer {
Hashmap *methods;
VarlinkConnect connect_callback;
VarlinkDisconnect disconnect_callback;
sd_event *event;
int64_t event_priority;
@ -1146,6 +1147,7 @@ int varlink_flush(Varlink *v) {
}
static void varlink_detach_server(Varlink *v) {
VarlinkServer *saved_server;
assert(v);
if (!v->server)
@ -1169,8 +1171,15 @@ static void varlink_detach_server(Varlink *v) {
v->server->n_connections--;
/* If this is a connection associated to a server, then let's disconnect the server and the
* connection from each other. This drops the dangling reference that connect_callback() set up. */
v->server = varlink_server_unref(v->server);
* connection from each other. This drops the dangling reference that connect_callback() set up. But
* before we release the references, let's call the disconnection callback if it is defined. */
saved_server = TAKE_PTR(v->server);
if (saved_server->disconnect_callback)
saved_server->disconnect_callback(saved_server, v, saved_server->userdata);
varlink_server_unref(saved_server);
varlink_unref(v);
}
@ -2413,6 +2422,16 @@ int varlink_server_bind_connect(VarlinkServer *s, VarlinkConnect callback) {
return 0;
}
int varlink_server_bind_disconnect(VarlinkServer *s, VarlinkDisconnect callback) {
assert_return(s, -EINVAL);
if (callback && s->disconnect_callback && callback != s->disconnect_callback)
return -EBUSY;
s->disconnect_callback = callback;
return 0;
}
unsigned varlink_server_connections_max(VarlinkServer *s) {
int dts;
@ -2460,6 +2479,12 @@ int varlink_server_set_connections_max(VarlinkServer *s, unsigned m) {
return 0;
}
unsigned varlink_server_current_connections(VarlinkServer *s) {
assert_return(s, UINT_MAX);
return s->n_connections;
}
int varlink_server_set_description(VarlinkServer *s, const char *description) {
assert_return(s, -EINVAL);

View file

@ -51,6 +51,7 @@ typedef enum VarlinkServerFlags {
typedef int (*VarlinkMethod)(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata);
typedef int (*VarlinkReply)(Varlink *link, JsonVariant *parameters, const char *error_id, VarlinkReplyFlags flags, void *userdata);
typedef int (*VarlinkConnect)(VarlinkServer *server, Varlink *link, void *userdata);
typedef void (*VarlinkDisconnect)(VarlinkServer *server, Varlink *link, void *userdata);
int varlink_connect_address(Varlink **ret, const char *address);
int varlink_connect_fd(Varlink **ret, int fd);
@ -134,6 +135,7 @@ int varlink_server_bind_method(VarlinkServer *s, const char *method, VarlinkMeth
int varlink_server_bind_method_many_internal(VarlinkServer *s, ...);
#define varlink_server_bind_method_many(s, ...) varlink_server_bind_method_many_internal(s, __VA_ARGS__, NULL)
int varlink_server_bind_connect(VarlinkServer *s, VarlinkConnect connect);
int varlink_server_bind_disconnect(VarlinkServer *s, VarlinkDisconnect disconnect);
void* varlink_server_set_userdata(VarlinkServer *s, void *userdata);
void* varlink_server_get_userdata(VarlinkServer *s);
@ -150,6 +152,8 @@ unsigned varlink_server_connections_per_uid_max(VarlinkServer *s);
int varlink_server_set_connections_per_uid_max(VarlinkServer *s, unsigned m);
int varlink_server_set_connections_max(VarlinkServer *s, unsigned m);
unsigned varlink_server_current_connections(VarlinkServer *s);
int varlink_server_set_description(VarlinkServer *s, const char *description);
DEFINE_TRIVIAL_CLEANUP_FUNC(Varlink *, varlink_unref);

View file

@ -4007,6 +4007,8 @@ typedef struct UnitStatusInfo {
int exit_code, exit_status;
const char *log_namespace;
usec_t condition_timestamp;
bool condition_result;
LIST_HEAD(UnitCondition, conditions);
@ -4545,6 +4547,7 @@ static void print_status_info(
show_journal_by_unit(
stdout,
i->id,
i->log_namespace,
arg_output,
0,
i->inactive_exit_timestamp_monotonic,
@ -5491,6 +5494,7 @@ static int show_one(
{ "ExecMainExitTimestamp", "t", NULL, offsetof(UnitStatusInfo, exit_timestamp) },
{ "ExecMainCode", "i", NULL, offsetof(UnitStatusInfo, exit_code) },
{ "ExecMainStatus", "i", NULL, offsetof(UnitStatusInfo, exit_status) },
{ "LogNamespace", "s", NULL, offsetof(UnitStatusInfo, log_namespace) },
{ "ConditionTimestamp", "t", NULL, offsetof(UnitStatusInfo, condition_timestamp) },
{ "ConditionResult", "b", NULL, offsetof(UnitStatusInfo, condition_result) },
{ "Conditions", "a(sbbsi)", map_conditions, 0 },

View file

@ -45,6 +45,18 @@ typedef void (*_sd_destroy_t)(void *userdata);
# define _sd_pure_ __attribute__((__pure__))
#endif
/* Note that strictly speaking __deprecated__ has been available before GCC 6. However, starting with GCC 6
* it also works on enum values, which we are interested in. Since this is a developer-facing feature anyway
* (as opposed to build engineer-facing), let's hence conditionalize this to gcc 6, given that the developers
* are probably going to use something newer anyway. */
#ifndef _sd_deprecated_
# if __GNUC__ >= 6
# define _sd_deprecated_ __attribute__((__deprecated__))
# else
# define _sd_deprecated_
# endif
#endif
#ifndef _SD_STRINGIFY
# define _SD_XSTRINGIFY(x) #x
# define _SD_STRINGIFY(x) _SD_XSTRINGIFY(x)

View file

@ -64,13 +64,15 @@ typedef struct sd_journal sd_journal;
/* Open flags */
enum {
SD_JOURNAL_LOCAL_ONLY = 1 << 0,
SD_JOURNAL_RUNTIME_ONLY = 1 << 1,
SD_JOURNAL_SYSTEM = 1 << 2,
SD_JOURNAL_CURRENT_USER = 1 << 3,
SD_JOURNAL_OS_ROOT = 1 << 4,
SD_JOURNAL_LOCAL_ONLY = 1 << 0,
SD_JOURNAL_RUNTIME_ONLY = 1 << 1,
SD_JOURNAL_SYSTEM = 1 << 2,
SD_JOURNAL_CURRENT_USER = 1 << 3,
SD_JOURNAL_OS_ROOT = 1 << 4,
SD_JOURNAL_ALL_NAMESPACES = 1 << 5, /* Show all namespaces, not just the default or specified one */
SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE = 1 << 6, /* Show default namespace in addition to specified one */
SD_JOURNAL_SYSTEM_ONLY = SD_JOURNAL_SYSTEM /* deprecated name */
SD_JOURNAL_SYSTEM_ONLY _sd_deprecated_ = SD_JOURNAL_SYSTEM /* deprecated name */
};
/* Wakeup event types */
@ -81,11 +83,12 @@ enum {
};
int sd_journal_open(sd_journal **ret, int flags);
int sd_journal_open_namespace(sd_journal **ret, const char *name_space, int flags);
int sd_journal_open_directory(sd_journal **ret, const char *path, int flags);
int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags);
int sd_journal_open_files(sd_journal **ret, const char **paths, int flags);
int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags);
int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); /* deprecated */
int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) _sd_deprecated_; /* deprecated */
void sd_journal_close(sd_journal *j);
int sd_journal_previous(sd_journal *j);

View file

@ -6,6 +6,9 @@ for header in sys.argv[2:]:
print('#include "{}"'.format(header.split('/')[-1]))
print('''
/* We want to check deprecated symbols too, without complaining */
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
const void* symbols[] = {''')
for line in open(sys.argv[1]):

View file

@ -148,6 +148,7 @@ static void test_protect_kernel_logs(void) {
NULL, 0,
NULL,
NULL,
NULL,
PROTECT_HOME_NO,
PROTECT_SYSTEM_NO,
0,

View file

@ -72,6 +72,7 @@ int main(int argc, char *argv[]) {
&(TemporaryFileSystem) { .path = (char*) "/var", .options = (char*) "ro" }, 1,
tmp_dir,
var_tmp_dir,
NULL,
PROTECT_HOME_NO,
PROTECT_SYSTEM_NO,
0,

View file

@ -0,0 +1 @@
../TEST-01-BASIC/Makefile

View file

@ -0,0 +1,39 @@
#!/bin/bash
set -e
TEST_DESCRIPTION="test log namespaces"
. $TEST_BASE_DIR/test-functions
test_setup() {
create_empty_image_rootdir
(
LOG_LEVEL=5
eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
setup_basic_environment
mask_supporting_services
# setup the testsuite service
cat >$initdir/etc/systemd/system/testsuite.service <<EOF
[Unit]
Description=Testsuite service
Before=getty-pre.target
Wants=getty-pre.target
Wants=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
After=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
[Service]
ExecStart=/testsuite.sh
Type=oneshot
LogTarget=foobar
EOF
cp testsuite.sh $initdir/
setup_testsuite
)
setup_nspawn_root
}
do_test "$@"

View file

@ -0,0 +1,19 @@
#!/bin/bash
set -ex
systemd-analyze log-level debug
systemd-run -p LogNamespace=foobar echo "hello world"
journalctl --namespace=foobar --sync
journalctl --namespace=foobar > /tmp/hello-world
journalctl > /tmp/no-hello-world
grep "hello world" /tmp/hello-world
! grep "hello world" /tmp/no-hello-world
systemd-analyze log-level info
echo OK > /testok
exit 0

View file

@ -32,17 +32,17 @@ Z /run/log/journal/%m ~2750 root systemd-journal - -
m4_ifdef(`HAVE_ACL',`m4_dnl
m4_ifdef(`ENABLE_ADM_GROUP',`m4_dnl
m4_ifdef(`ENABLE_WHEEL_GROUP',``
a+ /run/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x
a+ /run/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x
a+ /run/log/journal - - - - d:group::r-x,d:group:adm:r-x,d:group:wheel:r-x,group::r-x,group:adm:r-x,group:wheel:r-x
a+ /run/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x,group:adm:r-x,group:wheel:r-x
a+ /run/log/journal/%m/*.journal* - - - - group:adm:r--,group:wheel:r--
'',``
a+ /run/log/journal/%m - - - - d:group:adm:r-x
a+ /run/log/journal/%m - - - - group:adm:r-x
a+ /run/log/journal - - - - d:group::r-x,d:group:adm:r-x,group::r-x,group:adm:r-x
a+ /run/log/journal/%m - - - - d:group:adm:r-x,group:adm:r-x
a+ /run/log/journal/%m/*.journal* - - - - group:adm:r--
'')',`m4_dnl
m4_ifdef(`ENABLE_WHEEL_GROUP',``
a+ /run/log/journal/%m - - - - d:group:wheel:r-x
a+ /run/log/journal/%m - - - - group:wheel:r-x
a+ /run/log/journal - - - - d:group::r-x,d:group:wheel:r-x,group::r-x,group:wheel:r-x
a+ /run/log/journal/%m - - - - d:group:wheel:r-x,group:wheel:r-x
a+ /run/log/journal/%m/*.journal* - - - - group:wheel:r--
'')')')m4_dnl
@ -52,23 +52,17 @@ z /var/log/journal/%m/system.journal 0640 root systemd-journal - -
m4_ifdef(`HAVE_ACL',`m4_dnl
m4_ifdef(`ENABLE_ADM_GROUP',`m4_dnl
m4_ifdef(`ENABLE_WHEEL_GROUP',``
a+ /var/log/journal - - - - d:group::r-x,d:group:adm:r-x,d:group:wheel:r-x
a+ /var/log/journal - - - - group::r-x,group:adm:r-x,group:wheel:r-x
a+ /var/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x
a+ /var/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x
a+ /var/log/journal - - - - d:group::r-x,d:group:adm:r-x,d:group:wheel:r-x,group::r-x,group:adm:r-x,group:wheel:r-x
a+ /var/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x,group:adm:r-x,group:wheel:r-x
a+ /var/log/journal/%m/system.journal - - - - group:adm:r--,group:wheel:r--
'', ``
a+ /var/log/journal - - - - d:group::r-x,d:group:adm:r-x
a+ /var/log/journal - - - - group::r-x,group:adm:r-x
a+ /var/log/journal/%m - - - - d:group:adm:r-x
a+ /var/log/journal/%m - - - - group:adm:r-x
a+ /var/log/journal - - - - d:group::r-x,d:group:adm:r-x,group::r-x,group:adm:r-x
a+ /var/log/journal/%m - - - - d:group:adm:r-x,group:adm:r-x
a+ /var/log/journal/%m/system.journal - - - - group:adm:r--
'')',`m4_dnl
m4_ifdef(`ENABLE_WHEEL_GROUP',``
a+ /var/log/journal - - - - d:group::r-x,d:group:wheel:r-x
a+ /var/log/journal - - - - group::r-x,group:wheel:r-x
a+ /var/log/journal/%m - - - - d:group:wheel:r-x
a+ /var/log/journal/%m - - - - group:wheel:r-x
a+ /var/log/journal - - - - d:group::r-x,d:group:wheel:r-x,group::r-x,group:wheel:r-x
a+ /var/log/journal/%m - - - - d:group:wheel:r-x,group:wheel:r-x
a+ /var/log/journal/%m/system.journal - - - - group:wheel:r--
'')')')m4_dnl

View file

@ -114,6 +114,8 @@ units = [
['systemd-kexec.service', ''],
['systemd-machine-id-commit.service', '',
'sysinit.target.wants/'],
['systemd-journald@.socket', ''],
['systemd-journald-varlink@.socket', ''],
['systemd-networkd.socket', 'ENABLE_NETWORKD'],
['systemd-poweroff.service', ''],
['systemd-reboot.service', ''],
@ -180,6 +182,7 @@ in_units = [
['systemd-journal-upload.service', 'ENABLE_REMOTE HAVE_LIBCURL'],
['systemd-journald.service', '',
'sysinit.target.wants/'],
['systemd-journald@.service', ''],
['systemd-localed.service', 'ENABLE_LOCALED',
'dbus-org.freedesktop.locale1.service'],
['systemd-logind.service', 'ENABLE_LOGIND',

View file

@ -0,0 +1,18 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# This file is part of systemd.
#
# 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.
[Unit]
Description=Journal Varlink Socket for Namespace %i
Documentation=man:systemd-journald.service(8) man:journald.conf(5)
StopWhenUnneeded=yes
[Socket]
Service=systemd-journald@%i.service
ListenStream=/run/systemd/journal.%i/io.systemd.journal
SocketMode=0600

View file

@ -16,7 +16,6 @@ After=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-a
Before=sysinit.target
[Service]
OOMScoreAdjust=-250
CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_SYSLOG CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID CAP_MAC_OVERRIDE
DeviceAllow=char-* rw
ExecStart=@rootlibexecdir@/systemd-journald
@ -25,12 +24,15 @@ IPAddressDeny=any
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
OOMScoreAdjust=-250
Restart=always
RestartSec=0
RestrictAddressFamilies=AF_UNIX AF_NETLINK
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
RuntimeDirectory=systemd/journal
RuntimeDirectoryPreserve=yes
Sockets=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket
StandardOutput=null
SystemCallArchitectures=native

View file

@ -0,0 +1,44 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# This file is part of systemd.
#
# 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.
[Unit]
Description=Journal Service for Namespace %i
Documentation=man:systemd-journald.service(8) man:journald.conf(5)
Requires=systemd-journald@%i.socket systemd-journald-varlink@%i.socket
After=systemd-journald@%i.socket systemd-journald-varlink@%i.socket
[Service]
CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID CAP_MAC_OVERRIDE
DevicePolicy=closed
ExecStart=@rootlibexecdir@/systemd-journald %i
FileDescriptorStoreMax=4224
Group=systemd-journal
IPAddressDeny=any
LockPersonality=yes
LogsDirectory=journal/%m.%i
LogsDirectoryMode=02755
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
RuntimeDirectory=systemd/journal.%i
RuntimeDirectoryPreserve=yes
Sockets=systemd-journald@%i.socket
StandardOutput=null
SystemCallArchitectures=native
SystemCallErrorNumber=EPERM
SystemCallFilter=@system-service
Type=notify
@SERVICE_WATCHDOG@
# If there are many split up journal files we need a lot of fds to access them
# all in parallel.
LimitNOFILE=@HIGH_RLIMIT_NOFILE@

View file

@ -0,0 +1,24 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# This file is part of systemd.
#
# 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.
[Unit]
Description=Journal Socket for Namespace %i
Documentation=man:systemd-journald.service(8) man:journald.conf(5)
StopWhenUnneeded=yes
[Socket]
Service=systemd-journald@%i.service
ListenStream=/run/systemd/journal.%i/stdout
ListenDatagram=/run/systemd/journal.%i/socket
ListenDatagram=/run/systemd/journal.%i/dev-log
SocketMode=0666
PassCredentials=yes
PassSecurity=yes
ReceiveBuffer=8M
SendBuffer=8M