mirror of
https://github.com/systemd/systemd
synced 2024-10-15 12:34:37 +00:00
Merge pull request #7246 from poettering/journal-extra-fields
add new per-unit LogLevelMax= and LogExtraField= setting
This commit is contained in:
commit
6af6a50a0b
|
@ -90,17 +90,19 @@
|
|||
for more information about the collected metadata.
|
||||
</para>
|
||||
|
||||
<para>Log data collected by the journal is primarily text-based
|
||||
but can also include binary data where necessary. All objects
|
||||
stored in the journal can be up to 2^64-1 bytes in size.</para>
|
||||
<para>Log data collected by the journal is primarily text-based but can also include binary data where
|
||||
necessary. Individual fields making up a log record stored in the journal may be up to 2^64-1 bytes in size.</para>
|
||||
|
||||
<para>By default, the journal stores log data in
|
||||
<filename>/run/log/journal/</filename>. Since
|
||||
<filename>/run/</filename> is volatile, log data is lost at
|
||||
reboot. To make the data persistent, it is sufficient to create
|
||||
<filename>/var/log/journal/</filename> where
|
||||
<filename>systemd-journald</filename> will then store the
|
||||
data:</para>
|
||||
<para>The journal service stores log data either persistently below <filename>/var/log/journal</filename> or in a
|
||||
volatile way below <filename>/run/log/journal/</filename> (in the latter case it is lost at reboot). By default, log
|
||||
data is stored persistently if <filename>/var/log/journal/</filename> exists during boot, with an implicit fallback
|
||||
to volatile storage otherwise. Use <varname>Storage=</varname> in
|
||||
<citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> to configure
|
||||
where log data is placed, independently of the existence of <filename>/var/log/journal/</filename>.</para>
|
||||
|
||||
<para>On systems where <filename>/var/log/journal/</filename> does not exist yet but where persistent logging is
|
||||
desired (and the default <filename>journald.conf</filename> is used), it is sufficient to create the directory, and
|
||||
ensure it has the correct access modes and ownership:</para>
|
||||
|
||||
<programlisting>mkdir -p /var/log/journal
|
||||
systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
|
||||
|
|
|
@ -686,81 +686,96 @@
|
|||
that the screen and scrollback buffer is cleared. Defaults to
|
||||
<literal>no</literal>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>SyslogIdentifier=</varname></term>
|
||||
<listitem><para>Sets the process name to prefix log lines sent
|
||||
to the logging system or the kernel log buffer with. If not
|
||||
set, defaults to the process name of the executed process.
|
||||
This option is only useful when
|
||||
<varname>StandardOutput=</varname> or
|
||||
<varname>StandardError=</varname> are set to
|
||||
<option>syslog</option>, <option>journal</option> or
|
||||
<option>kmsg</option> (or to the same settings in combination
|
||||
with <option>+console</option>).</para></listitem>
|
||||
<listitem><para>Sets the process name ("<command>syslog</command> tag") to prefix log lines sent to the logging
|
||||
system or the kernel log buffer with. If not set, defaults to the process name of the executed process. This
|
||||
option is only useful when <varname>StandardOutput=</varname> or <varname>StandardError=</varname> are set to
|
||||
<option>journal</option>, <option>syslog</option> or <option>kmsg</option> (or to the same settings in
|
||||
combination with <option>+console</option>) and only applies to log messages written to stdout or
|
||||
stderr.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>SyslogFacility=</varname></term>
|
||||
<listitem><para>Sets the syslog facility to use when logging
|
||||
to syslog. One of <option>kern</option>,
|
||||
<option>user</option>, <option>mail</option>,
|
||||
<option>daemon</option>, <option>auth</option>,
|
||||
<option>syslog</option>, <option>lpr</option>,
|
||||
<option>news</option>, <option>uucp</option>,
|
||||
<option>cron</option>, <option>authpriv</option>,
|
||||
<option>ftp</option>, <option>local0</option>,
|
||||
<option>local1</option>, <option>local2</option>,
|
||||
<option>local3</option>, <option>local4</option>,
|
||||
<option>local5</option>, <option>local6</option> or
|
||||
<option>local7</option>. See
|
||||
<listitem><para>Sets the <command>syslog</command> facility identifier to use when logging. One of
|
||||
<option>kern</option>, <option>user</option>, <option>mail</option>, <option>daemon</option>,
|
||||
<option>auth</option>, <option>syslog</option>, <option>lpr</option>, <option>news</option>,
|
||||
<option>uucp</option>, <option>cron</option>, <option>authpriv</option>, <option>ftp</option>,
|
||||
<option>local0</option>, <option>local1</option>, <option>local2</option>, <option>local3</option>,
|
||||
<option>local4</option>, <option>local5</option>, <option>local6</option> or <option>local7</option>. See
|
||||
<citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
||||
for details. This option is only useful when
|
||||
<varname>StandardOutput=</varname> or
|
||||
<varname>StandardError=</varname> are set to
|
||||
<option>syslog</option>. Defaults to
|
||||
<option>daemon</option>.</para></listitem>
|
||||
for details. This option is only useful when <varname>StandardOutput=</varname> or
|
||||
<varname>StandardError=</varname> are set to <option>journal</option>, <option>syslog</option> or
|
||||
<option>kmsg</option> (or to the same settings in combination with <option>+console</option>), and only applies
|
||||
to log messages written to stdout or stderr. Defaults to <option>daemon</option>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>SyslogLevel=</varname></term>
|
||||
<listitem><para>The default syslog level to use when logging to
|
||||
syslog or the kernel log buffer. One of
|
||||
<option>emerg</option>,
|
||||
<option>alert</option>,
|
||||
<option>crit</option>,
|
||||
<option>err</option>,
|
||||
<option>warning</option>,
|
||||
<option>notice</option>,
|
||||
<option>info</option>,
|
||||
<option>debug</option>. See
|
||||
<citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
||||
for details. This option is only useful when
|
||||
<varname>StandardOutput=</varname> or
|
||||
<varname>StandardError=</varname> are set to
|
||||
<option>syslog</option> or <option>kmsg</option>. Note that
|
||||
individual lines output by the daemon might be prefixed with a
|
||||
different log level which can be used to override the default
|
||||
log level specified here. The interpretation of these prefixes
|
||||
may be disabled with <varname>SyslogLevelPrefix=</varname>,
|
||||
see below. For details, see
|
||||
<citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
|
||||
<listitem><para>The default <command>syslog</command> log level to use when logging to the logging system or
|
||||
the kernel log buffer. One of <option>emerg</option>, <option>alert</option>, <option>crit</option>,
|
||||
<option>err</option>, <option>warning</option>, <option>notice</option>, <option>info</option>,
|
||||
<option>debug</option>. See <citerefentry
|
||||
project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
|
||||
details. This option is only useful when <varname>StandardOutput=</varname> or
|
||||
<varname>StandardError=</varname> are set to <option>journal</option>, <option>syslog</option> or
|
||||
<option>kmsg</option> (or to the same settings in combination with <option>+console</option>), and only applies
|
||||
to log messages written to stdout or stderr. Note that individual lines output by executed processes may be
|
||||
prefixed with a different log level which can be used to override the default log level specified here. The
|
||||
interpretation of these prefixes may be disabled with <varname>SyslogLevelPrefix=</varname>, see below. For
|
||||
details, see <citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
|
||||
|
||||
Defaults to
|
||||
<option>info</option>.</para></listitem>
|
||||
Defaults to <option>info</option>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>SyslogLevelPrefix=</varname></term>
|
||||
<listitem><para>Takes a boolean argument. If true and
|
||||
<varname>StandardOutput=</varname> or
|
||||
<varname>StandardError=</varname> are set to
|
||||
<option>syslog</option>, <option>kmsg</option> or
|
||||
<option>journal</option>, log lines written by the executed
|
||||
process that are prefixed with a log level will be passed on
|
||||
to syslog with this log level set but the prefix removed. If
|
||||
set to false, the interpretation of these prefixes is disabled
|
||||
and the logged lines are passed on as-is. For details about
|
||||
this prefixing see
|
||||
<citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
|
||||
Defaults to true.</para></listitem>
|
||||
<listitem><para>Takes a boolean argument. If true and <varname>StandardOutput=</varname> or
|
||||
<varname>StandardError=</varname> are set to <option>journal</option>, <option>syslog</option> or
|
||||
<option>kmsg</option> (or to the same settings in combination with <option>+console</option>), log lines
|
||||
written by the executed process that are prefixed with a log level will be processed with this log level set
|
||||
but the prefix removed. If set to false, the interpretation of these prefixes is disabled and the logged lines
|
||||
are passed on as-is. This only applies to log messages written to stdout or stderr. For details
|
||||
about this prefixing see
|
||||
<citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>. Defaults to
|
||||
true.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LogLevelMax=</varname></term>
|
||||
<listitem><para>Configures filtering by log level of log messages generated by this unit. Takes a
|
||||
<command>syslog</command> log level, one of <option>emerg</option> (lowest log level, only highest priority
|
||||
messages), <option>alert</option>, <option>crit</option>, <option>err</option>, <option>warning</option>,
|
||||
<option>notice</option>, <option>info</option>, <option>debug</option> (highest log level, also lowest priority
|
||||
messages). See <citerefentry
|
||||
project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
|
||||
details. By default no filtering is applied (i.e. the default maximum log level is <option>debug</option>). Use
|
||||
this option to configure the logging system to drop log messages of a specific service above the specified
|
||||
level. For example, set <varname>LogLevelMax=</varname><option>info</option> in order to turn off debug logging
|
||||
of a particularly chatty unit. Note that the the configured level is applied to any log messages written by any
|
||||
of the processes belonging to this unit, sent via any supported logging protocol. The filtering is applied
|
||||
early in the logging pipeline, before any kind of further processing is done. Moreover, messages which pass
|
||||
through this filter successfully might still be dropped by filters applied at a later stage in the logging
|
||||
subsystem. For example, <varname>MaxLevelStore=</varname> configured in
|
||||
<citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> might
|
||||
prohibit messages of higher log levels to be stored on disk, even though the per-unit
|
||||
<varname>LogLevelMax=</varname> permitted it to be processed.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LogExtraFields=</varname></term>
|
||||
<listitem><para>Configures additional log metadata fields to include in all log records generated by processes
|
||||
associated with this unit. This setting takes one or more journal field assignments in the format
|
||||
<literal>FIELD=VALUE</literal> separated by whitespace. See
|
||||
<citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
|
||||
details on the journal field concept. Even though the underlying journal implementation permits binary field
|
||||
values, this setting accepts only valid UTF-8 values. To include space characters in a journal field value,
|
||||
enclose the assignment in double quotes ("). The usual specifiers are expanded in all assignments (see
|
||||
below). Note that this setting is not only useful for attaching additional metadata to log records of a unit,
|
||||
but given that all fields and values are indexed may also be used to implement cross-unit log record
|
||||
matching. Assign an empty string to reset the list.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
|
|
@ -847,8 +847,8 @@ int log_oom_internal(LogRealm realm, const char *file, int line, const char *fun
|
|||
|
||||
int log_format_iovec(
|
||||
struct iovec *iovec,
|
||||
unsigned iovec_len,
|
||||
unsigned *n,
|
||||
size_t iovec_len,
|
||||
size_t *n,
|
||||
bool newline_separator,
|
||||
int error,
|
||||
const char *format,
|
||||
|
@ -928,7 +928,7 @@ int log_struct_internal(
|
|||
if (journal_fd >= 0) {
|
||||
char header[LINE_MAX];
|
||||
struct iovec iovec[17] = {};
|
||||
unsigned n = 0, i;
|
||||
size_t n = 0, i;
|
||||
int r;
|
||||
struct msghdr mh = {
|
||||
.msg_iov = iovec,
|
||||
|
|
|
@ -180,8 +180,8 @@ int log_oom_internal(
|
|||
|
||||
int log_format_iovec(
|
||||
struct iovec *iovec,
|
||||
unsigned iovec_len,
|
||||
unsigned *n,
|
||||
size_t iovec_len,
|
||||
size_t *n,
|
||||
bool newline_separator,
|
||||
int error,
|
||||
const char *format,
|
||||
|
|
|
@ -26,15 +26,17 @@
|
|||
#include "af-list.h"
|
||||
#include "alloc-util.h"
|
||||
#include "bus-util.h"
|
||||
#include "capability-util.h"
|
||||
#include "cap-list.h"
|
||||
#include "capability-util.h"
|
||||
#include "dbus-execute.h"
|
||||
#include "env-util.h"
|
||||
#include "errno-list.h"
|
||||
#include "execute.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "io-util.h"
|
||||
#include "ioprio.h"
|
||||
#include "journal-util.h"
|
||||
#include "missing.h"
|
||||
#include "mount-util.h"
|
||||
#include "namespace.h"
|
||||
|
@ -771,6 +773,37 @@ static int property_get_bind_paths(
|
|||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int property_get_log_extra_fields(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
ExecContext *c = userdata;
|
||||
size_t i;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(c);
|
||||
assert(property);
|
||||
assert(reply);
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "ay");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (i = 0; i < c->n_log_extra_fields; i++) {
|
||||
r = sd_bus_message_append_array(reply, 'y', c->log_extra_fields[i].iov_base, c->log_extra_fields[i].iov_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
const sd_bus_vtable bus_exec_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
|
@ -838,6 +871,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
|||
SD_BUS_PROPERTY("SyslogLevelPrefix", "b", bus_property_get_bool, offsetof(ExecContext, syslog_level_prefix), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("SyslogLevel", "i", property_get_syslog_level, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("SyslogFacility", "i", property_get_syslog_facility, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("LogLevelMax", "i", bus_property_get_int, offsetof(ExecContext, log_level_max), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("LogExtraFields", "aay", property_get_log_extra_fields, 0, 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", property_get_capability_bounding_set, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("AmbientCapabilities", "t", property_get_ambient_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
|
@ -1137,6 +1172,98 @@ int bus_exec_context_set_transient_property(
|
|||
}
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (streq(name, "LogLevelMax")) {
|
||||
int32_t level;
|
||||
|
||||
r = sd_bus_message_read(message, "i", &level);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!log_level_is_valid(level))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Maximum log level value out of range");
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
c->log_level_max = level;
|
||||
unit_write_drop_in_private_format(u, mode, name, "LogLevelMax=%i", level);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (streq(name, "LogExtraFields")) {
|
||||
size_t n = 0;
|
||||
|
||||
r = sd_bus_message_enter_container(message, 'a', "ay");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ void *copy = NULL;
|
||||
struct iovec *t;
|
||||
const char *eq;
|
||||
const void *p;
|
||||
size_t sz;
|
||||
|
||||
/* Note that we expect a byte array for each field, instead of a string. That's because on the
|
||||
* lower-level journal fields can actually contain binary data and are not restricted to text,
|
||||
* and we should not "lose precision" in our types on the way. That said, I am pretty sure
|
||||
* actually encoding binary data as unit metadata is not a good idea. Hence we actually refuse
|
||||
* any actual binary data, and only accept UTF-8. This allows us to eventually lift this
|
||||
* limitation, should a good, valid usecase arise. */
|
||||
|
||||
r = sd_bus_message_read_array(message, 'y', &p, &sz);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
if (memchr(p, 0, sz))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field contains zero byte");
|
||||
|
||||
eq = memchr(p, '=', sz);
|
||||
if (!eq)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field contains no '=' character");
|
||||
if (!journal_field_valid(p, eq - (const char*) p, false))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field invalid");
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
t = realloc_multiply(c->log_extra_fields, sizeof(struct iovec), c->n_log_extra_fields+1);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
c->log_extra_fields = t;
|
||||
}
|
||||
|
||||
copy = malloc(sz + 1);
|
||||
if (!copy)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(copy, p, sz);
|
||||
((uint8_t*) copy)[sz] = 0;
|
||||
|
||||
if (!utf8_is_valid(copy))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field is not valid UTF-8");
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
c->log_extra_fields[c->n_log_extra_fields++] = IOVEC_MAKE(copy, sz);
|
||||
unit_write_drop_in_private_format(u, mode, name, "LogExtraFields=%s", (char*) copy);
|
||||
|
||||
copy = NULL;
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
r = sd_bus_message_exit_container(message);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (mode != UNIT_CHECK && n == 0) {
|
||||
exec_context_free_log_extra_fields(c);
|
||||
unit_write_drop_in_private(u, mode, name, "LogExtraFields=");
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (streq(name, "SecureBits")) {
|
||||
int n;
|
||||
|
||||
|
|
|
@ -3483,11 +3483,12 @@ void exec_context_init(ExecContext *c) {
|
|||
c->directories[i].mode = 0755;
|
||||
c->capability_bounding_set = CAP_ALL;
|
||||
c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
|
||||
c->log_level_max = -1;
|
||||
}
|
||||
|
||||
void exec_context_done(ExecContext *c) {
|
||||
unsigned l;
|
||||
ExecDirectoryType i;
|
||||
size_t l;
|
||||
|
||||
assert(c);
|
||||
|
||||
|
@ -3534,6 +3535,10 @@ void exec_context_done(ExecContext *c) {
|
|||
|
||||
for (i = 0; i < _EXEC_DIRECTORY_TYPE_MAX; i++)
|
||||
c->directories[i].paths = strv_free(c->directories[i].paths);
|
||||
|
||||
c->log_level_max = -1;
|
||||
|
||||
exec_context_free_log_extra_fields(c);
|
||||
}
|
||||
|
||||
int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_prefix) {
|
||||
|
@ -3796,9 +3801,9 @@ static void strv_fprintf(FILE *f, char **l) {
|
|||
}
|
||||
|
||||
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
|
||||
ExecDirectoryType dt;
|
||||
char **e, **d;
|
||||
unsigned i;
|
||||
ExecDirectoryType dt;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
|
@ -3966,6 +3971,26 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
|
|||
fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str);
|
||||
}
|
||||
|
||||
if (c->log_level_max >= 0) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
|
||||
(void) log_level_to_string_alloc(c->log_level_max, &t);
|
||||
|
||||
fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
|
||||
}
|
||||
|
||||
if (c->n_log_extra_fields > 0) {
|
||||
size_t j;
|
||||
|
||||
for (j = 0; j < c->n_log_extra_fields; j++) {
|
||||
fprintf(f, "%sLogExtraFields: ", prefix);
|
||||
fwrite(c->log_extra_fields[j].iov_base,
|
||||
1, c->log_extra_fields[j].iov_len,
|
||||
f);
|
||||
fputc('\n', f);
|
||||
}
|
||||
}
|
||||
|
||||
if (c->secure_bits) {
|
||||
_cleanup_free_ char *str = NULL;
|
||||
|
||||
|
@ -4177,6 +4202,17 @@ int exec_context_get_effective_ioprio(ExecContext *c) {
|
|||
return p;
|
||||
}
|
||||
|
||||
void exec_context_free_log_extra_fields(ExecContext *c) {
|
||||
size_t l;
|
||||
|
||||
assert(c);
|
||||
|
||||
for (l = 0; l < c->n_log_extra_fields; l++)
|
||||
free(c->log_extra_fields[l].iov_base);
|
||||
c->log_extra_fields = mfree(c->log_extra_fields);
|
||||
c->n_log_extra_fields = 0;
|
||||
}
|
||||
|
||||
void exec_status_start(ExecStatus *s, pid_t pid) {
|
||||
assert(s);
|
||||
|
||||
|
|
|
@ -212,6 +212,11 @@ struct ExecContext {
|
|||
char *syslog_identifier;
|
||||
bool syslog_level_prefix;
|
||||
|
||||
int log_level_max;
|
||||
|
||||
struct iovec* log_extra_fields;
|
||||
size_t n_log_extra_fields;
|
||||
|
||||
bool cpu_sched_reset_on_fork;
|
||||
bool non_blocking;
|
||||
bool private_tmp;
|
||||
|
@ -353,6 +358,8 @@ bool exec_context_maintains_privileges(ExecContext *c);
|
|||
|
||||
int exec_context_get_effective_ioprio(ExecContext *c);
|
||||
|
||||
void exec_context_free_log_extra_fields(ExecContext *c);
|
||||
|
||||
void exec_status_start(ExecStatus *s, pid_t pid);
|
||||
void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status);
|
||||
void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix);
|
||||
|
|
|
@ -49,6 +49,8 @@ $1.SyslogIdentifier, config_parse_unit_string_printf, 0,
|
|||
$1.SyslogFacility, config_parse_log_facility, 0, offsetof($1, exec_context.syslog_priority)
|
||||
$1.SyslogLevel, config_parse_log_level, 0, offsetof($1, exec_context.syslog_priority)
|
||||
$1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix)
|
||||
$1.LogLevelMax, config_parse_log_level, 0, offsetof($1, exec_context.log_level_max)
|
||||
$1.LogExtraFields, config_parse_log_extra_fields, 0, offsetof($1, exec_context)
|
||||
$1.Capabilities, config_parse_warn_compat, DISABLED_LEGACY, offsetof($1, exec_context)
|
||||
$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context)
|
||||
$1.CapabilityBoundingSet, config_parse_capability_set, 0, offsetof($1, exec_context.capability_bounding_set)
|
||||
|
|
|
@ -45,7 +45,9 @@
|
|||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "io-util.h"
|
||||
#include "ioprio.h"
|
||||
#include "journal-util.h"
|
||||
#include "load-fragment.h"
|
||||
#include "log.h"
|
||||
#include "missing.h"
|
||||
|
@ -2331,6 +2333,78 @@ int config_parse_unset_environ(
|
|||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_log_extra_fields(
|
||||
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) {
|
||||
|
||||
ExecContext *c = data;
|
||||
Unit *u = userdata;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(c);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
exec_context_free_log_extra_fields(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (p = rvalue;; ) {
|
||||
_cleanup_free_ char *word = NULL, *k = NULL;
|
||||
struct iovec *t;
|
||||
const char *eq;
|
||||
|
||||
r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_QUOTES);
|
||||
if (r == 0)
|
||||
break;
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = unit_full_printf(u, word, &k);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring field: %m", word);
|
||||
continue;
|
||||
}
|
||||
|
||||
eq = strchr(k, '=');
|
||||
if (!eq) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Log field lacks '=' character, ignoring field: %s", k);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!journal_field_valid(k, eq-k, false)) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Log field name is invalid, ignoring field: %s", k);
|
||||
continue;
|
||||
}
|
||||
|
||||
t = realloc_multiply(c->log_extra_fields, sizeof(struct iovec), c->n_log_extra_fields+1);
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
c->log_extra_fields = t;
|
||||
c->log_extra_fields[c->n_log_extra_fields++] = IOVEC_MAKE_STRING(k);
|
||||
|
||||
k = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_ip_tos(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
|
|
|
@ -121,6 +121,7 @@ int config_parse_bind_paths(const char *unit, const char *filename, unsigned lin
|
|||
int config_parse_exec_keyring_mode(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);
|
||||
int config_parse_job_timeout_sec(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);
|
||||
int config_parse_job_running_timeout_sec(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);
|
||||
int config_parse_log_extra_fields(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);
|
||||
|
||||
/* gperf prototypes */
|
||||
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
|
||||
|
|
|
@ -53,14 +53,15 @@
|
|||
#include "dirent-util.h"
|
||||
#include "env-util.h"
|
||||
#include "escape.h"
|
||||
#include "execute.h"
|
||||
#include "exec-util.h"
|
||||
#include "execute.h"
|
||||
#include "exit-status.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "io-util.h"
|
||||
#include "label.h"
|
||||
#include "locale-setup.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
|
@ -714,6 +715,12 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (MANAGER_IS_SYSTEM(m)) {
|
||||
r = mkdir_label("/run/systemd/units", 0755);
|
||||
if (r < 0 && r != -EEXIST)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Note that we do not set up the notify fd here. We do that after deserialization,
|
||||
* since they might have gotten serialized across the reexec. */
|
||||
|
||||
|
|
|
@ -769,6 +769,8 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
|
|||
m->reset_accounting = false;
|
||||
}
|
||||
|
||||
unit_export_state_files(UNIT(m));
|
||||
|
||||
r = unit_setup_exec_runtime(UNIT(m));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
|
|
@ -339,6 +339,8 @@ static int scope_start(Unit *u) {
|
|||
(void) unit_reset_cpu_accounting(u);
|
||||
(void) unit_reset_ip_accounting(u);
|
||||
|
||||
unit_export_state_files(UNIT(s));
|
||||
|
||||
r = unit_attach_pids_to_cgroup(u);
|
||||
if (r < 0) {
|
||||
log_unit_warning_errno(UNIT(s), r, "Failed to add PIDs to scope's control group: %m");
|
||||
|
|
|
@ -1251,6 +1251,8 @@ static int service_spawn(
|
|||
s->reset_accounting = false;
|
||||
}
|
||||
|
||||
unit_export_state_files(UNIT(s));
|
||||
|
||||
r = unit_setup_exec_runtime(UNIT(s));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
|
|
@ -1884,6 +1884,8 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
|
|||
s->reset_accounting = false;
|
||||
}
|
||||
|
||||
unit_export_state_files(UNIT(s));
|
||||
|
||||
r = unit_setup_exec_runtime(UNIT(s));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
|
|
@ -619,6 +619,8 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
|
|||
s->reset_accounting = false;
|
||||
}
|
||||
|
||||
unit_export_state_files(UNIT(s));
|
||||
|
||||
r = unit_setup_exec_runtime(UNIT(s));
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
|
216
src/core/unit.c
216
src/core/unit.c
|
@ -38,6 +38,7 @@
|
|||
#include "fd-util.h"
|
||||
#include "fileio-label.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "id128-util.h"
|
||||
#include "io-util.h"
|
||||
#include "load-dropin.h"
|
||||
|
@ -51,6 +52,7 @@
|
|||
#include "process-util.h"
|
||||
#include "set.h"
|
||||
#include "signal-util.h"
|
||||
#include "sparse-endian.h"
|
||||
#include "special.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
|
@ -597,6 +599,9 @@ void unit_free(Unit *u) {
|
|||
|
||||
unit_release_cgroup(u);
|
||||
|
||||
if (!MANAGER_IS_RELOADING(u->manager))
|
||||
unit_unlink_state_files(u);
|
||||
|
||||
unit_unref_uid_gid(u, false);
|
||||
|
||||
(void) manager_update_failed_units(u->manager, u, false);
|
||||
|
@ -2296,9 +2301,11 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
|
|||
/* Keep track of failed units */
|
||||
(void) manager_update_failed_units(u->manager, u, ns == UNIT_FAILED);
|
||||
|
||||
/* Make sure the cgroup is always removed when we become inactive */
|
||||
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
|
||||
/* Make sure the cgroup and state files are always removed when we become inactive */
|
||||
if (UNIT_IS_INACTIVE_OR_FAILED(ns)) {
|
||||
unit_prune_cgroup(u);
|
||||
unit_unlink_state_files(u);
|
||||
}
|
||||
|
||||
/* Note that this doesn't apply to RemainAfterExit services exiting
|
||||
* successfully, since there's no change of state in that case. Which is
|
||||
|
@ -3102,6 +3109,10 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
|
|||
|
||||
unit_serialize_item(u, f, "transient", yes_no(u->transient));
|
||||
|
||||
unit_serialize_item(u, f, "exported-invocation-id", yes_no(u->exported_invocation_id));
|
||||
unit_serialize_item(u, f, "exported-log-level-max", yes_no(u->exported_log_level_max));
|
||||
unit_serialize_item(u, f, "exported-log-extra-fields", yes_no(u->exported_log_extra_fields));
|
||||
|
||||
unit_serialize_item_format(u, f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base);
|
||||
if (u->cpu_usage_last != NSEC_INFINITY)
|
||||
unit_serialize_item_format(u, f, "cpu-usage-last", "%" PRIu64, u->cpu_usage_last);
|
||||
|
@ -3342,6 +3353,36 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
|
|||
|
||||
continue;
|
||||
|
||||
} else if (streq(l, "exported-invocation-id")) {
|
||||
|
||||
r = parse_boolean(v);
|
||||
if (r < 0)
|
||||
log_unit_debug(u, "Failed to parse exported invocation ID bool %s, ignoring.", v);
|
||||
else
|
||||
u->exported_invocation_id = r;
|
||||
|
||||
continue;
|
||||
|
||||
} else if (streq(l, "exported-log-level-max")) {
|
||||
|
||||
r = parse_boolean(v);
|
||||
if (r < 0)
|
||||
log_unit_debug(u, "Failed to parse exported log level max bool %s, ignoring.", v);
|
||||
else
|
||||
u->exported_log_level_max = r;
|
||||
|
||||
continue;
|
||||
|
||||
} else if (streq(l, "exported-log-extra-fields")) {
|
||||
|
||||
r = parse_boolean(v);
|
||||
if (r < 0)
|
||||
log_unit_debug(u, "Failed to parse exported log extra fields bool %s, ignoring.", v);
|
||||
else
|
||||
u->exported_log_extra_fields = r;
|
||||
|
||||
continue;
|
||||
|
||||
} else if (STR_IN_SET(l, "cpu-usage-base", "cpuacct-usage-base")) {
|
||||
|
||||
r = safe_atou64(v, &u->cpu_usage_base);
|
||||
|
@ -4913,3 +4954,174 @@ void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) {
|
|||
} while (!done);
|
||||
}
|
||||
}
|
||||
|
||||
static int unit_export_invocation_id(Unit *u) {
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (u->exported_invocation_id)
|
||||
return 0;
|
||||
|
||||
if (sd_id128_is_null(u->invocation_id))
|
||||
return 0;
|
||||
|
||||
p = strjoina("/run/systemd/units/invocation:", u->id);
|
||||
r = symlink_atomic(u->invocation_id_string, p);
|
||||
if (r < 0)
|
||||
return log_unit_debug_errno(u, r, "Failed to create invocation ID symlink %s: %m", p);
|
||||
|
||||
u->exported_invocation_id = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unit_export_log_level_max(Unit *u, const ExecContext *c) {
|
||||
const char *p;
|
||||
char buf[2];
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(c);
|
||||
|
||||
if (u->exported_log_level_max)
|
||||
return 0;
|
||||
|
||||
if (c->log_level_max < 0)
|
||||
return 0;
|
||||
|
||||
assert(c->log_level_max <= 7);
|
||||
|
||||
buf[0] = '0' + c->log_level_max;
|
||||
buf[1] = 0;
|
||||
|
||||
p = strjoina("/run/systemd/units/log-level-max:", u->id);
|
||||
r = symlink_atomic(buf, p);
|
||||
if (r < 0)
|
||||
return log_unit_debug_errno(u, r, "Failed to create maximum log level symlink %s: %m", p);
|
||||
|
||||
u->exported_log_level_max = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unit_export_log_extra_fields(Unit *u, const ExecContext *c) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
struct iovec *iovec;
|
||||
const char *p;
|
||||
char *pattern;
|
||||
le64_t *sizes;
|
||||
ssize_t n;
|
||||
size_t i;
|
||||
int r;
|
||||
|
||||
if (u->exported_log_extra_fields)
|
||||
return 0;
|
||||
|
||||
if (c->n_log_extra_fields <= 0)
|
||||
return 0;
|
||||
|
||||
sizes = newa(le64_t, c->n_log_extra_fields);
|
||||
iovec = newa(struct iovec, c->n_log_extra_fields * 2);
|
||||
|
||||
for (i = 0; i < c->n_log_extra_fields; i++) {
|
||||
sizes[i] = htole64(c->log_extra_fields[i].iov_len);
|
||||
|
||||
iovec[i*2] = IOVEC_MAKE(sizes + i, sizeof(le64_t));
|
||||
iovec[i*2+1] = c->log_extra_fields[i];
|
||||
}
|
||||
|
||||
p = strjoina("/run/systemd/units/log-extra-fields:", u->id);
|
||||
pattern = strjoina(p, ".XXXXXX");
|
||||
|
||||
fd = mkostemp_safe(pattern);
|
||||
if (fd < 0)
|
||||
return log_unit_debug_errno(u, fd, "Failed to create extra fields file %s: %m", p);
|
||||
|
||||
n = writev(fd, iovec, c->n_log_extra_fields*2);
|
||||
if (n < 0) {
|
||||
r = log_unit_debug_errno(u, errno, "Failed to write extra fields: %m");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
(void) fchmod(fd, 0644);
|
||||
|
||||
if (rename(pattern, p) < 0) {
|
||||
r = log_unit_debug_errno(u, errno, "Failed to rename extra fields file: %m");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
u->exported_log_extra_fields = true;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
(void) unlink(pattern);
|
||||
return r;
|
||||
}
|
||||
|
||||
void unit_export_state_files(Unit *u) {
|
||||
const ExecContext *c;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (!u->id)
|
||||
return;
|
||||
|
||||
if (!MANAGER_IS_SYSTEM(u->manager))
|
||||
return;
|
||||
|
||||
/* Exports a couple of unit properties to /run/systemd/units/, so that journald can quickly query this data
|
||||
* from there. Ideally, journald would use IPC to query this, like everybody else, but that's hard, as long as
|
||||
* the IPC system itself and PID 1 also log to the journal.
|
||||
*
|
||||
* Note that these files really shouldn't be considered API for anyone else, as use a runtime file system as
|
||||
* IPC replacement is not compatible with today's world of file system namespaces. However, this doesn't really
|
||||
* apply to communication between the journal and systemd, as we assume that these two daemons live in the same
|
||||
* namespace at least.
|
||||
*
|
||||
* Note that some of the "files" exported here are actually symlinks and not regular files. Symlinks work
|
||||
* better for storing small bits of data, in particular as we can write them with two system calls, and read
|
||||
* them with one. */
|
||||
|
||||
(void) unit_export_invocation_id(u);
|
||||
|
||||
c = unit_get_exec_context(u);
|
||||
if (c) {
|
||||
(void) unit_export_log_level_max(u, c);
|
||||
(void) unit_export_log_extra_fields(u, c);
|
||||
}
|
||||
}
|
||||
|
||||
void unit_unlink_state_files(Unit *u) {
|
||||
const char *p;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (!u->id)
|
||||
return;
|
||||
|
||||
if (!MANAGER_IS_SYSTEM(u->manager))
|
||||
return;
|
||||
|
||||
/* Undoes the effect of unit_export_state() */
|
||||
|
||||
if (u->exported_invocation_id) {
|
||||
p = strjoina("/run/systemd/units/invocation:", u->id);
|
||||
(void) unlink(p);
|
||||
|
||||
u->exported_invocation_id = false;
|
||||
}
|
||||
|
||||
if (u->exported_log_level_max) {
|
||||
p = strjoina("/run/systemd/units/log-level-max:", u->id);
|
||||
(void) unlink(p);
|
||||
|
||||
u->exported_log_level_max = false;
|
||||
}
|
||||
|
||||
if (u->exported_log_extra_fields) {
|
||||
p = strjoina("/run/systemd/units/extra-fields:", u->id);
|
||||
(void) unlink(p);
|
||||
|
||||
u->exported_log_extra_fields = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -338,6 +338,11 @@ struct Unit {
|
|||
|
||||
/* For transient units: whether to add a bus track reference after creating the unit */
|
||||
bool bus_track_add:1;
|
||||
|
||||
/* Remember which unit state files we created */
|
||||
bool exported_invocation_id:1;
|
||||
bool exported_log_level_max:1;
|
||||
bool exported_log_extra_fields:1;
|
||||
};
|
||||
|
||||
struct UnitStatusMessageFormats {
|
||||
|
@ -742,6 +747,9 @@ int unit_fork_helper_process(Unit *u, pid_t *ret);
|
|||
|
||||
void unit_remove_dependencies(Unit *u, UnitDependencyMask mask);
|
||||
|
||||
void unit_export_state_files(Unit *u);
|
||||
void unit_unlink_state_files(Unit *u);
|
||||
|
||||
/* Macros which append UNIT= or USER_UNIT= to the message */
|
||||
|
||||
#define log_unit_full(unit, level, error, ...) \
|
||||
|
|
|
@ -29,10 +29,10 @@
|
|||
typedef struct MapField {
|
||||
const char *audit_field;
|
||||
const char *journal_field;
|
||||
int (*map)(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov);
|
||||
int (*map)(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov);
|
||||
} MapField;
|
||||
|
||||
static int map_simple_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) {
|
||||
static int map_simple_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) {
|
||||
_cleanup_free_ char *c = NULL;
|
||||
size_t l = 0, allocated = 0;
|
||||
const char *e;
|
||||
|
@ -61,9 +61,7 @@ static int map_simple_field(const char *field, const char **p, struct iovec **io
|
|||
if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
(*iov)[*n_iov].iov_base = c;
|
||||
(*iov)[*n_iov].iov_len = l;
|
||||
(*n_iov)++;
|
||||
(*iov)[(*n_iov)++] = IOVEC_MAKE(c, l);
|
||||
|
||||
*p = e;
|
||||
c = NULL;
|
||||
|
@ -71,7 +69,7 @@ static int map_simple_field(const char *field, const char **p, struct iovec **io
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int map_string_field_internal(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov, bool filter_printable) {
|
||||
static int map_string_field_internal(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov, bool filter_printable) {
|
||||
_cleanup_free_ char *c = NULL;
|
||||
const char *s, *e;
|
||||
size_t l;
|
||||
|
@ -140,9 +138,7 @@ static int map_string_field_internal(const char *field, const char **p, struct i
|
|||
if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
(*iov)[*n_iov].iov_base = c;
|
||||
(*iov)[*n_iov].iov_len = l;
|
||||
(*n_iov)++;
|
||||
(*iov)[(*n_iov)++] = IOVEC_MAKE(c, l);
|
||||
|
||||
*p = e;
|
||||
c = NULL;
|
||||
|
@ -150,15 +146,15 @@ static int map_string_field_internal(const char *field, const char **p, struct i
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int map_string_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) {
|
||||
static int map_string_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) {
|
||||
return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, false);
|
||||
}
|
||||
|
||||
static int map_string_field_printable(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) {
|
||||
static int map_string_field_printable(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) {
|
||||
return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, true);
|
||||
}
|
||||
|
||||
static int map_generic_field(const char *prefix, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) {
|
||||
static int map_generic_field(const char *prefix, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) {
|
||||
const char *e, *f;
|
||||
char *c, *t;
|
||||
int r;
|
||||
|
@ -218,29 +214,29 @@ static const MapField map_fields_kernel[] = {
|
|||
|
||||
/* First, we map certain well-known audit fields into native
|
||||
* well-known fields */
|
||||
{ "pid=", "_PID=", map_simple_field },
|
||||
{ "ppid=", "_PPID=", map_simple_field },
|
||||
{ "uid=", "_UID=", map_simple_field },
|
||||
{ "euid=", "_EUID=", map_simple_field },
|
||||
{ "fsuid=", "_FSUID=", map_simple_field },
|
||||
{ "gid=", "_GID=", map_simple_field },
|
||||
{ "egid=", "_EGID=", map_simple_field },
|
||||
{ "fsgid=", "_FSGID=", map_simple_field },
|
||||
{ "tty=", "_TTY=", map_simple_field },
|
||||
{ "ses=", "_AUDIT_SESSION=", map_simple_field },
|
||||
{ "auid=", "_AUDIT_LOGINUID=", map_simple_field },
|
||||
{ "subj=", "_SELINUX_CONTEXT=", map_simple_field },
|
||||
{ "comm=", "_COMM=", map_string_field },
|
||||
{ "exe=", "_EXE=", map_string_field },
|
||||
{ "proctitle=", "_CMDLINE=", map_string_field_printable },
|
||||
{ "pid=", "_PID=", map_simple_field },
|
||||
{ "ppid=", "_PPID=", map_simple_field },
|
||||
{ "uid=", "_UID=", map_simple_field },
|
||||
{ "euid=", "_EUID=", map_simple_field },
|
||||
{ "fsuid=", "_FSUID=", map_simple_field },
|
||||
{ "gid=", "_GID=", map_simple_field },
|
||||
{ "egid=", "_EGID=", map_simple_field },
|
||||
{ "fsgid=", "_FSGID=", map_simple_field },
|
||||
{ "tty=", "_TTY=", map_simple_field },
|
||||
{ "ses=", "_AUDIT_SESSION=", map_simple_field },
|
||||
{ "auid=", "_AUDIT_LOGINUID=", map_simple_field },
|
||||
{ "subj=", "_SELINUX_CONTEXT=", map_simple_field },
|
||||
{ "comm=", "_COMM=", map_string_field },
|
||||
{ "exe=", "_EXE=", map_string_field },
|
||||
{ "proctitle=", "_CMDLINE=", map_string_field_printable },
|
||||
|
||||
/* Some fields don't map to native well-known fields. However,
|
||||
* we know that they are string fields, hence let's undo
|
||||
* string field escaping for them, though we stick to the
|
||||
* generic field names. */
|
||||
{ "path=", "_AUDIT_FIELD_PATH=", map_string_field },
|
||||
{ "dev=", "_AUDIT_FIELD_DEV=", map_string_field },
|
||||
{ "name=", "_AUDIT_FIELD_NAME=", map_string_field },
|
||||
{ "path=", "_AUDIT_FIELD_PATH=", map_string_field },
|
||||
{ "dev=", "_AUDIT_FIELD_DEV=", map_string_field },
|
||||
{ "name=", "_AUDIT_FIELD_NAME=", map_string_field },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -248,11 +244,11 @@ static const MapField map_fields_kernel[] = {
|
|||
* msg='. All of these fields are untrusted, hence carry no "_"
|
||||
* prefix. We map the fields we don't know to AUDIT_FIELD_XYZ= */
|
||||
static const MapField map_fields_userspace[] = {
|
||||
{ "cwd=", "AUDIT_FIELD_CWD=", map_string_field },
|
||||
{ "cmd=", "AUDIT_FIELD_CMD=", map_string_field },
|
||||
{ "acct=", "AUDIT_FIELD_ACCT=", map_string_field },
|
||||
{ "exe=", "AUDIT_FIELD_EXE=", map_string_field },
|
||||
{ "comm=", "AUDIT_FIELD_COMM=", map_string_field },
|
||||
{ "cwd=", "AUDIT_FIELD_CWD=", map_string_field },
|
||||
{ "cmd=", "AUDIT_FIELD_CMD=", map_string_field },
|
||||
{ "acct=", "AUDIT_FIELD_ACCT=", map_string_field },
|
||||
{ "exe=", "AUDIT_FIELD_EXE=", map_string_field },
|
||||
{ "comm=", "AUDIT_FIELD_COMM=", map_string_field },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -263,7 +259,7 @@ static int map_all_fields(
|
|||
bool handle_msg,
|
||||
struct iovec **iov,
|
||||
size_t *n_iov_allocated,
|
||||
unsigned *n_iov) {
|
||||
size_t *n_iov) {
|
||||
|
||||
int r;
|
||||
|
||||
|
@ -335,16 +331,15 @@ static int map_all_fields(
|
|||
}
|
||||
|
||||
static void process_audit_string(Server *s, int type, const char *data, size_t size) {
|
||||
size_t n_iov_allocated = 0, n_iov = 0, z;
|
||||
_cleanup_free_ struct iovec *iov = NULL;
|
||||
size_t n_iov_allocated = 0;
|
||||
unsigned n_iov = 0, k;
|
||||
uint64_t seconds, msec, id;
|
||||
const char *p, *type_name;
|
||||
unsigned z;
|
||||
char id_field[sizeof("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)],
|
||||
type_field[sizeof("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)],
|
||||
source_time_field[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)];
|
||||
char *m;
|
||||
int k;
|
||||
|
||||
assert(s);
|
||||
|
||||
|
|
|
@ -24,9 +24,16 @@
|
|||
#include "alloc-util.h"
|
||||
#include "audit-util.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "io-util.h"
|
||||
#include "journal-util.h"
|
||||
#include "journald-context.h"
|
||||
#include "process-util.h"
|
||||
#include "string-util.h"
|
||||
#include "syslog-util.h"
|
||||
#include "unaligned.h"
|
||||
#include "user-util.h"
|
||||
|
||||
/* This implements a metadata cache for clients, which are identified by their PID. Requesting metadata through /proc
|
||||
|
@ -115,6 +122,8 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
|
|||
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;
|
||||
|
||||
r = hashmap_put(s->client_contexts, PID_TO_PTR(pid), c);
|
||||
if (r < 0) {
|
||||
|
@ -154,6 +163,13 @@ static void client_context_reset(ClientContext *c) {
|
|||
|
||||
c->label = mfree(c->label);
|
||||
c->label_size = 0;
|
||||
|
||||
c->extra_fields_iovec = mfree(c->extra_fields_iovec);
|
||||
c->extra_fields_n_iovec = 0;
|
||||
c->extra_fields_data = mfree(c->extra_fields_data);
|
||||
c->extra_fields_mtime = NSEC_INFINITY;
|
||||
|
||||
c->log_level_max = -1;
|
||||
}
|
||||
|
||||
static ClientContext* client_context_free(Server *s, ClientContext *c) {
|
||||
|
@ -296,40 +312,141 @@ static int client_context_read_invocation_id(
|
|||
Server *s,
|
||||
ClientContext *c) {
|
||||
|
||||
_cleanup_free_ char *escaped = NULL, *slice_path = NULL;
|
||||
char ids[SD_ID128_STRING_MAX];
|
||||
_cleanup_free_ char *value = NULL;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(c);
|
||||
|
||||
/* Read the invocation ID of a unit off a unit. It's stored in the "trusted.invocation_id" extended attribute
|
||||
* on the cgroup path. */
|
||||
/* Read the invocation ID of a unit off a unit. PID 1 stores it in a per-unit symlink in /run/systemd/units/ */
|
||||
|
||||
if (!c->unit || !c->slice)
|
||||
if (!c->unit)
|
||||
return 0;
|
||||
|
||||
r = cg_slice_to_path(c->slice, &slice_path);
|
||||
p = strjoina("/run/systemd/units/invocation:", c->unit);
|
||||
r = readlink_malloc(p, &value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
escaped = cg_escape(c->unit);
|
||||
if (!escaped)
|
||||
return -ENOMEM;
|
||||
return sd_id128_from_string(value, &c->invocation_id);
|
||||
}
|
||||
|
||||
p = strjoina(s->cgroup_root, "/", slice_path, "/", escaped);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
static int client_context_read_log_level_max(
|
||||
Server *s,
|
||||
ClientContext *c) {
|
||||
|
||||
r = cg_get_xattr(SYSTEMD_CGROUP_CONTROLLER, p, "trusted.invocation_id", ids, 32);
|
||||
_cleanup_free_ char *value = NULL;
|
||||
const char *p;
|
||||
int r, ll;
|
||||
|
||||
if (!c->unit)
|
||||
return 0;
|
||||
|
||||
p = strjoina("/run/systemd/units/log-level-max:", c->unit);
|
||||
r = readlink_malloc(p, &value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r != 32)
|
||||
|
||||
ll = log_level_from_string(value);
|
||||
if (ll < 0)
|
||||
return -EINVAL;
|
||||
ids[32] = 0;
|
||||
|
||||
return sd_id128_from_string(ids, &c->invocation_id);
|
||||
c->log_level_max = ll;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int client_context_read_extra_fields(
|
||||
Server *s,
|
||||
ClientContext *c) {
|
||||
|
||||
size_t size = 0, n_iovec = 0, n_allocated = 0, left;
|
||||
_cleanup_free_ struct iovec *iovec = NULL;
|
||||
_cleanup_free_ void *data = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
struct stat st;
|
||||
const char *p;
|
||||
uint8_t *q;
|
||||
int r;
|
||||
|
||||
if (!c->unit)
|
||||
return 0;
|
||||
|
||||
p = strjoina("/run/systemd/units/log-extra-fields:", c->unit);
|
||||
|
||||
if (c->extra_fields_mtime != NSEC_INFINITY) {
|
||||
if (stat(p, &st) < 0) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (timespec_load_nsec(&st.st_mtim) == c->extra_fields_mtime)
|
||||
return 0;
|
||||
}
|
||||
|
||||
f = fopen(p, "re");
|
||||
if (!f) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (fstat(fileno(f), &st) < 0) /* The file might have been replaced since the stat() above, let's get a new
|
||||
* one, that matches the stuff we are reading */
|
||||
return -errno;
|
||||
|
||||
r = read_full_stream(f, (char**) &data, &size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
q = data, left = size;
|
||||
while (left > 0) {
|
||||
uint8_t *field, *eq;
|
||||
uint64_t v, n;
|
||||
|
||||
if (left < sizeof(uint64_t))
|
||||
return -EBADMSG;
|
||||
|
||||
v = unaligned_read_le64(q);
|
||||
if (v < 2)
|
||||
return -EBADMSG;
|
||||
|
||||
n = sizeof(uint64_t) + v;
|
||||
if (left < n)
|
||||
return -EBADMSG;
|
||||
|
||||
field = q + sizeof(uint64_t);
|
||||
|
||||
eq = memchr(field, '=', v);
|
||||
if (!eq)
|
||||
return -EBADMSG;
|
||||
|
||||
if (!journal_field_valid((const char *) field, eq - field, false))
|
||||
return -EBADMSG;
|
||||
|
||||
if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec+1))
|
||||
return -ENOMEM;
|
||||
|
||||
iovec[n_iovec++] = IOVEC_MAKE(field, v);
|
||||
|
||||
left -= n, q += n;
|
||||
}
|
||||
|
||||
free(c->extra_fields_iovec);
|
||||
free(c->extra_fields_data);
|
||||
|
||||
c->extra_fields_iovec = iovec;
|
||||
c->extra_fields_n_iovec = n_iovec;
|
||||
c->extra_fields_data = data;
|
||||
c->extra_fields_mtime = timespec_load_nsec(&st.st_mtim);
|
||||
|
||||
iovec = NULL;
|
||||
data = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void client_context_really_refresh(
|
||||
|
@ -356,6 +473,8 @@ static void client_context_really_refresh(
|
|||
|
||||
(void) client_context_read_cgroup(s, c, unit_id);
|
||||
(void) client_context_read_invocation_id(s, c);
|
||||
(void) client_context_read_log_level_max(s, c);
|
||||
(void) client_context_read_extra_fields(s, c);
|
||||
|
||||
c->timestamp = timestamp;
|
||||
|
||||
|
|
|
@ -60,6 +60,13 @@ struct ClientContext {
|
|||
|
||||
char *label;
|
||||
size_t label_size;
|
||||
|
||||
int log_level_max;
|
||||
|
||||
struct iovec *extra_fields_iovec;
|
||||
size_t extra_fields_n_iovec;
|
||||
void *extra_fields_data;
|
||||
nsec_t extra_fields_mtime;
|
||||
};
|
||||
|
||||
int client_context_get(
|
||||
|
@ -90,3 +97,17 @@ void client_context_maybe_refresh(
|
|||
|
||||
void client_context_acquire_default(Server *s);
|
||||
void client_context_flush_all(Server *s);
|
||||
|
||||
static inline size_t client_context_extra_fields_n_iovec(const ClientContext *c) {
|
||||
return c ? c->extra_fields_n_iovec : 0;
|
||||
}
|
||||
|
||||
static inline bool client_context_test_priority(const ClientContext *c, int priority) {
|
||||
if (!c)
|
||||
return true;
|
||||
|
||||
if (c->log_level_max < 0)
|
||||
return true;
|
||||
|
||||
return LOG_PRI(priority) <= c->log_level_max;
|
||||
}
|
||||
|
|
|
@ -109,15 +109,16 @@ static bool is_us(const char *pid) {
|
|||
}
|
||||
|
||||
static void dev_kmsg_record(Server *s, const char *p, size_t l) {
|
||||
struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS];
|
||||
|
||||
_cleanup_free_ char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL, *identifier = NULL, *pid = NULL;
|
||||
int priority, r;
|
||||
unsigned n = 0, z = 0, j;
|
||||
struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS];
|
||||
char *kernel_device = NULL;
|
||||
unsigned long long usec;
|
||||
size_t n = 0, z = 0, j;
|
||||
int priority, r;
|
||||
char *e, *f, *k;
|
||||
uint64_t serial;
|
||||
size_t pl;
|
||||
char *kernel_device = NULL;
|
||||
|
||||
assert(s);
|
||||
assert(p);
|
||||
|
@ -155,7 +156,7 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
|
|||
|
||||
/* Did we lose any? */
|
||||
if (serial > *s->kernel_seqnum)
|
||||
server_driver_message(s,
|
||||
server_driver_message(s, 0,
|
||||
"MESSAGE_ID=" SD_MESSAGE_JOURNAL_MISSED_STR,
|
||||
LOG_MESSAGE("Missed %"PRIu64" kernel messages",
|
||||
serial - *s->kernel_seqnum),
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "fs-util.h"
|
||||
#include "io-util.h"
|
||||
#include "journal-importer.h"
|
||||
#include "journal-util.h"
|
||||
#include "journald-console.h"
|
||||
#include "journald-kmsg.h"
|
||||
#include "journald-native.h"
|
||||
|
@ -43,41 +44,6 @@
|
|||
#include "string-util.h"
|
||||
#include "unaligned.h"
|
||||
|
||||
bool valid_user_field(const char *p, size_t l, bool allow_protected) {
|
||||
const char *a;
|
||||
|
||||
/* We kinda enforce POSIX syntax recommendations for
|
||||
environment variables here, but make a couple of additional
|
||||
requirements.
|
||||
|
||||
http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
|
||||
|
||||
/* No empty field names */
|
||||
if (l <= 0)
|
||||
return false;
|
||||
|
||||
/* Don't allow names longer than 64 chars */
|
||||
if (l > 64)
|
||||
return false;
|
||||
|
||||
/* Variables starting with an underscore are protected */
|
||||
if (!allow_protected && p[0] == '_')
|
||||
return false;
|
||||
|
||||
/* Don't allow digits as first character */
|
||||
if (p[0] >= '0' && p[0] <= '9')
|
||||
return false;
|
||||
|
||||
/* Only allow A-Z0-9 and '_' */
|
||||
for (a = p; a < p + l; a++)
|
||||
if ((*a < 'A' || *a > 'Z') &&
|
||||
(*a < '0' || *a > '9') &&
|
||||
*a != '_')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool allow_object_pid(const struct ucred *ucred) {
|
||||
return ucred && ucred->uid == 0;
|
||||
}
|
||||
|
@ -148,19 +114,17 @@ static int server_process_entry(
|
|||
const struct timeval *tv,
|
||||
const char *label, size_t label_len) {
|
||||
|
||||
/* Process a single entry from a native message.
|
||||
* Returns 0 if nothing special happened and the message processing should continue,
|
||||
* and a negative or positive value otherwise.
|
||||
/* Process a single entry from a native message. Returns 0 if nothing special happened and the message
|
||||
* processing should continue, and a negative or positive value otherwise.
|
||||
*
|
||||
* Note that *remaining is altered on both success and failure. */
|
||||
|
||||
struct iovec *iovec = NULL;
|
||||
unsigned n = 0, j, tn = (unsigned) -1;
|
||||
const char *p;
|
||||
size_t m = 0, entry_size = 0;
|
||||
int priority = LOG_INFO;
|
||||
size_t n = 0, j, tn = (size_t) -1, m = 0, entry_size = 0;
|
||||
char *identifier = NULL, *message = NULL;
|
||||
struct iovec *iovec = NULL;
|
||||
int priority = LOG_INFO;
|
||||
pid_t object_pid = 0;
|
||||
const char *p;
|
||||
int r = 0;
|
||||
|
||||
p = buffer;
|
||||
|
@ -194,26 +158,25 @@ static int server_process_entry(
|
|||
/* A property follows */
|
||||
|
||||
/* n existing properties, 1 new, +1 for _TRANSPORT */
|
||||
if (!GREEDY_REALLOC(iovec, m, n + 2 + N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS)) {
|
||||
if (!GREEDY_REALLOC(iovec, m,
|
||||
n + 2 +
|
||||
N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS +
|
||||
client_context_extra_fields_n_iovec(context))) {
|
||||
r = log_oom();
|
||||
break;
|
||||
}
|
||||
|
||||
q = memchr(p, '=', e - p);
|
||||
if (q) {
|
||||
if (valid_user_field(p, q - p, false)) {
|
||||
if (journal_field_valid(p, q - p, false)) {
|
||||
size_t l;
|
||||
|
||||
l = e - p;
|
||||
|
||||
/* If the field name starts with an
|
||||
* underscore, skip the variable,
|
||||
* since that indicates a trusted
|
||||
* field */
|
||||
iovec[n].iov_base = (char*) p;
|
||||
iovec[n].iov_len = l;
|
||||
/* If the field name starts with an underscore, skip the variable, since that indicates
|
||||
* a trusted field */
|
||||
iovec[n++] = IOVEC_MAKE((char*) p, l);
|
||||
entry_size += l;
|
||||
n++;
|
||||
|
||||
server_process_entry_meta(p, l, ucred,
|
||||
&priority,
|
||||
|
@ -257,7 +220,7 @@ static int server_process_entry(
|
|||
k[e - p] = '=';
|
||||
memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
|
||||
|
||||
if (valid_user_field(p, e - p, false)) {
|
||||
if (journal_field_valid(p, e - p, false)) {
|
||||
iovec[n].iov_base = k;
|
||||
iovec[n].iov_len = (e - p) + 1 + l;
|
||||
entry_size += iovec[n].iov_len;
|
||||
|
@ -281,13 +244,17 @@ static int server_process_entry(
|
|||
goto finish;
|
||||
}
|
||||
|
||||
if (!client_context_test_priority(context, priority)) {
|
||||
r = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
tn = n++;
|
||||
iovec[tn] = IOVEC_MAKE_STRING("_TRANSPORT=journal");
|
||||
entry_size += strlen("_TRANSPORT=journal");
|
||||
|
||||
if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */
|
||||
log_debug("Entry is too big with %u properties and %zu bytes, ignoring.",
|
||||
n, entry_size);
|
||||
log_debug("Entry is too big with %zu properties and %zu bytes, ignoring.", n, entry_size);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
|
|
|
@ -219,7 +219,8 @@ void server_space_usage_message(Server *s, JournalStorage *storage) {
|
|||
format_bytes(fb5, sizeof(fb5), storage->space.limit);
|
||||
format_bytes(fb6, sizeof(fb6), storage->space.available);
|
||||
|
||||
server_driver_message(s, "MESSAGE_ID=" SD_MESSAGE_JOURNAL_USAGE_STR,
|
||||
server_driver_message(s, 0,
|
||||
"MESSAGE_ID=" SD_MESSAGE_JOURNAL_USAGE_STR,
|
||||
LOG_MESSAGE("%s (%s) is %s, max %s, %s free.",
|
||||
storage->name, storage->path, fb1, fb5, fb6),
|
||||
"JOURNAL_NAME=%s", storage->name,
|
||||
|
@ -752,7 +753,7 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned
|
|||
|
||||
static void dispatch_message_real(
|
||||
Server *s,
|
||||
struct iovec *iovec, unsigned n, unsigned m,
|
||||
struct iovec *iovec, size_t n, size_t m,
|
||||
const ClientContext *c,
|
||||
const struct timeval *tv,
|
||||
int priority,
|
||||
|
@ -765,7 +766,10 @@ static void dispatch_message_real(
|
|||
assert(s);
|
||||
assert(iovec);
|
||||
assert(n > 0);
|
||||
assert(n + N_IOVEC_META_FIELDS + (pid_is_valid(object_pid) ? N_IOVEC_OBJECT_FIELDS : 0) <= m);
|
||||
assert(n +
|
||||
N_IOVEC_META_FIELDS +
|
||||
(pid_is_valid(object_pid) ? N_IOVEC_OBJECT_FIELDS : 0) +
|
||||
client_context_extra_fields_n_iovec(c) <= m);
|
||||
|
||||
if (c) {
|
||||
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->pid, pid_t, pid_is_valid, PID_FMT, "_PID");
|
||||
|
@ -791,6 +795,11 @@ static void dispatch_message_real(
|
|||
IOVEC_ADD_STRING_FIELD(iovec, n, c->user_slice, "_SYSTEMD_USER_SLICE");
|
||||
|
||||
IOVEC_ADD_ID128_FIELD(iovec, n, c->invocation_id, "_SYSTEMD_INVOCATION_ID");
|
||||
|
||||
if (c->extra_fields_n_iovec > 0) {
|
||||
memcpy(iovec + n, c->extra_fields_iovec, c->extra_fields_n_iovec * sizeof(struct iovec));
|
||||
n += c->extra_fields_n_iovec;
|
||||
}
|
||||
}
|
||||
|
||||
assert(n <= m);
|
||||
|
@ -859,16 +868,19 @@ static void dispatch_message_real(
|
|||
write_to_journal(s, journal_uid, iovec, n, priority);
|
||||
}
|
||||
|
||||
void server_driver_message(Server *s, const char *message_id, const char *format, ...) {
|
||||
void server_driver_message(Server *s, pid_t object_pid, const char *message_id, const char *format, ...) {
|
||||
|
||||
struct iovec iovec[N_IOVEC_META_FIELDS + 5 + N_IOVEC_PAYLOAD_FIELDS];
|
||||
unsigned n = 0, m;
|
||||
struct iovec *iovec;
|
||||
size_t n = 0, k, m;
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(format);
|
||||
|
||||
m = N_IOVEC_META_FIELDS + 5 + N_IOVEC_PAYLOAD_FIELDS + client_context_extra_fields_n_iovec(s->my_context);
|
||||
iovec = newa(struct iovec, m);
|
||||
|
||||
assert_cc(3 == LOG_FAC(LOG_DAEMON));
|
||||
iovec[n++] = IOVEC_MAKE_STRING("SYSLOG_FACILITY=3");
|
||||
iovec[n++] = IOVEC_MAKE_STRING("SYSLOG_IDENTIFIER=systemd-journald");
|
||||
|
@ -879,18 +891,18 @@ void server_driver_message(Server *s, const char *message_id, const char *format
|
|||
|
||||
if (message_id)
|
||||
iovec[n++] = IOVEC_MAKE_STRING(message_id);
|
||||
m = n;
|
||||
k = n;
|
||||
|
||||
va_start(ap, format);
|
||||
r = log_format_iovec(iovec, ELEMENTSOF(iovec), &n, false, 0, format, ap);
|
||||
r = log_format_iovec(iovec, m, &n, false, 0, format, ap);
|
||||
/* Error handling below */
|
||||
va_end(ap);
|
||||
|
||||
if (r >= 0)
|
||||
dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), s->my_context, NULL, LOG_INFO, 0);
|
||||
dispatch_message_real(s, iovec, n, m, s->my_context, NULL, LOG_INFO, object_pid);
|
||||
|
||||
while (m < n)
|
||||
free(iovec[m++].iov_base);
|
||||
while (k < n)
|
||||
free(iovec[k++].iov_base);
|
||||
|
||||
if (r < 0) {
|
||||
/* We failed to format the message. Emit a warning instead. */
|
||||
|
@ -901,13 +913,13 @@ void server_driver_message(Server *s, const char *message_id, const char *format
|
|||
n = 3;
|
||||
iovec[n++] = IOVEC_MAKE_STRING("PRIORITY=4");
|
||||
iovec[n++] = IOVEC_MAKE_STRING(buf);
|
||||
dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), s->my_context, NULL, LOG_INFO, 0);
|
||||
dispatch_message_real(s, iovec, n, m, s->my_context, NULL, LOG_INFO, object_pid);
|
||||
}
|
||||
}
|
||||
|
||||
void server_dispatch_message(
|
||||
Server *s,
|
||||
struct iovec *iovec, unsigned n, unsigned m,
|
||||
struct iovec *iovec, size_t n, size_t m,
|
||||
ClientContext *c,
|
||||
const struct timeval *tv,
|
||||
int priority,
|
||||
|
@ -939,8 +951,10 @@ void server_dispatch_message(
|
|||
|
||||
/* Write a suppression message if we suppressed something */
|
||||
if (rl > 1)
|
||||
server_driver_message(s, "MESSAGE_ID=" SD_MESSAGE_JOURNAL_DROPPED_STR,
|
||||
LOG_MESSAGE("Suppressed %u messages from %s", rl - 1, c->unit),
|
||||
server_driver_message(s, c->pid,
|
||||
"MESSAGE_ID=" SD_MESSAGE_JOURNAL_DROPPED_STR,
|
||||
LOG_MESSAGE("Suppressed %i messages from %s", rl - 1, c->unit),
|
||||
LOG_MESSAGE("N_DROPPED=%i", rl - 1),
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
@ -1038,7 +1052,7 @@ finish:
|
|||
|
||||
sd_journal_close(j);
|
||||
|
||||
server_driver_message(s, NULL,
|
||||
server_driver_message(s, 0, NULL,
|
||||
LOG_MESSAGE("Time spent on flushing to /var is %s for %u entries.",
|
||||
format_timespan(ts, sizeof(ts), now(CLOCK_MONOTONIC) - start, 0),
|
||||
n),
|
||||
|
|
|
@ -187,8 +187,8 @@ struct Server {
|
|||
#define N_IOVEC_OBJECT_FIELDS 14
|
||||
#define N_IOVEC_PAYLOAD_FIELDS 15
|
||||
|
||||
void server_dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, ClientContext *c, const struct timeval *tv, int priority, pid_t object_pid);
|
||||
void server_driver_message(Server *s, const char *message_id, const char *format, ...) _printf_(3,0) _sentinel_;
|
||||
void server_dispatch_message(Server *s, struct iovec *iovec, size_t n, size_t m, ClientContext *c, const struct timeval *tv, int priority, pid_t object_pid);
|
||||
void server_driver_message(Server *s, pid_t object_pid, const char *message_id, const char *format, ...) _sentinel_;
|
||||
|
||||
/* gperf lookup function */
|
||||
const struct ConfigPerfItem* journald_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
|
||||
|
|
|
@ -251,22 +251,33 @@ fail:
|
|||
}
|
||||
|
||||
static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_break) {
|
||||
struct iovec iovec[N_IOVEC_META_FIELDS + 7];
|
||||
struct iovec *iovec;
|
||||
int priority;
|
||||
char syslog_priority[] = "PRIORITY=\0";
|
||||
char syslog_facility[sizeof("SYSLOG_FACILITY=")-1 + DECIMAL_STR_MAX(int) + 1];
|
||||
_cleanup_free_ char *message = NULL, *syslog_identifier = NULL;
|
||||
unsigned n = 0;
|
||||
size_t n = 0, m;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(p);
|
||||
|
||||
if (s->context)
|
||||
(void) client_context_maybe_refresh(s->server, s->context, NULL, NULL, 0, NULL, USEC_INFINITY);
|
||||
else if (pid_is_valid(s->ucred.pid)) {
|
||||
r = client_context_acquire(s->server, s->ucred.pid, &s->ucred, s->label, strlen_ptr(s->label), s->unit_id, &s->context);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to acquire client context, ignoring: %m");
|
||||
}
|
||||
|
||||
priority = s->priority;
|
||||
|
||||
if (s->level_prefix)
|
||||
syslog_parse_priority(&p, &priority, false);
|
||||
|
||||
if (!client_context_test_priority(s->context, priority))
|
||||
return 0;
|
||||
|
||||
if (isempty(p))
|
||||
return 0;
|
||||
|
||||
|
@ -282,6 +293,9 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea
|
|||
if (s->server->forward_to_wall)
|
||||
server_forward_wall(s->server, priority, s->identifier, p, &s->ucred);
|
||||
|
||||
m = N_IOVEC_META_FIELDS + 7 + client_context_extra_fields_n_iovec(s->context);
|
||||
iovec = newa(struct iovec, m);
|
||||
|
||||
iovec[n++] = IOVEC_MAKE_STRING("_TRANSPORT=stdout");
|
||||
iovec[n++] = IOVEC_MAKE_STRING(s->id_field);
|
||||
|
||||
|
@ -315,15 +329,7 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea
|
|||
if (message)
|
||||
iovec[n++] = IOVEC_MAKE_STRING(message);
|
||||
|
||||
if (s->context)
|
||||
(void) client_context_maybe_refresh(s->server, s->context, NULL, NULL, 0, NULL, USEC_INFINITY);
|
||||
else if (pid_is_valid(s->ucred.pid)) {
|
||||
r = client_context_acquire(s->server, s->ucred.pid, &s->ucred, s->label, strlen_ptr(s->label), s->unit_id, &s->context);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to acquire client context, ignoring: %m");
|
||||
}
|
||||
|
||||
server_dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), s->context, NULL, priority, 0);
|
||||
server_dispatch_message(s->server, iovec, n, m, s->context, NULL, priority, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -324,18 +324,27 @@ void server_process_syslog_message(
|
|||
syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
|
||||
const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
|
||||
_cleanup_free_ char *identifier = NULL, *pid = NULL;
|
||||
struct iovec iovec[N_IOVEC_META_FIELDS + 6];
|
||||
int priority = LOG_USER | LOG_INFO, r;
|
||||
ClientContext *context = NULL;
|
||||
struct iovec *iovec;
|
||||
const char *orig;
|
||||
unsigned n = 0;
|
||||
size_t n = 0, m;
|
||||
|
||||
assert(s);
|
||||
assert(buf);
|
||||
|
||||
if (ucred && pid_is_valid(ucred->pid)) {
|
||||
r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid);
|
||||
}
|
||||
|
||||
orig = buf;
|
||||
syslog_parse_priority(&buf, &priority, true);
|
||||
|
||||
if (!client_context_test_priority(context, priority))
|
||||
return;
|
||||
|
||||
if (s->forward_to_syslog)
|
||||
forward_syslog_raw(s, priority, orig, ucred, tv);
|
||||
|
||||
|
@ -351,6 +360,9 @@ void server_process_syslog_message(
|
|||
if (s->forward_to_wall)
|
||||
server_forward_wall(s, priority, identifier, buf, ucred);
|
||||
|
||||
m = N_IOVEC_META_FIELDS + 6 + client_context_extra_fields_n_iovec(context);
|
||||
iovec = newa(struct iovec, m);
|
||||
|
||||
iovec[n++] = IOVEC_MAKE_STRING("_TRANSPORT=syslog");
|
||||
|
||||
xsprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
|
||||
|
@ -375,13 +387,7 @@ void server_process_syslog_message(
|
|||
if (message)
|
||||
iovec[n++] = IOVEC_MAKE_STRING(message);
|
||||
|
||||
if (ucred && pid_is_valid(ucred->pid)) {
|
||||
r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid);
|
||||
}
|
||||
|
||||
server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), context, tv, priority, 0);
|
||||
server_dispatch_message(s, iovec, n, m, context, tv, priority, 0);
|
||||
}
|
||||
|
||||
int server_open_syslog_socket(Server *s) {
|
||||
|
@ -449,7 +455,7 @@ void server_maybe_warn_forward_syslog_missed(Server *s) {
|
|||
if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
|
||||
return;
|
||||
|
||||
server_driver_message(s,
|
||||
server_driver_message(s, 0,
|
||||
"MESSAGE_ID=" SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR,
|
||||
LOG_MESSAGE("Forwarding to syslog missed %u messages.",
|
||||
s->n_forward_syslog_missed),
|
||||
|
|
|
@ -56,7 +56,7 @@ int main(int argc, char *argv[]) {
|
|||
server_flush_dev_kmsg(&server);
|
||||
|
||||
log_debug("systemd-journald running as pid "PID_FMT, getpid_cached());
|
||||
server_driver_message(&server,
|
||||
server_driver_message(&server, 0,
|
||||
"MESSAGE_ID=" SD_MESSAGE_JOURNAL_START_STR,
|
||||
LOG_MESSAGE("Journal started"),
|
||||
NULL);
|
||||
|
@ -115,7 +115,7 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
log_debug("systemd-journald stopped as pid "PID_FMT, getpid_cached());
|
||||
server_driver_message(&server,
|
||||
server_driver_message(&server, 0,
|
||||
"MESSAGE_ID=" SD_MESSAGE_JOURNAL_STOP_STR,
|
||||
LOG_MESSAGE("Journal stopped"),
|
||||
NULL);
|
||||
|
|
|
@ -156,6 +156,31 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
|||
r = sd_bus_message_append(m, "sv", n, "t", t);
|
||||
goto finish;
|
||||
|
||||
} else if (streq(field, "LogExtraFields")) {
|
||||
|
||||
r = sd_bus_message_append(m, "s", "LogExtraFields");
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = sd_bus_message_open_container(m, 'v', "aay");
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = sd_bus_message_open_container(m, 'a', "ay");
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = sd_bus_message_append_array(m, 'y', eq, strlen(eq));
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
goto finish;
|
||||
|
||||
} else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) {
|
||||
uint64_t bytes;
|
||||
|
||||
|
@ -363,7 +388,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
|||
|
||||
r = sd_bus_message_append(m, "v", "(bs)", ignore, s);
|
||||
|
||||
} else if (streq(field, "SyslogLevel")) {
|
||||
} else if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax")) {
|
||||
int level;
|
||||
|
||||
level = log_level_from_string(eq);
|
||||
|
|
|
@ -834,7 +834,6 @@ int config_parse_log_facility(
|
|||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
|
||||
int *o = data, x;
|
||||
|
||||
assert(filename);
|
||||
|
@ -865,7 +864,6 @@ int config_parse_log_level(
|
|||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
|
||||
int *o = data, x;
|
||||
|
||||
assert(filename);
|
||||
|
@ -879,7 +877,11 @@ int config_parse_log_level(
|
|||
return 0;
|
||||
}
|
||||
|
||||
*o = (*o & LOG_FACMASK) | x;
|
||||
if (*o < 0) /* if it wasn't initialized so far, assume zero facility */
|
||||
*o = x;
|
||||
else
|
||||
*o = (*o & LOG_FACMASK) | x;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -149,3 +149,41 @@ int journal_access_check_and_warn(sd_journal *j, bool quiet) {
|
|||
|
||||
return r;
|
||||
}
|
||||
|
||||
bool journal_field_valid(const char *p, size_t l, bool allow_protected) {
|
||||
const char *a;
|
||||
|
||||
/* We kinda enforce POSIX syntax recommendations for
|
||||
environment variables here, but make a couple of additional
|
||||
requirements.
|
||||
|
||||
http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
|
||||
|
||||
if (l == (size_t) -1)
|
||||
l = strlen(p);
|
||||
|
||||
/* No empty field names */
|
||||
if (l <= 0)
|
||||
return false;
|
||||
|
||||
/* Don't allow names longer than 64 chars */
|
||||
if (l > 64)
|
||||
return false;
|
||||
|
||||
/* Variables starting with an underscore are protected */
|
||||
if (!allow_protected && p[0] == '_')
|
||||
return false;
|
||||
|
||||
/* Don't allow digits as first character */
|
||||
if (p[0] >= '0' && p[0] <= '9')
|
||||
return false;
|
||||
|
||||
/* Only allow A-Z0-9 and '_' */
|
||||
for (a = p; a < p + l; a++)
|
||||
if ((*a < 'A' || *a > 'Z') &&
|
||||
(*a < '0' || *a > '9') &&
|
||||
*a != '_')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,10 @@
|
|||
***/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-journal.h"
|
||||
|
||||
bool journal_field_valid(const char *p, size_t l, bool allow_protected);
|
||||
|
||||
int journal_access_check_and_warn(sd_journal *j, bool quiet);
|
||||
|
|
|
@ -445,6 +445,73 @@ static void test_config_parse_exec(void) {
|
|||
manager_free(m);
|
||||
}
|
||||
|
||||
static void test_config_parse_log_extra_fields(void) {
|
||||
/* int config_parse_log_extra_fields(
|
||||
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) */
|
||||
|
||||
int r;
|
||||
|
||||
Manager *m = NULL;
|
||||
Unit *u = NULL;
|
||||
ExecContext c = {};
|
||||
|
||||
r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m);
|
||||
if (MANAGER_SKIP_TEST(r)) {
|
||||
log_notice_errno(r, "Skipping test: manager_new: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
assert_se(r >= 0);
|
||||
assert_se(manager_startup(m, NULL, NULL) >= 0);
|
||||
|
||||
assert_se(u = unit_new(m, sizeof(Service)));
|
||||
|
||||
log_info("/* %s – basic test */", __func__);
|
||||
r = config_parse_log_extra_fields(NULL, "fake", 1, "section", 1,
|
||||
"LValue", 0, "FOO=BAR \"QOOF=quux ' ' \"",
|
||||
&c, u);
|
||||
assert_se(r >= 0);
|
||||
assert_se(c.n_log_extra_fields == 2);
|
||||
assert_se(strneq(c.log_extra_fields[0].iov_base, "FOO=BAR", c.log_extra_fields[0].iov_len));
|
||||
assert_se(strneq(c.log_extra_fields[1].iov_base, "QOOF=quux ' ' ", c.log_extra_fields[1].iov_len));
|
||||
|
||||
log_info("/* %s – add some */", __func__);
|
||||
r = config_parse_log_extra_fields(NULL, "fake", 1, "section", 1,
|
||||
"LValue", 0, "FOO2=BAR2 QOOF2=quux ' '",
|
||||
&c, u);
|
||||
assert_se(r >= 0);
|
||||
assert_se(c.n_log_extra_fields == 4);
|
||||
assert_se(strneq(c.log_extra_fields[0].iov_base, "FOO=BAR", c.log_extra_fields[0].iov_len));
|
||||
assert_se(strneq(c.log_extra_fields[1].iov_base, "QOOF=quux ' ' ", c.log_extra_fields[1].iov_len));
|
||||
assert_se(strneq(c.log_extra_fields[2].iov_base, "FOO2=BAR2", c.log_extra_fields[2].iov_len));
|
||||
assert_se(strneq(c.log_extra_fields[3].iov_base, "QOOF2=quux", c.log_extra_fields[3].iov_len));
|
||||
|
||||
exec_context_dump(&c, stdout, " --> ");
|
||||
|
||||
log_info("/* %s – reset */", __func__);
|
||||
r = config_parse_log_extra_fields(NULL, "fake", 1, "section", 1,
|
||||
"LValue", 0, "",
|
||||
&c, u);
|
||||
assert_se(r >= 0);
|
||||
assert_se(c.n_log_extra_fields == 0);
|
||||
|
||||
exec_context_free_log_extra_fields(&c);
|
||||
|
||||
unit_free(u);
|
||||
manager_free(m);
|
||||
|
||||
log_info("/* %s – bye */", __func__);
|
||||
}
|
||||
|
||||
#define env_file_1 \
|
||||
"a=a\n" \
|
||||
"b=b\\\n" \
|
||||
|
@ -868,6 +935,7 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
r = test_unit_file_get_set();
|
||||
test_config_parse_exec();
|
||||
test_config_parse_log_extra_fields();
|
||||
test_config_parse_capability_set();
|
||||
test_config_parse_rlimit();
|
||||
test_config_parse_pass_environ();
|
||||
|
|
Loading…
Reference in a new issue