1
0
mirror of https://github.com/systemd/systemd synced 2025-10-07 20:54:45 +02:00

Compare commits

...

10 Commits

Author SHA1 Message Date
Matteo Croce
7aefb194e7 man/systemd.exec: explain how BPF token works
Add a small paragraph explaining how BPF token works, how it's being
created and its relationship between the BPF filesystem.
Move all the relevant documentation in the PrivateBPF= section and let
point all the BPFDelegate* options to that one.
2025-07-10 21:40:07 +02:00
Ubuntu
df5b3426f6 journald: support reloading configuration at runtime 2025-07-10 21:38:36 +02:00
Lennart Poettering
c35ceb84e3
Introduce ERRNO_IS_FS_WRITE_REFUSED(), and use it in binfmt_mounted() (#38117)
- This introduces ERRNO_IS_FS_WRITE_REFUSED(), and apply it where
usable.
- This makes unexpected errors in access_fd() called by binfmt_mounted()
propagated to the caller.
- Renames binfmt_mounted() to binfmt_mounted_and_writable(), as it also
checks the fs is writable.
- Voidifies one disable_binfmt() call in shutdown.c.
2025-07-10 21:38:13 +02:00
DaanDeMeyer
cc43510a13 userdb: Add userdb.transient credentials
To implement --bind-user in systemd-vmspawn, we need a transient
version of these credentials. These are useful when the home directory
of the user is mounted into the container/vm and every trace of the user
will be (mostly) gone again when the container/vm is shut down.
2025-07-10 21:36:09 +02:00
DaanDeMeyer
88444243b8 ruff: Default to python 3.7 version
For some use cases we still want python 3.7 compat so let's default
to that and only target python 3.9 in a few specific cases.
2025-07-10 18:09:17 +02:00
Li Tian
b6d4997683 Add --entry-type=type1|type2 option to kernel-install.
Both kernel-core and kernel-uki-virt call kernel-install upon removal. Need an additional argument to avoid complete removal for both traditional kernel and UKI.

Signed-off-by: Li Tian <litian@redhat.com>
2025-07-10 18:07:23 +02:00
Yu Watanabe
7425f76c4e shutdown: voidify disable_binfmt() 2025-07-10 14:02:00 +09:00
Yu Watanabe
9a33cbac7d binfmt-util: rename binfmt_mounted() -> binfmt_mounted_and_writable()
As it does not only check if binfmt_misc is mounted, but also check if
it is writable.
2025-07-10 14:02:00 +09:00
Yu Watanabe
bd068e2903 binfmt-util: propagate failure in access_fd()
It is not necessary to hide errors in access_fd(), and only acceptable
errors here are -EROFS, -EACCES, and -EPERM.
2025-07-10 14:02:00 +09:00
Yu Watanabe
e28d408c6b errno-util: introduce ERRNO_IS_NEG_FS_WRITE_REFUSED() 2025-07-10 14:02:00 +09:00
50 changed files with 954 additions and 219 deletions

View File

@ -218,6 +218,22 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
<xi:include href="version-info.xml" xpointer="v228"/></listitem> <xi:include href="version-info.xml" xpointer="v228"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term>SIGHUP</term>
<listitem><para>Upon reception of the <constant>SIGHUP</constant> process signal
<command>systemd-journald</command> will reload its configuration values
and update the kernel log buffer and journals to reflect the new configuration.
If <varname>ReadKmsg=</varname> has changed, the kernel log buffer will be flushed
and updated as part of the reload. The active journal (e.g., persistent, volatile)
will continue to be used with the updated configuration.
However, if the storage mode has changed from persistent to volatile
and the current journal in use is the persistent journal, then the active journal will
be switched to the volatile journal.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>

View File

@ -2561,10 +2561,16 @@ RestrictNamespaces=~cgroup net</programlisting>
<listitem><para>Takes a boolean argument. If set, mount a private instance of the BPF filesystem <listitem><para>Takes a boolean argument. If set, mount a private instance of the BPF filesystem
on <filename>/sys/fs/bpf/</filename>, effectively hiding the host bpffs which contains information on <filename>/sys/fs/bpf/</filename>, effectively hiding the host bpffs which contains information
about loaded programs and maps. Otherwise, if <varname>ProtectKernelTunables=</varname> is set, the about loaded programs and maps. Otherwise, if <varname>ProtectKernelTunables=</varname> is set, the
instance from the host is inherited but mounted read-only.</para> instance from the host is inherited but mounted read-only. Defaults to false.</para>
<para>This can be used together with the bpffs delegate feature to choose what BPF functions are
available to the unit's processes. When mounting the BPF filesystem with the fsopen() API, four mount
options can be specified to set a list of BPF commands, maps, programs and attachment types that are
allowed to be used. Processes needs to get a file descriptor for the bpffs mountpoint and use that to
get a token which will enable for that user namespace the BPF functionalities choosen upon bpffs mount.
A more detailed explanation of the feature can be found in this
<ulink url="https://lwn.net/Articles/947173/">LWN post</ulink>.</para>
<para>This can be used together with the BPF delegate feature to choose what BPF functions are
available to the unit's processes. Defaults to false.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem> <xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry> </varlistentry>
@ -2575,10 +2581,10 @@ RestrictNamespaces=~cgroup net</programlisting>
<listitem><para>Accepts a list of BPF commands to allow or <literal>any</literal> to allow everything. <listitem><para>Accepts a list of BPF commands to allow or <literal>any</literal> to allow everything.
Defaults to none. The accepted values are: Defaults to none. The accepted values are:
<xi:include href="bpf-delegate.xml" xpointer="bpf_delegate_cmd"/> <xi:include href="bpf-delegate.xml" xpointer="bpf_delegate_cmd"/>
Requires <varname>PrivateBPF=</varname> enabled to be effective. This will set the <constant>delegate_cmds</constant> bpffs mount option.</para>
This will set the <constant>delegate_cmds</constant> bpffs mount option.
A more detailed explanation of the feature can be found in this <para>Requires <varname>PrivateBPF=yes</varname> to be effective,
<ulink url="https://lwn.net/Articles/947173/">LWN post</ulink>.</para> see <varname>PrivateBPF=</varname> more details.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem> <xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry> </varlistentry>
@ -2589,8 +2595,10 @@ RestrictNamespaces=~cgroup net</programlisting>
<listitem><para>Accepts a list of BPF maps to allow or <literal>any</literal> to allow everything. <listitem><para>Accepts a list of BPF maps to allow or <literal>any</literal> to allow everything.
Defaults to none. The accepted values are: Defaults to none. The accepted values are:
<xi:include href="bpf-delegate.xml" xpointer="bpf_delegate_map_type"/> <xi:include href="bpf-delegate.xml" xpointer="bpf_delegate_map_type"/>
This will set the <constant>delegate_maps</constant> bpffs mount option. This will set the <constant>delegate_maps</constant> bpffs mount option.</para>
See <varname>BPFDelegateCommands=</varname> for dependencies and more details.</para>
<para>Requires <varname>PrivateBPF=yes</varname> to be effective,
see <varname>PrivateBPF=</varname> more details.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem> <xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry> </varlistentry>
@ -2601,8 +2609,10 @@ RestrictNamespaces=~cgroup net</programlisting>
<listitem><para>Accepts a list of BPF programs to allow or <literal>any</literal> to allow everything. <listitem><para>Accepts a list of BPF programs to allow or <literal>any</literal> to allow everything.
Defaults to none. The accepted values are: Defaults to none. The accepted values are:
<xi:include href="bpf-delegate.xml" xpointer="bpf_delegate_prog_type"/> <xi:include href="bpf-delegate.xml" xpointer="bpf_delegate_prog_type"/>
This will set the <constant>delegate_progs</constant> bpffs mount option. This will set the <constant>delegate_progs</constant> bpffs mount option.</para>
See <varname>BPFDelegateCommands=</varname> for dependencies and more details.</para>
<para>Requires <varname>PrivateBPF=yes</varname> to be effective,
see <varname>PrivateBPF=</varname> more details.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem> <xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry> </varlistentry>
@ -2613,8 +2623,10 @@ RestrictNamespaces=~cgroup net</programlisting>
<listitem><para>Accepts a list of BPF attach points to allow or <literal>any</literal> to allow everything. <listitem><para>Accepts a list of BPF attach points to allow or <literal>any</literal> to allow everything.
Defaults to none. The accepted values are: Defaults to none. The accepted values are:
<xi:include href="bpf-delegate.xml" xpointer="bpf_delegate_attach_type"/> <xi:include href="bpf-delegate.xml" xpointer="bpf_delegate_attach_type"/>
This will set the <constant>delegate_attachs</constant> bpffs mount option. This will set the <constant>delegate_attachs</constant> bpffs mount option.</para>
See <varname>BPFDelegateCommands=</varname> for dependencies and more details.</para>
<para>Requires <varname>PrivateBPF=yes</varname> to be effective,
see <varname>PrivateBPF=</varname> more details.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem> <xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry> </varlistentry>

View File

@ -475,6 +475,8 @@
<varlistentry> <varlistentry>
<term><varname>userdb.user.*</varname></term> <term><varname>userdb.user.*</varname></term>
<term><varname>userdb.group.*</varname></term> <term><varname>userdb.group.*</varname></term>
<term><varname>userdb.transient.user.*</varname></term>
<term><varname>userdb.transient.group.*</varname></term>
<listitem> <listitem>
<para>Configure JSON user and group records. Read by <para>Configure JSON user and group records. Read by
@ -499,7 +501,10 @@
<literal>userdb.user.foobar</literal> the suffix is <literal>foobar</literal>) must match the user <literal>userdb.user.foobar</literal> the suffix is <literal>foobar</literal>) must match the user
or group name field from the user or group record.</para> or group name field from the user or group record.</para>
<para>Note that the records are created in <filename>/etc/userdb/</filename> <para>Note that the records created for <varname>userdb.user.*</varname> and
<varname>userdb.group.*</varname> credentials are created in <filename>/etc/userdb/</filename> and
the records created for <varname>userdb.transient.user.*</varname> and
<varname>userdb.transient.group.*</varname> are created in <filename>/run/userdb/</filename>
(<filename>/etc/passwd</filename> and <filename>/etc/group</filename> are not modified).</para> (<filename>/etc/passwd</filename> and <filename>/etc/group</filename> are not modified).</para>
<xi:include href="version-info.xml" xpointer="v258"/> <xi:include href="version-info.xml" xpointer="v258"/>

View File

@ -1,6 +1,11 @@
target-version = "py39" target-version = "py37"
line-length = 109 line-length = 109
lint.select = ["E", "F", "I", "UP"] lint.select = ["E", "F", "I", "UP"]
[format] [format]
quote-style = "single" quote-style = "single"
[per-file-target-version]
"src/ukify/*.py" = "py39"
"test/**/integration-test-wrapper.py" = "py39"
"test/test-udev.py" = "py39"

View File

@ -21,6 +21,15 @@ _kernel_install() {
local comps local comps
local MACHINE_ID local MACHINE_ID
local cur=${COMP_WORDS[COMP_CWORD]} local cur=${COMP_WORDS[COMP_CWORD]}
local prev=${COMP_WORDS[COMP_CWORD-1]}
local entry_types="type1 type2 all"
case "$prev" in
--entry-type)
COMPREPLY=( $(compgen -W "$entry_types" -- "$cur") )
return 0
;;
esac
case $COMP_CWORD in case $COMP_CWORD in
1) 1)
@ -42,6 +51,11 @@ _kernel_install() {
;; ;;
esac esac
if [[ "${COMP_WORDS[1]}" == "remove" ]] && [[ $cur == -* ]]; then
COMPREPLY=( $(compgen -W '--entry-type' -- "$cur") )
return 0
fi
COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0 return 0
} }

View File

@ -188,6 +188,12 @@ static inline bool ERRNO_IS_NEG_PRIVILEGE(intmax_t r) {
} }
_DEFINE_ABS_WRAPPER(PRIVILEGE); _DEFINE_ABS_WRAPPER(PRIVILEGE);
/* Three different errors for writing on a filesystem */
static inline bool ERRNO_IS_NEG_FS_WRITE_REFUSED(intmax_t r) {
return r == -EROFS || ERRNO_IS_NEG_PRIVILEGE(r);
}
_DEFINE_ABS_WRAPPER(FS_WRITE_REFUSED);
/* Three different errors for "not enough disk space" */ /* Three different errors for "not enough disk space" */
static inline bool ERRNO_IS_NEG_DISK_SPACE(intmax_t r) { static inline bool ERRNO_IS_NEG_DISK_SPACE(intmax_t r) {
return IN_SET(r, return IN_SET(r,

View File

@ -193,10 +193,10 @@ static int parse_argv(int argc, char *argv[]) {
return 1; return 1;
} }
static int binfmt_mounted_warn(void) { static int binfmt_mounted_and_writable_warn(void) {
int r; int r;
r = binfmt_mounted(); r = binfmt_mounted_and_writable();
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to check if /proc/sys/fs/binfmt_misc is mounted: %m"); return log_error_errno(r, "Failed to check if /proc/sys/fs/binfmt_misc is mounted: %m");
if (r == 0) if (r == 0)
@ -222,7 +222,7 @@ static int run(int argc, char *argv[]) {
return disable_binfmt(); return disable_binfmt();
if (argc > optind) { if (argc > optind) {
r = binfmt_mounted_warn(); r = binfmt_mounted_and_writable_warn();
if (r <= 0) if (r <= 0)
return r; return r;
@ -239,7 +239,7 @@ static int run(int argc, char *argv[]) {
if (arg_cat_flags != CAT_CONFIG_OFF) if (arg_cat_flags != CAT_CONFIG_OFF)
return cat_config(files); return cat_config(files);
r = binfmt_mounted_warn(); r = binfmt_mounted_and_writable_warn();
if (r <= 0) if (r <= 0)
return r; return r;

View File

@ -62,7 +62,7 @@
/* Returns the log level to use when cgroup attribute writes fail. When an attribute is missing or we have access /* Returns the log level to use when cgroup attribute writes fail. When an attribute is missing or we have access
* problems we downgrade to LOG_DEBUG. This is supposed to be nice to container managers and kernels which want to mask * problems we downgrade to LOG_DEBUG. This is supposed to be nice to container managers and kernels which want to mask
* out specific attributes from us. */ * out specific attributes from us. */
#define LOG_LEVEL_CGROUP_WRITE(r) (IN_SET(ABS(r), ENOENT, EROFS, EACCES, EPERM) ? LOG_DEBUG : LOG_WARNING) #define LOG_LEVEL_CGROUP_WRITE(r) ((ABS(r) == ENOENT || ERRNO_IS_FS_WRITE_REFUSED(r)) ? LOG_DEBUG : LOG_WARNING)
static void unit_remove_from_cgroup_empty_queue(Unit *u); static void unit_remove_from_cgroup_empty_queue(Unit *u);

View File

@ -1271,8 +1271,8 @@ static void bump_file_max_and_nr_open(void) {
* different, but the operation would fail silently.) */ * different, but the operation would fail silently.) */
r = sysctl_write("fs/file-max", LONG_MAX_STR); r = sysctl_write("fs/file-max", LONG_MAX_STR);
if (r < 0) if (r < 0)
log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, log_full_errno(ERRNO_IS_NEG_FS_WRITE_REFUSED(r) ? LOG_DEBUG : LOG_WARNING, r,
r, "Failed to bump fs.file-max, ignoring: %m"); "Failed to bump fs.file-max, ignoring: %m");
#endif #endif
#if BUMP_PROC_SYS_FS_NR_OPEN #if BUMP_PROC_SYS_FS_NR_OPEN
@ -1293,7 +1293,8 @@ static void bump_file_max_and_nr_open(void) {
continue; continue;
} }
if (r < 0) { if (r < 0) {
log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to bump fs.nr_open, ignoring: %m"); log_full_errno(ERRNO_IS_NEG_FS_WRITE_REFUSED(r) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to bump fs.nr_open, ignoring: %m");
break; break;
} }
@ -1534,7 +1535,7 @@ static int bump_unix_max_dgram_qlen(void) {
r = sysctl_write("net/unix/max_dgram_qlen", STRINGIFY(DEFAULT_UNIX_MAX_DGRAM_QLEN)); r = sysctl_write("net/unix/max_dgram_qlen", STRINGIFY(DEFAULT_UNIX_MAX_DGRAM_QLEN));
if (r < 0) if (r < 0)
return log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, return log_full_errno(ERRNO_IS_NEG_FS_WRITE_REFUSED(r) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to bump AF_UNIX datagram queue length, ignoring: %m"); "Failed to bump AF_UNIX datagram queue length, ignoring: %m");
return 1; return 1;

View File

@ -1269,7 +1269,7 @@ static void mount_enter_mounting(Mount *m) {
* when the path is on NFS. See issue #24120. All such errors will be logged in the debug level. */ * when the path is on NFS. See issue #24120. All such errors will be logged in the debug level. */
if (r < 0 && r != -EEXIST) if (r < 0 && r != -EEXIST)
log_unit_full_errno(UNIT(m), log_unit_full_errno(UNIT(m),
(r == -EROFS || ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_WARNING, ERRNO_IS_NEG_FS_WRITE_REFUSED(r) ? LOG_DEBUG : LOG_WARNING,
r, "Failed to make bind mount source '%s', ignoring: %m", p->what); r, "Failed to make bind mount source '%s', ignoring: %m", p->what);
} }

View File

@ -9,7 +9,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
fuzz_setup_logging(); fuzz_setup_logging();
assert_se(manager_new(&m) >= 0); assert_se(manager_new(&m, /* namespace= */ NULL) >= 0);
dummy_manager_init(m, data, size); dummy_manager_init(m, data, size);
process_audit_string(m, 0, m->buffer, size); process_audit_string(m, 0, m->buffer, size);

View File

@ -12,7 +12,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
fuzz_setup_logging(); fuzz_setup_logging();
assert_se(manager_new(&m) >= 0); assert_se(manager_new(&m, /* namespace= */ NULL) >= 0);
dummy_manager_init(m, data, size); dummy_manager_init(m, data, size);
dev_kmsg_record(m, m->buffer, size); dev_kmsg_record(m, m->buffer, size);

View File

@ -21,7 +21,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
fuzz_setup_logging(); fuzz_setup_logging();
assert_se(manager_new(&m) >= 0); assert_se(manager_new(&m, /* namespace= */ NULL) >= 0);
dummy_manager_init(m, NULL, 0); dummy_manager_init(m, NULL, 0);
sealed_fd = memfd_new_and_seal(NULL, data, size); sealed_fd = memfd_new_and_seal(NULL, data, size);

View File

@ -23,7 +23,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
fuzz_setup_logging(); fuzz_setup_logging();
assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, stream_fds) >= 0); assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, stream_fds) >= 0);
assert_se(manager_new(&m) >= 0); assert_se(manager_new(&m, /* namespace= */ NULL) >= 0);
dummy_manager_init(m, NULL, 0); dummy_manager_init(m, NULL, 0);
assert_se(stdout_stream_install(m, stream_fds[0], &stream) >= 0); assert_se(stdout_stream_install(m, stream_fds[0], &stream) >= 0);

View File

@ -9,7 +9,7 @@
void dummy_manager_init(Manager *m, const uint8_t *buffer, size_t size) { void dummy_manager_init(Manager *m, const uint8_t *buffer, size_t size) {
assert(m); assert(m);
m->storage = STORAGE_NONE; m->config.storage = STORAGE_NONE;
assert_se(sd_event_default(&m->event) >= 0); assert_se(sd_event_default(&m->event) >= 0);
if (buffer) { if (buffer) {
@ -33,7 +33,7 @@ void fuzz_journald_processing_function(
if (size == 0) if (size == 0)
return; return;
assert_se(manager_new(&m) >= 0); assert_se(manager_new(&m, /* namespace= */ NULL) >= 0);
dummy_manager_init(m, data, size); dummy_manager_init(m, data, size);
(*f)(m, m->buffer, size, ucred, tv, label, label_len); (*f)(m, m->buffer, size, ucred, tv, label, label_len);
} }

View File

@ -52,7 +52,7 @@ void manager_forward_console(
assert(m); assert(m);
assert(message); assert(message);
if (LOG_PRI(priority) > m->max_level_console) if (LOG_PRI(priority) > m->config.max_level_console)
return; return;
/* First: timestamp */ /* First: timestamp */

View File

@ -19,7 +19,7 @@ struct ConfigPerfItem;
%struct-type %struct-type
%includes %includes
%% %%
Journal.Storage, config_parse_storage, 0, offsetof(Manager, storage) Journal.Storage, config_parse_storage, 0, offsetof(Manager, config_by_conf.storage)
Journal.Compress, config_parse_compress, 0, offsetof(Manager, compress) Journal.Compress, config_parse_compress, 0, offsetof(Manager, compress)
Journal.Seal, config_parse_bool, 0, offsetof(Manager, seal) Journal.Seal, config_parse_bool, 0, offsetof(Manager, seal)
Journal.ReadKMsg, config_parse_bool, 0, offsetof(Manager, read_kmsg) Journal.ReadKMsg, config_parse_bool, 0, offsetof(Manager, read_kmsg)
@ -39,17 +39,17 @@ Journal.RuntimeKeepFree, config_parse_iec_uint64, 0, offsetof(Manager,
Journal.RuntimeMaxFiles, config_parse_uint64, 0, offsetof(Manager, runtime_storage.metrics.n_max_files) Journal.RuntimeMaxFiles, config_parse_uint64, 0, offsetof(Manager, runtime_storage.metrics.n_max_files)
Journal.MaxRetentionSec, config_parse_sec, 0, offsetof(Manager, max_retention_usec) Journal.MaxRetentionSec, config_parse_sec, 0, offsetof(Manager, max_retention_usec)
Journal.MaxFileSec, config_parse_sec, 0, offsetof(Manager, max_file_usec) Journal.MaxFileSec, config_parse_sec, 0, offsetof(Manager, max_file_usec)
Journal.ForwardToSyslog, config_parse_bool, 0, offsetof(Manager, forward_to_syslog) Journal.ForwardToSyslog, config_parse_bool, 0, offsetof(Manager, config_by_conf.forward_to_syslog)
Journal.ForwardToKMsg, config_parse_bool, 0, offsetof(Manager, forward_to_kmsg) Journal.ForwardToKMsg, config_parse_bool, 0, offsetof(Manager, config_by_conf.forward_to_kmsg)
Journal.ForwardToConsole, config_parse_bool, 0, offsetof(Manager, forward_to_console) Journal.ForwardToConsole, config_parse_bool, 0, offsetof(Manager, config_by_conf.forward_to_console)
Journal.ForwardToWall, config_parse_bool, 0, offsetof(Manager, forward_to_wall) Journal.ForwardToWall, config_parse_bool, 0, offsetof(Manager, config_by_conf.forward_to_wall)
Journal.ForwardToSocket, config_parse_forward_to_socket, 0, offsetof(Manager, forward_to_socket) Journal.ForwardToSocket, config_parse_forward_to_socket, 0, offsetof(Manager, config_by_conf.forward_to_socket)
Journal.TTYPath, config_parse_path, 0, offsetof(Manager, tty_path) Journal.TTYPath, config_parse_path, 0, offsetof(Manager, tty_path)
Journal.MaxLevelStore, config_parse_log_level, 0, offsetof(Manager, max_level_store) Journal.MaxLevelStore, config_parse_log_level, 0, offsetof(Manager, config_by_conf.max_level_store)
Journal.MaxLevelSyslog, config_parse_log_level, 0, offsetof(Manager, max_level_syslog) Journal.MaxLevelSyslog, config_parse_log_level, 0, offsetof(Manager, config_by_conf.max_level_syslog)
Journal.MaxLevelKMsg, config_parse_log_level, 0, offsetof(Manager, max_level_kmsg) Journal.MaxLevelKMsg, config_parse_log_level, 0, offsetof(Manager, config_by_conf.max_level_kmsg)
Journal.MaxLevelConsole, config_parse_log_level, 0, offsetof(Manager, max_level_console) Journal.MaxLevelConsole, config_parse_log_level, 0, offsetof(Manager, config_by_conf.max_level_console)
Journal.MaxLevelWall, config_parse_log_level, 0, offsetof(Manager, max_level_wall) Journal.MaxLevelWall, config_parse_log_level, 0, offsetof(Manager, config_by_conf.max_level_wall)
Journal.MaxLevelSocket, config_parse_log_level, 0, offsetof(Manager, max_level_socket) Journal.MaxLevelSocket, config_parse_log_level, 0, offsetof(Manager, config_by_conf.max_level_socket)
Journal.SplitMode, config_parse_split_mode, 0, offsetof(Manager, split_mode) Journal.SplitMode, config_parse_split_mode, 0, offsetof(Manager, split_mode)
Journal.LineMax, config_parse_line_max, 0, offsetof(Manager, line_max) Journal.LineMax, config_parse_line_max, 0, offsetof(Manager, line_max)

View File

@ -46,7 +46,7 @@ void manager_forward_kmsg(
assert(priority <= 999); assert(priority <= 999);
assert(message); assert(message);
if (_unlikely_(LOG_PRI(priority) > m->max_level_kmsg)) if (_unlikely_(LOG_PRI(priority) > m->config.max_level_kmsg))
return; return;
if (_unlikely_(m->dev_kmsg_fd < 0)) if (_unlikely_(m->dev_kmsg_fd < 0))
@ -128,7 +128,7 @@ void dev_kmsg_record(Manager *m, char *p, size_t l) {
if (r < 0 || priority < 0 || priority > 999) if (r < 0 || priority < 0 || priority > 999)
return; return;
if (m->forward_to_kmsg && LOG_FAC(priority) != LOG_KERN) if (m->config.forward_to_kmsg && LOG_FAC(priority) != LOG_KERN)
return; return;
/* seqnum */ /* seqnum */
@ -389,6 +389,10 @@ static int dispatch_dev_kmsg(sd_event_source *es, int fd, uint32_t revents, void
return manager_read_dev_kmsg(m); return manager_read_dev_kmsg(m);
} }
static mode_t manager_kmsg_mode(bool read_kmsg) {
return O_CLOEXEC|O_NONBLOCK|O_NOCTTY|(read_kmsg ? O_RDWR : O_WRONLY);
}
int manager_open_dev_kmsg(Manager *m) { int manager_open_dev_kmsg(Manager *m) {
int r; int r;
@ -396,7 +400,7 @@ int manager_open_dev_kmsg(Manager *m) {
assert(m->dev_kmsg_fd < 0); assert(m->dev_kmsg_fd < 0);
assert(!m->dev_kmsg_event_source); assert(!m->dev_kmsg_event_source);
mode_t mode = O_CLOEXEC|O_NONBLOCK|O_NOCTTY|(m->read_kmsg ? O_RDWR : O_WRONLY); mode_t mode = manager_kmsg_mode(m->read_kmsg);
_cleanup_close_ int fd = open("/dev/kmsg", mode); _cleanup_close_ int fd = open("/dev/kmsg", mode);
if (fd < 0) { if (fd < 0) {
@ -441,3 +445,33 @@ int manager_open_kernel_seqnum(Manager *m) {
return 0; return 0;
} }
int manager_reload_dev_kmsg(Manager *m) {
int r;
assert(m);
/* Check if the fd has not yet been initialized. If so, open /dev/kmsg. */
if (m->dev_kmsg_fd < 0)
return manager_open_dev_kmsg(m);
mode_t mode = manager_kmsg_mode(m->read_kmsg);
int flags = fcntl(m->dev_kmsg_fd, F_GETFL);
if (flags < 0)
/* Proceed with reload in case the flags have changed. */
log_warning_errno(errno, "Failed to get flags for /dev/kmsg, ignoring: %m");
else if ((flags & O_ACCMODE_STRICT) == mode)
/* Mode is the same. No-op. */
return 0;
/* Flush kmsg. */
r = manager_flush_dev_kmsg(m);
if (r < 0)
log_warning_errno(r, "Failed to flush /dev/kmsg on reload, ignoring: %m");
/* Set kmsg values to default. */
m->dev_kmsg_event_source = sd_event_source_disable_unref(m->dev_kmsg_event_source);
m->dev_kmsg_fd = safe_close(m->dev_kmsg_fd);
return manager_open_dev_kmsg(m);
}

View File

@ -5,6 +5,7 @@
int manager_open_dev_kmsg(Manager *m); int manager_open_dev_kmsg(Manager *m);
int manager_flush_dev_kmsg(Manager *m); int manager_flush_dev_kmsg(Manager *m);
int manager_reload_dev_kmsg(Manager *m);
void manager_forward_kmsg(Manager *m, int priority, const char *identifier, const char *message, const struct ucred *ucred); void manager_forward_kmsg(Manager *m, int priority, const char *identifier, const char *message, const struct ucred *ucred);

View File

@ -100,6 +100,7 @@
static int manager_schedule_sync(Manager *m, int priority); static int manager_schedule_sync(Manager *m, int priority);
static int manager_refresh_idle_timer(Manager *m); static int manager_refresh_idle_timer(Manager *m);
static int dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata);
static int manager_determine_path_usage( static int manager_determine_path_usage(
Manager *m, Manager *m,
@ -269,6 +270,12 @@ static void manager_add_acls(JournalFile *f, uid_t uid) {
#endif #endif
} }
static int manager_get_file_flags(Manager *m, bool seal) {
return (m->compress.enabled ? JOURNAL_COMPRESS : 0) |
(seal ? JOURNAL_SEAL : 0) |
JOURNAL_STRICT_ORDER;
}
static int manager_open_journal( static int manager_open_journal(
Manager *m, Manager *m,
bool reliably, bool reliably,
@ -286,10 +293,7 @@ static int manager_open_journal(
assert(fname); assert(fname);
assert(ret); assert(ret);
file_flags = file_flags = manager_get_file_flags(m, seal);
(m->compress.enabled ? JOURNAL_COMPRESS : 0) |
(seal ? JOURNAL_SEAL : 0) |
JOURNAL_STRICT_ORDER;
set_clear(m->deferred_closes); set_clear(m->deferred_closes);
@ -363,7 +367,7 @@ static int manager_system_journal_open(
int r = 0; int r = 0;
if (!m->system_journal && if (!m->system_journal &&
IN_SET(m->storage, STORAGE_PERSISTENT, STORAGE_AUTO) && IN_SET(m->config.storage, STORAGE_PERSISTENT, STORAGE_AUTO) &&
(flush_requested || manager_flushed_flag_is_set(m)) && (flush_requested || manager_flushed_flag_is_set(m)) &&
!relinquish_requested) { !relinquish_requested) {
@ -371,7 +375,7 @@ static int manager_system_journal_open(
* *
* If in persistent mode: create /var/log/journal and the machine path */ * If in persistent mode: create /var/log/journal and the machine path */
if (m->storage == STORAGE_PERSISTENT) if (m->config.storage == STORAGE_PERSISTENT)
(void) mkdir_parents(m->system_storage.path, 0755); (void) mkdir_parents(m->system_storage.path, 0755);
(void) mkdir(m->system_storage.path, 0755); (void) mkdir(m->system_storage.path, 0755);
@ -408,7 +412,7 @@ static int manager_system_journal_open(
} }
if (!m->runtime_journal && if (!m->runtime_journal &&
(m->storage != STORAGE_NONE)) { (m->config.storage != STORAGE_NONE)) {
fn = strjoina(m->runtime_storage.path, "/system.journal"); fn = strjoina(m->runtime_storage.path, "/system.journal");
@ -532,7 +536,7 @@ static JournalFile* manager_find_journal(Manager *m, uid_t uid) {
* persistent journal of any sort. * persistent journal of any sort.
* *
* Fixes https://github.com/systemd/systemd/issues/20390 */ * Fixes https://github.com/systemd/systemd/issues/20390 */
if (!IN_SET(m->storage, STORAGE_AUTO, STORAGE_PERSISTENT)) if (!IN_SET(m->config.storage, STORAGE_AUTO, STORAGE_PERSISTENT))
return NULL; return NULL;
if (!uid_for_system_journal(uid)) { if (!uid_for_system_journal(uid)) {
@ -1279,12 +1283,12 @@ void manager_dispatch_message(
if (n == 0) if (n == 0)
return; return;
if (LOG_PRI(priority) > m->max_level_store) if (LOG_PRI(priority) > m->config.max_level_store)
return; return;
/* Stop early in case the information will not be stored /* Stop early in case the information will not be stored
* in a journal. */ * in a journal. */
if (m->storage == STORAGE_NONE) if (m->config.storage == STORAGE_NONE)
return; return;
if (c && c->unit) { if (c && c->unit) {
@ -1320,7 +1324,7 @@ int manager_flush_to_var(Manager *m, bool require_flag_file) {
assert(m); assert(m);
if (!IN_SET(m->storage, STORAGE_AUTO, STORAGE_PERSISTENT)) if (!IN_SET(m->config.storage, STORAGE_AUTO, STORAGE_PERSISTENT))
return 0; return 0;
if (m->namespace) /* Flushing concept does not exist for namespace instances */ if (m->namespace) /* Flushing concept does not exist for namespace instances */
@ -1468,7 +1472,7 @@ finish:
int manager_relinquish_var(Manager *m) { int manager_relinquish_var(Manager *m) {
assert(m); assert(m);
if (m->storage == STORAGE_NONE) if (m->config.storage == STORAGE_NONE)
return 0; return 0;
if (m->namespace) /* Concept does not exist for namespaced instances */ if (m->namespace) /* Concept does not exist for namespaced instances */
@ -1859,6 +1863,10 @@ static int manager_setup_signals(Manager *m) {
if (r < 0) if (r < 0)
return r; return r;
r = sd_event_add_signal(m->event, NULL, SIGHUP|SD_EVENT_SIGNAL_PROCMASK, dispatch_reload_signal, m);
if (r < 0)
return r;
return 0; return 0;
} }
@ -1872,7 +1880,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0) if (r < 0)
log_warning("Failed to parse forward to syslog switch \"%s\". Ignoring.", value); log_warning("Failed to parse forward to syslog switch \"%s\". Ignoring.", value);
else else
m->forward_to_syslog = r; m->config_by_cmdline.forward_to_syslog = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_kmsg")) { } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_kmsg")) {
@ -1880,7 +1888,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0) if (r < 0)
log_warning("Failed to parse forward to kmsg switch \"%s\". Ignoring.", value); log_warning("Failed to parse forward to kmsg switch \"%s\". Ignoring.", value);
else else
m->forward_to_kmsg = r; m->config_by_cmdline.forward_to_kmsg = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_console")) { } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_console")) {
@ -1888,7 +1896,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0) if (r < 0)
log_warning("Failed to parse forward to console switch \"%s\". Ignoring.", value); log_warning("Failed to parse forward to console switch \"%s\". Ignoring.", value);
else else
m->forward_to_console = r; m->config_by_cmdline.forward_to_console = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_wall")) { } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_wall")) {
@ -1896,7 +1904,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0) if (r < 0)
log_warning("Failed to parse forward to wall switch \"%s\". Ignoring.", value); log_warning("Failed to parse forward to wall switch \"%s\". Ignoring.", value);
else else
m->forward_to_wall = r; m->config_by_cmdline.forward_to_wall = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_console")) { } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_console")) {
@ -1907,7 +1915,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0) if (r < 0)
log_warning("Failed to parse max level console value \"%s\". Ignoring.", value); log_warning("Failed to parse max level console value \"%s\". Ignoring.", value);
else else
m->max_level_console = r; m->config_by_cmdline.max_level_console = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_store")) { } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_store")) {
@ -1918,7 +1926,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0) if (r < 0)
log_warning("Failed to parse max level store value \"%s\". Ignoring.", value); log_warning("Failed to parse max level store value \"%s\". Ignoring.", value);
else else
m->max_level_store = r; m->config_by_cmdline.max_level_store = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_syslog")) { } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_syslog")) {
@ -1929,7 +1937,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0) if (r < 0)
log_warning("Failed to parse max level syslog value \"%s\". Ignoring.", value); log_warning("Failed to parse max level syslog value \"%s\". Ignoring.", value);
else else
m->max_level_syslog = r; m->config_by_cmdline.max_level_syslog = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_kmsg")) { } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_kmsg")) {
@ -1940,7 +1948,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0) if (r < 0)
log_warning("Failed to parse max level kmsg value \"%s\". Ignoring.", value); log_warning("Failed to parse max level kmsg value \"%s\". Ignoring.", value);
else else
m->max_level_kmsg = r; m->config_by_cmdline.max_level_kmsg = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_wall")) { } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_wall")) {
@ -1951,7 +1959,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0) if (r < 0)
log_warning("Failed to parse max level wall value \"%s\". Ignoring.", value); log_warning("Failed to parse max level wall value \"%s\". Ignoring.", value);
else else
m->max_level_wall = r; m->config_by_cmdline.max_level_wall = r;
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_socket")) { } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_socket")) {
@ -1962,7 +1970,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0) if (r < 0)
log_warning("Failed to parse max level socket value \"%s\". Ignoring.", value); log_warning("Failed to parse max level socket value \"%s\". Ignoring.", value);
else else
m->max_level_socket = r; m->config_by_cmdline.max_level_socket = r;
} else if (startswith(key, "systemd.journald")) } else if (startswith(key, "systemd.journald"))
log_warning("Unknown journald kernel command line option \"%s\". Ignoring.", key); log_warning("Unknown journald kernel command line option \"%s\". Ignoring.", key);
@ -2394,7 +2402,7 @@ static void manager_load_credentials(Manager *m) {
if (r < 0) if (r < 0)
log_debug_errno(r, "Failed to read credential journal.forward_to_socket, ignoring: %m"); log_debug_errno(r, "Failed to read credential journal.forward_to_socket, ignoring: %m");
else { else {
r = socket_address_parse(&m->forward_to_socket, data); r = socket_address_parse(&m->config_by_cred.forward_to_socket, data);
if (r < 0) if (r < 0)
log_debug_errno(r, "Failed to parse socket address '%s' from credential journal.forward_to_socket, ignoring: %m", (char *) data); log_debug_errno(r, "Failed to parse socket address '%s' from credential journal.forward_to_socket, ignoring: %m", (char *) data);
} }
@ -2409,12 +2417,225 @@ static void manager_load_credentials(Manager *m) {
if (r < 0) if (r < 0)
log_debug_errno(r, "Failed to parse storage '%s' from credential journal.storage, ignoring: %m", (char *) data); log_debug_errno(r, "Failed to parse storage '%s' from credential journal.storage, ignoring: %m", (char *) data);
else else
m->storage = r; m->config_by_cred.storage = r;
} }
} }
int manager_new(Manager **ret) { static void manager_set_defaults(Manager *m) {
assert(m);
m->compress.enabled = true;
m->compress.threshold_bytes = UINT64_MAX;
m->seal = true;
/* By default, only read from /dev/kmsg if are the main namespace */
m->read_kmsg = !m->namespace;
m->set_audit = true;
m->sync_interval_usec = DEFAULT_SYNC_INTERVAL_USEC;
m->ratelimit_interval = DEFAULT_RATE_LIMIT_INTERVAL;
m->ratelimit_burst = DEFAULT_RATE_LIMIT_BURST;
m->system_storage.name = "System Journal";
journal_reset_metrics(&m->system_storage.metrics);
m->runtime_storage.name = "Runtime Journal";
journal_reset_metrics(&m->runtime_storage.metrics);
m->max_file_usec = DEFAULT_MAX_FILE_USEC;
m->config.forward_to_wall = true;
m->config.max_level_store = LOG_DEBUG;
m->config.max_level_syslog = LOG_DEBUG;
m->config.max_level_kmsg = LOG_NOTICE;
m->config.max_level_console = LOG_INFO;
m->config.max_level_wall = LOG_EMERG;
m->config.max_level_socket = LOG_DEBUG;
m->line_max = DEFAULT_LINE_MAX;
}
static void manager_reset_configs(Manager *m) {
assert(m);
m->config_by_cmdline = JOURNAL_CONFIG_INIT;
m->config_by_conf = JOURNAL_CONFIG_INIT;
m->config_by_cred = JOURNAL_CONFIG_INIT;
}
static void manager_adjust_configs(Manager *m) {
assert(m);
if (!!m->ratelimit_interval != !!m->ratelimit_burst) { /* One set to 0 and the other not? */
log_debug(
"Setting both rate limit interval and burst from %s/%u to 0/0",
FORMAT_TIMESPAN(m->ratelimit_interval, USEC_PER_SEC),
m->ratelimit_burst);
m->ratelimit_interval = m->ratelimit_burst = 0;
}
}
static void manager_merge_forward_to_socket(Manager *m) {
assert(m);
/* Conf file takes precendence over credentials. */
if (m->config_by_conf.forward_to_socket.sockaddr.sa.sa_family != AF_UNSPEC)
m->config.forward_to_socket = m->config_by_conf.forward_to_socket;
else if (m->config_by_cred.forward_to_socket.sockaddr.sa.sa_family != AF_UNSPEC)
m->config.forward_to_socket = m->config_by_cred.forward_to_socket;
else
m->config.forward_to_socket = (SocketAddress) { .sockaddr.sa.sa_family = AF_UNSPEC };
}
static void manager_merge_storage(Manager *m) {
assert(m);
/* Conf file takes precendence over credentials. */
if (m->config_by_conf.storage != _STORAGE_INVALID)
m->config.storage = m->config_by_conf.storage;
else if (m->config_by_cred.storage != _STORAGE_INVALID)
m->config.storage = m->config_by_cred.storage;
else
m->config.storage = m->namespace ? STORAGE_PERSISTENT : STORAGE_AUTO;
}
#define MERGE_BOOL(name, default_value) \
(m->config.name = (m->config_by_cmdline.name ? m->config_by_cmdline.name : \
m->config_by_conf.name ? m->config_by_conf.name : \
m->config_by_cred.name ? m->config_by_cred.name : \
default_value))
#define MERGE_NON_NEGATIVE(name, default_value) \
(m->config.name = (m->config_by_cmdline.name >= 0 ? m->config_by_cmdline.name : \
m->config_by_conf.name >= 0 ? m->config_by_conf.name : \
m->config_by_cred.name >= 0 ? m->config_by_cred.name : \
default_value))
static void manager_merge_configs(Manager *m) {
assert(m);
/*
* From highest to lowest priority: cmdline, conf, cred
*/
manager_merge_storage(m);
manager_merge_forward_to_socket(m);
MERGE_BOOL(forward_to_syslog, false);
MERGE_BOOL(forward_to_kmsg, false);
MERGE_BOOL(forward_to_console, false);
MERGE_BOOL(forward_to_wall, true);
MERGE_NON_NEGATIVE(max_level_store, LOG_DEBUG);
MERGE_NON_NEGATIVE(max_level_syslog, LOG_DEBUG);
MERGE_NON_NEGATIVE(max_level_kmsg, LOG_NOTICE);
MERGE_NON_NEGATIVE(max_level_console, LOG_INFO);
MERGE_NON_NEGATIVE(max_level_wall, LOG_EMERG);
MERGE_NON_NEGATIVE(max_level_socket, LOG_DEBUG);
}
static void manager_load_config(Manager *m) {
assert(m);
int r;
manager_set_defaults(m);
manager_reset_configs(m);
manager_load_credentials(m);
manager_parse_config_file(m);
if (!m->namespace) {
/* Parse kernel command line, but only if we are not a namespace instance */
r = proc_cmdline_parse(parse_proc_cmdline_item, m, PROC_CMDLINE_STRIP_RD_PREFIX);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
}
manager_merge_configs(m);
manager_adjust_configs(m);
}
static void manager_reload_config(Manager *m) {
assert(m);
manager_set_defaults(m);
m->config_by_conf = JOURNAL_CONFIG_INIT;
manager_parse_config_file(m);
manager_merge_configs(m);
manager_adjust_configs(m);
}
static int manager_reload_journals(Manager *m) {
assert(m);
int r;
if (m->system_journal && IN_SET(m->config.storage, STORAGE_PERSISTENT, STORAGE_AUTO)) {
/* Current journal can continue being used. Update config values as needed. */
r = journal_file_reload(
m->system_journal,
manager_get_file_flags(m, m->seal),
m->compress.threshold_bytes,
&m->system_storage.metrics);
if (r < 0)
return log_warning_errno(r, "Failed to reload system journal on reload, ignoring: %m");
} else if (m->system_journal && m->config.storage == STORAGE_VOLATILE) {
/* Journal needs to be switched from system to runtime. */
r = manager_relinquish_var(m);
if (r < 0)
return log_warning_errno(r, "Failed to relinquish to runtime journal on reload, ignoring: %m");
} else if (m->runtime_journal && IN_SET(m->config.storage, STORAGE_PERSISTENT, STORAGE_AUTO, STORAGE_VOLATILE)) {
/* Current journal can continue being used. Update config values as needed.*/
r = journal_file_reload(
m->runtime_journal,
manager_get_file_flags(m, /* seal */ false),
m->compress.threshold_bytes,
&m->runtime_storage.metrics);
if (r < 0)
return log_warning_errno(r, "Failed to reload runtime journal on reload, ignoring: %m");
}
/* If journal-related configuration, such as SystemMaxUse, SystemMaxFileSize, RuntimeMaxUse, RuntimeMaxFileSize,
* were to change, then we can vacuum for the change to take effect. For example, if pre-reload SystemMaxUse=2M,
* current usage=1.5M, and the post-reload SystemMaxUse=1M, the vacuum can shrink it to 1M.
*/
manager_vacuum(m, /* verbose */ false);
return 0;
}
static int dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
Manager *m = ASSERT_PTR(userdata);
int r;
(void) notify_reloading();
manager_reload_config(m);
r = manager_reload_dev_kmsg(m);
if (r < 0)
return r;
r = manager_reload_journals(m);
if (r < 0)
return r;
log_info("Config file reloaded.");
(void) sd_notify(/* unset_environment */ false, NOTIFY_READY_MESSAGE);
return 0;
}
int manager_new(Manager **ret, const char *namespace) {
_cleanup_(manager_freep) Manager *m = NULL; _cleanup_(manager_freep) Manager *m = NULL;
int r;
assert(ret); assert(ret);
@ -2432,37 +2653,10 @@ int manager_new(Manager **ret) {
.notify_fd = -EBADF, .notify_fd = -EBADF,
.forward_socket_fd = -EBADF, .forward_socket_fd = -EBADF,
.compress.enabled = true,
.compress.threshold_bytes = UINT64_MAX,
.seal = true,
.set_audit = true,
.watchdog_usec = USEC_INFINITY, .watchdog_usec = USEC_INFINITY,
.sync_interval_usec = DEFAULT_SYNC_INTERVAL_USEC,
.sync_scheduled = false, .sync_scheduled = false,
.ratelimit_interval = DEFAULT_RATE_LIMIT_INTERVAL,
.ratelimit_burst = DEFAULT_RATE_LIMIT_BURST,
.forward_to_wall = true,
.forward_to_socket = { .sockaddr.sa.sa_family = AF_UNSPEC },
.max_file_usec = DEFAULT_MAX_FILE_USEC,
.max_level_store = LOG_DEBUG,
.max_level_syslog = LOG_DEBUG,
.max_level_kmsg = LOG_NOTICE,
.max_level_console = LOG_INFO,
.max_level_wall = LOG_EMERG,
.max_level_socket = LOG_DEBUG,
.line_max = DEFAULT_LINE_MAX,
.runtime_storage.name = "Runtime Journal",
.system_storage.name = "System Journal",
.kmsg_own_ratelimit = { .kmsg_own_ratelimit = {
.interval = DEFAULT_KMSG_OWN_INTERVAL, .interval = DEFAULT_KMSG_OWN_INTERVAL,
.burst = DEFAULT_KMSG_OWN_BURST, .burst = DEFAULT_KMSG_OWN_BURST,
@ -2472,11 +2666,17 @@ int manager_new(Manager **ret) {
.sigrtmin18_info.memory_pressure_userdata = m, .sigrtmin18_info.memory_pressure_userdata = m,
}; };
r = manager_set_namespace(m, namespace);
if (r < 0)
return r;
manager_load_config(m);
*ret = TAKE_PTR(m); *ret = TAKE_PTR(m);
return 0; return 0;
} }
int manager_init(Manager *m, const char *namespace) { int manager_init(Manager *m) {
const char *native_socket, *syslog_socket, *stdout_socket, *varlink_socket, *e; const char *native_socket, *syslog_socket, *stdout_socket, *varlink_socket, *e;
_cleanup_fdset_free_ FDSet *fds = NULL; _cleanup_fdset_free_ FDSet *fds = NULL;
int n, r, varlink_fd = -EBADF; int n, r, varlink_fd = -EBADF;
@ -2484,33 +2684,6 @@ int manager_init(Manager *m, const char *namespace) {
assert(m); assert(m);
r = manager_set_namespace(m, namespace);
if (r < 0)
return r;
/* By default, only read from /dev/kmsg if are the main namespace */
m->read_kmsg = !m->namespace;
m->storage = m->namespace ? STORAGE_PERSISTENT : STORAGE_AUTO;
journal_reset_metrics(&m->system_storage.metrics);
journal_reset_metrics(&m->runtime_storage.metrics);
manager_load_credentials(m);
manager_parse_config_file(m);
if (!m->namespace) {
/* Parse kernel command line, but only if we are not a namespace instance */
r = proc_cmdline_parse(parse_proc_cmdline_item, m, PROC_CMDLINE_STRIP_RD_PREFIX);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
}
if (!!m->ratelimit_interval != !!m->ratelimit_burst) { /* One set to 0 and the other not? */
log_debug("Setting both rate limit interval and burst from "USEC_FMT",%u to 0,0",
m->ratelimit_interval, m->ratelimit_burst);
m->ratelimit_interval = m->ratelimit_burst = 0;
}
e = getenv("RUNTIME_DIRECTORY"); e = getenv("RUNTIME_DIRECTORY");
if (e) if (e)
m->runtime_directory = strdup(e); m->runtime_directory = strdup(e);

View File

@ -55,6 +55,23 @@ typedef struct SeqnumData {
uint64_t seqnum; uint64_t seqnum;
} SeqnumData; } SeqnumData;
typedef struct JournalConfig {
SocketAddress forward_to_socket;
Storage storage;
bool forward_to_kmsg;
bool forward_to_syslog;
bool forward_to_console;
bool forward_to_wall;
int max_level_store;
int max_level_syslog;
int max_level_kmsg;
int max_level_console;
int max_level_wall;
int max_level_socket;
} JournalConfig;
typedef struct Manager { typedef struct Manager {
char *namespace; char *namespace;
@ -111,12 +128,6 @@ typedef struct Manager {
bool sent_notify_ready; bool sent_notify_ready;
bool sync_scheduled; bool sync_scheduled;
bool forward_to_kmsg;
bool forward_to_syslog;
bool forward_to_console;
bool forward_to_wall;
SocketAddress forward_to_socket;
unsigned n_forward_syslog_missed; unsigned n_forward_syslog_missed;
usec_t last_warn_forward_syslog_missed; usec_t last_warn_forward_syslog_missed;
@ -130,14 +141,6 @@ typedef struct Manager {
char *tty_path; char *tty_path;
int max_level_store;
int max_level_syslog;
int max_level_kmsg;
int max_level_console;
int max_level_wall;
int max_level_socket;
Storage storage;
SplitMode split_mode; SplitMode split_mode;
MMapCache *mmap; MMapCache *mmap;
@ -183,6 +186,21 @@ typedef struct Manager {
/* Pending synchronization requests with non-zero rqlen counter */ /* Pending synchronization requests with non-zero rqlen counter */
LIST_HEAD(SyncReq, sync_req_pending_rqlen); LIST_HEAD(SyncReq, sync_req_pending_rqlen);
/* These structs are used to preserve configurations set by credentials and command line.
config - main configuration used by journald manager,
config_by_cred - configuration set by credentials,
config_by_conf - configuration set by configuration file,
config_by_cmdline - configuration set by command line.
The priority order of the sub-configurations is:
config_by_cmdline > config_by_conf > config_by_cred
where A > B means that if the two have the same setting,
A's value overrides B's value for that setting.
*/
JournalConfig config;
JournalConfig config_by_cred;
JournalConfig config_by_conf;
JournalConfig config_by_cmdline;
} Manager; } Manager;
#define MANAGER_MACHINE_ID(s) ((s)->machine_id_field + STRLEN("_MACHINE_ID=")) #define MANAGER_MACHINE_ID(s) ((s)->machine_id_field + STRLEN("_MACHINE_ID="))
@ -209,6 +227,17 @@ void manager_dispatch_message(Manager *m, struct iovec *iovec, size_t n, size_t
void manager_driver_message_internal(Manager *m, pid_t object_pid, const char *format, ...) _sentinel_; void manager_driver_message_internal(Manager *m, pid_t object_pid, const char *format, ...) _sentinel_;
#define manager_driver_message(...) manager_driver_message_internal(__VA_ARGS__, NULL) #define manager_driver_message(...) manager_driver_message_internal(__VA_ARGS__, NULL)
#define JOURNAL_CONFIG_INIT \
(JournalConfig) { \
.forward_to_socket = (SocketAddress) { .sockaddr.sa.sa_family = AF_UNSPEC }, \
.storage = _STORAGE_INVALID, \
.max_level_store = -1, \
.max_level_syslog = -1, \
.max_level_kmsg = -1, \
.max_level_console = -1, \
.max_level_wall = -1, \
}
/* gperf lookup function */ /* gperf lookup function */
const struct ConfigPerfItem* journald_gperf_lookup(const char *key, GPERF_LEN_TYPE length); const struct ConfigPerfItem* journald_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
@ -225,8 +254,8 @@ CONFIG_PARSER_PROTOTYPE(config_parse_split_mode);
const char* split_mode_to_string(SplitMode s) _const_; const char* split_mode_to_string(SplitMode s) _const_;
SplitMode split_mode_from_string(const char *s) _pure_; SplitMode split_mode_from_string(const char *s) _pure_;
int manager_new(Manager **ret); int manager_new(Manager **ret, const char *namespace);
int manager_init(Manager *m, const char *namespace); int manager_init(Manager *m);
Manager* manager_free(Manager *m); Manager* manager_free(Manager *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
void manager_full_sync(Manager *m, bool wait); void manager_full_sync(Manager *m, bool wait);

View File

@ -267,16 +267,16 @@ static int manager_process_entry(
if (r <= 0) if (r <= 0)
goto finish; goto finish;
if (m->forward_to_syslog) if (m->config.forward_to_syslog)
manager_forward_syslog(m, syslog_fixup_facility(priority), identifier, message, ucred, tv); manager_forward_syslog(m, syslog_fixup_facility(priority), identifier, message, ucred, tv);
if (m->forward_to_kmsg) if (m->config.forward_to_kmsg)
manager_forward_kmsg(m, priority, identifier, message, ucred); manager_forward_kmsg(m, priority, identifier, message, ucred);
if (m->forward_to_console) if (m->config.forward_to_console)
manager_forward_console(m, priority, identifier, message, ucred); manager_forward_console(m, priority, identifier, message, ucred);
if (m->forward_to_wall) if (m->config.forward_to_wall)
manager_forward_wall(m, priority, identifier, message, ucred); manager_forward_wall(m, priority, identifier, message, ucred);
} }

View File

@ -23,13 +23,13 @@ static int manager_open_forward_socket(Manager *m) {
assert(m); assert(m);
/* Noop if there is nothing to do. */ /* Noop if there is nothing to do. */
if (m->forward_to_socket.sockaddr.sa.sa_family == AF_UNSPEC || m->namespace) if (m->config.forward_to_socket.sockaddr.sa.sa_family == AF_UNSPEC || m->namespace)
return 0; return 0;
/* All ready, nothing to do. */ /* All ready, nothing to do. */
if (m->forward_socket_fd >= 0) if (m->forward_socket_fd >= 0)
return 1; return 1;
addr = &m->forward_to_socket; addr = &m->config.forward_to_socket;
family = socket_address_family(addr); family = socket_address_family(addr);
@ -86,7 +86,7 @@ int manager_forward_socket(
assert(n_iovec > 0); assert(n_iovec > 0);
assert(ts); assert(ts);
if (LOG_PRI(priority) > m->max_level_socket) if (LOG_PRI(priority) > m->config.max_level_socket)
return 0; return 0;
r = manager_open_forward_socket(m); r = manager_open_forward_socket(m);

View File

@ -251,16 +251,16 @@ static int stdout_stream_log(
if (r <= 0) if (r <= 0)
return r; return r;
if (s->forward_to_syslog || s->manager->forward_to_syslog) if (s->forward_to_syslog || s->manager->config.forward_to_syslog)
manager_forward_syslog(s->manager, syslog_fixup_facility(priority), s->identifier, p, &s->ucred, NULL); manager_forward_syslog(s->manager, syslog_fixup_facility(priority), s->identifier, p, &s->ucred, NULL);
if (s->forward_to_kmsg || s->manager->forward_to_kmsg) if (s->forward_to_kmsg || s->manager->config.forward_to_kmsg)
manager_forward_kmsg(s->manager, priority, s->identifier, p, &s->ucred); manager_forward_kmsg(s->manager, priority, s->identifier, p, &s->ucred);
if (s->forward_to_console || s->manager->forward_to_console) if (s->forward_to_console || s->manager->config.forward_to_console)
manager_forward_console(s->manager, priority, s->identifier, p, &s->ucred); manager_forward_console(s->manager, priority, s->identifier, p, &s->ucred);
if (s->manager->forward_to_wall) if (s->manager->config.forward_to_wall)
manager_forward_wall(s->manager, priority, s->identifier, p, &s->ucred); manager_forward_wall(s->manager, priority, s->identifier, p, &s->ucred);
m = N_IOVEC_META_FIELDS + 7 + client_context_extra_fields_n_iovec(s->context); m = N_IOVEC_META_FIELDS + 7 + client_context_extra_fields_n_iovec(s->context);

View File

@ -127,7 +127,7 @@ static void forward_syslog_raw(
assert(m); assert(m);
assert(buffer); assert(buffer);
if (LOG_PRI(priority) > m->max_level_syslog) if (LOG_PRI(priority) > m->config.max_level_syslog)
return; return;
iovec = IOVEC_MAKE((char *) buffer, buffer_len); iovec = IOVEC_MAKE((char *) buffer, buffer_len);
@ -154,7 +154,7 @@ void manager_forward_syslog(
assert(priority <= 999); assert(priority <= 999);
assert(message); assert(message);
if (LOG_PRI(priority) > m->max_level_syslog) if (LOG_PRI(priority) > m->config.max_level_syslog)
return; return;
/* First: priority field */ /* First: priority field */
@ -403,16 +403,16 @@ void manager_process_syslog_message(
syslog_parse_identifier(&msg, &identifier, &pid); syslog_parse_identifier(&msg, &identifier, &pid);
if (m->forward_to_syslog) if (m->config.forward_to_syslog)
forward_syslog_raw(m, priority, buf, raw_len, ucred, tv); forward_syslog_raw(m, priority, buf, raw_len, ucred, tv);
if (m->forward_to_kmsg) if (m->config.forward_to_kmsg)
manager_forward_kmsg(m, priority, identifier, msg, ucred); manager_forward_kmsg(m, priority, identifier, msg, ucred);
if (m->forward_to_console) if (m->config.forward_to_console)
manager_forward_console(m, priority, identifier, msg, ucred); manager_forward_console(m, priority, identifier, msg, ucred);
if (m->forward_to_wall) if (m->config.forward_to_wall)
manager_forward_wall(m, priority, identifier, msg, ucred); manager_forward_wall(m, priority, identifier, msg, ucred);
mm = N_IOVEC_META_FIELDS + 8 + client_context_extra_fields_n_iovec(context); mm = N_IOVEC_META_FIELDS + 8 + client_context_extra_fields_n_iovec(context);

View File

@ -23,7 +23,7 @@ void manager_forward_wall(
assert(m); assert(m);
assert(message); assert(message);
if (LOG_PRI(priority) > m->max_level_wall) if (LOG_PRI(priority) > m->config.max_level_wall)
return; return;
if (ucred) { if (ucred) {

View File

@ -52,11 +52,11 @@ static int run(int argc, char *argv[]) {
sigbus_install(); sigbus_install();
r = manager_new(&m); r = manager_new(&m, namespace);
if (r < 0) if (r < 0)
return log_oom(); return log_oom();
r = manager_init(m, namespace); r = manager_init(m);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -33,6 +33,11 @@ case "$COMMAND" in
exec depmod -a "$KERNEL_VERSION" exec depmod -a "$KERNEL_VERSION"
;; ;;
remove) remove)
[ "$KERNEL_INSTALL_BOOT_ENTRY_TYPE" = "type2" ] || \
[ "$KERNEL_INSTALL_BOOT_ENTRY_TYPE" = "type1" ] && \
[ -d "/lib/modules/$KERNEL_VERSION/kernel" ] && \
echo "Multiple entry types exist, not removing modules.dep or associated files." && \
exit 0
[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \
echo "Removing /lib/modules/${KERNEL_VERSION}/modules.dep and associated files" echo "Removing /lib/modules/${KERNEL_VERSION}/modules.dep and associated files"
exec rm -f \ exec rm -f \

View File

@ -49,6 +49,11 @@ LOADER_ENTRY="$BOOT_ROOT/loader/entries/$ENTRY_TOKEN-$KERNEL_VERSION.conf"
case "$COMMAND" in case "$COMMAND" in
remove) remove)
if [ -f "/lib/modules/$KERNEL_VERSION/vmlinuz" ] && [ "$KERNEL_INSTALL_BOOT_ENTRY_TYPE" = "type2" ]; then
[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \
echo "BOOT_ENTRY_TYPE=type2, not removing loader entries."
exit 0
fi
[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \
echo "Removing ${LOADER_ENTRY%.conf}*.conf" echo "Removing ${LOADER_ENTRY%.conf}*.conf"
exec rm -f \ exec rm -f \

View File

@ -7,6 +7,7 @@
#include "argv-util.h" #include "argv-util.h"
#include "boot-entry.h" #include "boot-entry.h"
#include "bootspec.h"
#include "build.h" #include "build.h"
#include "chase.h" #include "chase.h"
#include "conf-files.h" #include "conf-files.h"
@ -88,6 +89,7 @@ typedef struct Context {
Action action; Action action;
sd_id128_t machine_id; sd_id128_t machine_id;
bool machine_id_is_random; bool machine_id_is_random;
BootEntryType entry_type;
KernelImageType kernel_image_type; KernelImageType kernel_image_type;
Layout layout; Layout layout;
char *layout_other; char *layout_other;
@ -427,6 +429,20 @@ static int context_set_initrds(Context *c, char* const* strv) {
return context_set_path_strv(c, strv, "command line", "initrds", &c->initrds); return context_set_path_strv(c, strv, "command line", "initrds", &c->initrds);
} }
static int context_set_entry_type(Context *c, const char *s) {
assert(c);
BootEntryType e;
if (isempty(s) || streq(s, "all")) {
c->entry_type = _BOOT_ENTRY_TYPE_INVALID;
return 0;
}
e = boot_entry_type_from_string(s);
if (e < 0)
return log_error_errno(e, "Invalid entry type: %s", s);
c->entry_type = e;
return 1;
}
static int context_load_environment(Context *c) { static int context_load_environment(Context *c) {
assert(c); assert(c);
@ -983,6 +999,7 @@ static int context_build_environment(Context *c) {
"KERNEL_INSTALL_LAYOUT", context_get_layout(c), "KERNEL_INSTALL_LAYOUT", context_get_layout(c),
"KERNEL_INSTALL_INITRD_GENERATOR", strempty(c->initrd_generator), "KERNEL_INSTALL_INITRD_GENERATOR", strempty(c->initrd_generator),
"KERNEL_INSTALL_UKI_GENERATOR", strempty(c->uki_generator), "KERNEL_INSTALL_UKI_GENERATOR", strempty(c->uki_generator),
"KERNEL_INSTALL_BOOT_ENTRY_TYPE", boot_entry_type_to_string(c->entry_type),
"KERNEL_INSTALL_STAGING_AREA", c->staging_area); "KERNEL_INSTALL_STAGING_AREA", c->staging_area);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to build environment variables for plugins: %m"); return log_error_errno(r, "Failed to build environment variables for plugins: %m");
@ -1487,6 +1504,9 @@ static int help(void) {
" --root=PATH Operate on an alternate filesystem root\n" " --root=PATH Operate on an alternate filesystem root\n"
" --image=PATH Operate on disk image as filesystem root\n" " --image=PATH Operate on disk image as filesystem root\n"
" --image-policy=POLICY Specify disk image dissection policy\n" " --image-policy=POLICY Specify disk image dissection policy\n"
" --entry-type=type1|type2|all\n"
" Operate only on the specified bootloader\n"
" entry type\n"
"\n" "\n"
"This program may also be invoked as 'installkernel':\n" "This program may also be invoked as 'installkernel':\n"
" installkernel [OPTIONS...] VERSION VMLINUZ [MAP] [INSTALLATION-DIR]\n" " installkernel [OPTIONS...] VERSION VMLINUZ [MAP] [INSTALLATION-DIR]\n"
@ -1516,6 +1536,7 @@ static int parse_argv(int argc, char *argv[], Context *c) {
ARG_ROOT, ARG_ROOT,
ARG_IMAGE, ARG_IMAGE,
ARG_IMAGE_POLICY, ARG_IMAGE_POLICY,
ARG_BOOT_ENTRY_TYPE,
}; };
static const struct option options[] = { static const struct option options[] = {
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
@ -1531,6 +1552,7 @@ static int parse_argv(int argc, char *argv[], Context *c) {
{ "image", required_argument, NULL, ARG_IMAGE }, { "image", required_argument, NULL, ARG_IMAGE },
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY }, { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND }, { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "entry-type", required_argument, NULL, ARG_BOOT_ENTRY_TYPE },
{} {}
}; };
int t, r; int t, r;
@ -1614,6 +1636,12 @@ static int parse_argv(int argc, char *argv[], Context *c) {
return r; return r;
break; break;
case ARG_BOOT_ENTRY_TYPE:
r = context_set_entry_type(c, optarg);
if (r < 0)
return r;
break;
case '?': case '?':
return -EINVAL; return -EINVAL;
@ -1641,6 +1669,7 @@ static int run(int argc, char* argv[]) {
.action = _ACTION_INVALID, .action = _ACTION_INVALID,
.kernel_image_type = KERNEL_IMAGE_TYPE_UNKNOWN, .kernel_image_type = KERNEL_IMAGE_TYPE_UNKNOWN,
.layout = _LAYOUT_INVALID, .layout = _LAYOUT_INVALID,
.entry_type = _BOOT_ENTRY_TYPE_INVALID,
.entry_token_type = BOOT_ENTRY_TOKEN_AUTO, .entry_token_type = BOOT_ENTRY_TOKEN_AUTO,
}; };
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;

View File

@ -97,6 +97,13 @@ test ! -e "$entry"
test ! -e "$BOOT_ROOT/the-token/1.1.1/linux" test ! -e "$BOOT_ROOT/the-token/1.1.1/linux"
test ! -e "$BOOT_ROOT/the-token/1.1.1/initrd" test ! -e "$BOOT_ROOT/the-token/1.1.1/initrd"
# Test --entry-type options
"$kernel_install" -v add 1.1.1 "$D/sources/linux" "$D/sources/initrd"
"$kernel_install" -v remove 1.1.1 --entry-type=type1
test ! -e "$entry"
test ! -e "$BOOT_ROOT/the-token/1.1.1/linux"
test ! -e "$BOOT_ROOT/the-token/1.1.1/initrd"
# Invoke kernel-install as installkernel # Invoke kernel-install as installkernel
ln -s --relative -v "$kernel_install" "$D/sources/installkernel" ln -s --relative -v "$kernel_install" "$D/sources/installkernel"
"$D/sources/installkernel" -v 1.1.2 "$D/sources/linux" System.map /somedirignored "$D/sources/installkernel" -v 1.1.2 "$D/sources/linux" System.map /somedirignored

View File

@ -4082,6 +4082,54 @@ static void journal_default_metrics(JournalMetrics *m, int fd, bool compact) {
m->n_max_files); m->n_max_files);
} }
static uint64_t get_compress_threshold_bytes(uint64_t compress_threshold_bytes) {
return compress_threshold_bytes == UINT64_MAX ?
DEFAULT_COMPRESS_THRESHOLD :
MAX(MIN_COMPRESS_THRESHOLD, compress_threshold_bytes);
}
static int set_metrics(JournalFile *f, JournalMetrics *metrics, JournalFile *template) {
assert(f);
int r;
if (!journal_file_writable(f))
return 0;
if (metrics) {
journal_default_metrics(metrics, f->fd, JOURNAL_HEADER_COMPACT(f->header));
f->metrics = *metrics;
} else if (template)
f->metrics = template->metrics;
r = journal_file_refresh_header(f);
if (r < 0)
return log_error_errno(r, "Failed to refresh journal file header. Error to be handled by caller.");
return 0;
}
int journal_file_reload(
JournalFile *f,
JournalFileFlags file_flags,
uint64_t compress_threshold_bytes,
JournalMetrics *metrics) {
assert(f);
assert((file_flags & ~_JOURNAL_FILE_FLAGS_ALL) == 0);
assert(metrics);
int r;
f->compress_threshold_bytes = get_compress_threshold_bytes(compress_threshold_bytes);
r = set_metrics(f, metrics, /* template */ NULL);
if (r < 0)
/* Journal file header failed to be rotated. The changes may not have taken effect in this case. */
return r;
return 0;
}
int journal_file_open( int journal_file_open(
int fd, int fd,
const char *fname, const char *fname,
@ -4121,9 +4169,7 @@ int journal_file_open(
.fd = fd, .fd = fd,
.mode = mode, .mode = mode,
.open_flags = open_flags, .open_flags = open_flags,
.compress_threshold_bytes = compress_threshold_bytes == UINT64_MAX ? .compress_threshold_bytes = get_compress_threshold_bytes(compress_threshold_bytes),
DEFAULT_COMPRESS_THRESHOLD :
MAX(MIN_COMPRESS_THRESHOLD, compress_threshold_bytes),
.strict_order = FLAGS_SET(file_flags, JOURNAL_STRICT_ORDER), .strict_order = FLAGS_SET(file_flags, JOURNAL_STRICT_ORDER),
.newest_boot_id_prioq_idx = PRIOQ_IDX_NULL, .newest_boot_id_prioq_idx = PRIOQ_IDX_NULL,
.last_direction = _DIRECTION_INVALID, .last_direction = _DIRECTION_INVALID,
@ -4232,17 +4278,9 @@ int journal_file_open(
} }
#endif #endif
if (journal_file_writable(f)) { r = set_metrics(f, metrics, template);
if (metrics) { if (r < 0)
journal_default_metrics(metrics, f->fd, JOURNAL_HEADER_COMPACT(f->header)); goto fail;
f->metrics = *metrics;
} else if (template)
f->metrics = template->metrics;
r = journal_file_refresh_header(f);
if (r < 0)
goto fail;
}
#if HAVE_GCRYPT #if HAVE_GCRYPT
r = journal_file_hmac_setup(f); r = journal_file_hmac_setup(f);

View File

@ -149,6 +149,12 @@ int journal_file_open(
JournalFile *template, JournalFile *template,
JournalFile **ret); JournalFile **ret);
int journal_file_reload(
JournalFile *f,
JournalFileFlags file_flags,
uint64_t compress_threshold_bytes,
JournalMetrics *metrics);
int journal_file_set_offline_thread_join(JournalFile *f); int journal_file_set_offline_thread_join(JournalFile *f);
JournalFile* journal_file_close(JournalFile *j); JournalFile* journal_file_close(JournalFile *j);
int journal_file_fstat(JournalFile *f); int journal_file_fstat(JournalFile *f);

View File

@ -1907,14 +1907,14 @@ static int setup_timezone(const char *dest) {
log_debug_errno(r, "Timezone %s does not exist (or is not accessible) in container, not creating symlink: %m", z); log_debug_errno(r, "Timezone %s does not exist (or is not accessible) in container, not creating symlink: %m", z);
else { else {
if (unlink(where) < 0 && errno != ENOENT) { if (unlink(where) < 0 && errno != ENOENT) {
log_full_errno(IN_SET(errno, EROFS, EACCES, EPERM) ? LOG_DEBUG : LOG_WARNING, /* Don't complain on read-only images */ log_full_errno(ERRNO_IS_FS_WRITE_REFUSED(errno) ? LOG_DEBUG : LOG_WARNING, /* Don't complain on read-only images */
errno, "Failed to remove existing timezone info %s in container, ignoring: %m", where); errno, "Failed to remove existing timezone info %s in container, ignoring: %m", where);
return 0; return 0;
} }
what = strjoina("../usr/share/zoneinfo/", z); what = strjoina("../usr/share/zoneinfo/", z);
if (symlink(what, where) < 0) { if (symlink(what, where) < 0) {
log_full_errno(IN_SET(errno, EROFS, EACCES, EPERM) ? LOG_DEBUG : LOG_WARNING, log_full_errno(ERRNO_IS_FS_WRITE_REFUSED(errno) ? LOG_DEBUG : LOG_WARNING,
errno, "Failed to correct timezone of container, ignoring: %m"); errno, "Failed to correct timezone of container, ignoring: %m");
return 0; return 0;
} }
@ -1949,7 +1949,7 @@ static int setup_timezone(const char *dest) {
/* If mounting failed, try to copy */ /* If mounting failed, try to copy */
r = copy_file_atomic("/etc/localtime", where, 0644, COPY_REFLINK|COPY_REPLACE); r = copy_file_atomic("/etc/localtime", where, 0644, COPY_REFLINK|COPY_REPLACE);
if (r < 0) { if (r < 0) {
log_full_errno(IN_SET(r, -EROFS, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r, log_full_errno(ERRNO_IS_NEG_FS_WRITE_REFUSED(r) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to copy /etc/localtime to %s, ignoring: %m", where); "Failed to copy /etc/localtime to %s, ignoring: %m", where);
return 0; return 0;
} }
@ -2085,7 +2085,7 @@ static int setup_resolv_conf(const char *dest) {
* If the disk image is read-only, there's also no point in complaining. * If the disk image is read-only, there's also no point in complaining.
*/ */
log_full_errno(!IN_SET(RESOLV_CONF_COPY_HOST, RESOLV_CONF_COPY_STATIC, RESOLV_CONF_COPY_UPLINK, RESOLV_CONF_COPY_STUB) && log_full_errno(!IN_SET(RESOLV_CONF_COPY_HOST, RESOLV_CONF_COPY_STATIC, RESOLV_CONF_COPY_UPLINK, RESOLV_CONF_COPY_STUB) &&
IN_SET(r, -ELOOP, -EROFS, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r, (r == -ELOOP || ERRNO_IS_NEG_FS_WRITE_REFUSED(r)) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to copy /etc/resolv.conf to %s, ignoring: %m", where); "Failed to copy /etc/resolv.conf to %s, ignoring: %m", where);
return 0; return 0;
} }

View File

@ -11,7 +11,7 @@
#include "missing_magic.h" #include "missing_magic.h"
#include "stat-util.h" #include "stat-util.h"
int binfmt_mounted(void) { int binfmt_mounted_and_writable(void) {
_cleanup_close_ int fd = -EBADF; _cleanup_close_ int fd = -EBADF;
int r; int r;
@ -25,7 +25,13 @@ int binfmt_mounted(void) {
if (r <= 0) if (r <= 0)
return r; return r;
return access_fd(fd, W_OK) >= 0; r = access_fd(fd, W_OK);
if (ERRNO_IS_NEG_FS_WRITE_REFUSED(r))
return false;
if (r < 0)
return r;
return true;
} }
int disable_binfmt(void) { int disable_binfmt(void) {
@ -37,7 +43,7 @@ int disable_binfmt(void) {
* We are a bit careful here, since binfmt_misc might still be an autofs which we don't want to * We are a bit careful here, since binfmt_misc might still be an autofs which we don't want to
* trigger. */ * trigger. */
r = binfmt_mounted(); r = binfmt_mounted_and_writable();
if (r < 0) if (r < 0)
return log_warning_errno(r, "Failed to determine whether binfmt_misc is mounted: %m"); return log_warning_errno(r, "Failed to determine whether binfmt_misc is mounted: %m");
if (r == 0) { if (r == 0) {

View File

@ -1,5 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once #pragma once
int binfmt_mounted(void); int binfmt_mounted_and_writable(void);
int disable_binfmt(void); int disable_binfmt(void);

View File

@ -48,7 +48,7 @@ static const char* const boot_entry_type_table[_BOOT_ENTRY_TYPE_MAX] = {
[BOOT_ENTRY_AUTO] = "auto", [BOOT_ENTRY_AUTO] = "auto",
}; };
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(boot_entry_type, BootEntryType); DEFINE_STRING_TABLE_LOOKUP(boot_entry_type, BootEntryType);
static const char* const boot_entry_source_description_table[_BOOT_ENTRY_SOURCE_MAX] = { static const char* const boot_entry_source_description_table[_BOOT_ENTRY_SOURCE_MAX] = {
[BOOT_ENTRY_ESP] = "EFI System Partition", [BOOT_ENTRY_ESP] = "EFI System Partition",

View File

@ -93,6 +93,7 @@ typedef struct BootConfig {
const char* boot_entry_type_description_to_string(BootEntryType) _const_; const char* boot_entry_type_description_to_string(BootEntryType) _const_;
const char* boot_entry_type_to_string(BootEntryType) _const_; const char* boot_entry_type_to_string(BootEntryType) _const_;
BootEntryType boot_entry_type_from_string(const char *s) _pure_;
const char* boot_entry_source_description_to_string(BootEntrySource) _const_; const char* boot_entry_source_description_to_string(BootEntrySource) _const_;
const char* boot_entry_source_to_string(BootEntrySource) _const_; const char* boot_entry_source_to_string(BootEntrySource) _const_;

View File

@ -660,7 +660,7 @@ int loop_device_make_by_path_at(
r = fd; r = fd;
/* Retry read-only? */ /* Retry read-only? */
if (open_flags >= 0 || !(ERRNO_IS_PRIVILEGE(r) || r == -EROFS)) if (open_flags >= 0 || !ERRNO_IS_NEG_FS_WRITE_REFUSED(r))
return r; return r;
fd = xopenat(dir_fd, path, basic_flags|direct_flags|O_RDONLY); fd = xopenat(dir_fd, path, basic_flags|direct_flags|O_RDONLY);

View File

@ -447,7 +447,7 @@ int main(int argc, char *argv[]) {
(void) sync_with_progress(-EBADF); (void) sync_with_progress(-EBADF);
disable_coredumps(); disable_coredumps();
disable_binfmt(); (void) disable_binfmt();
log_info("Sending SIGTERM to remaining processes..."); log_info("Sending SIGTERM to remaining processes...");
broadcast_signal(SIGTERM, true, true, arg_timeout); broadcast_signal(SIGTERM, true, true, arg_timeout);

View File

@ -321,7 +321,7 @@ static int add_export_unix_socket(
log_debug("Container manager does not provide /run/host/unix-export/ mount, not binding AF_UNIX socket there."); log_debug("Container manager does not provide /run/host/unix-export/ mount, not binding AF_UNIX socket there.");
return 0; return 0;
} }
if (errno == EROFS || ERRNO_IS_PRIVILEGE(errno)) { if (ERRNO_IS_FS_WRITE_REFUSED(errno)) {
log_debug("Container manager does not provide write access to /run/host/unix-export/, not binding AF_UNIX socket there."); log_debug("Container manager does not provide write access to /run/host/unix-export/, not binding AF_UNIX socket there.");
return 0; return 0;
} }

View File

@ -95,7 +95,7 @@ static int sysctl_write_or_warn(const char *key, const char *value, bool ignore_
* permission problem here, since that's how container managers usually protected their * permission problem here, since that's how container managers usually protected their
* sysctls.) * sysctls.)
* In all other cases log an error and make the tool fail. */ * In all other cases log an error and make the tool fail. */
if (ignore_failure || (!arg_strict && (r == -EROFS || ERRNO_IS_PRIVILEGE(r)))) if (ignore_failure || (!arg_strict && ERRNO_IS_NEG_FS_WRITE_REFUSED(r)))
log_debug_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key); log_debug_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);
else if (ignore_enoent && r == -ENOENT) else if (ignore_enoent && r == -ENOENT)
log_warning_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key); log_warning_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);

View File

@ -3,8 +3,8 @@
#include "binfmt-util.h" #include "binfmt-util.h"
#include "tests.h" #include "tests.h"
TEST(binfmt_mounted) { TEST(binfmt_mounted_and_writable) {
ASSERT_OK(binfmt_mounted()); ASSERT_OK(binfmt_mounted_and_writable());
} }
DEFINE_TEST_MAIN(LOG_DEBUG); DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -64,10 +64,8 @@ TEST(cg_create) {
(void) cg_trim(test_b, /* delete_root= */ true); (void) cg_trim(test_b, /* delete_root= */ true);
r = cg_create(test_a); r = cg_create(test_a);
if (IN_SET(r, -EPERM, -EACCES, -EROFS)) { if (ERRNO_IS_NEG_FS_WRITE_REFUSED(r))
log_info_errno(r, "Skipping %s: %m", __func__); return (void) log_tests_skipped_errno(r, "%s: Failed to create cgroup %s", __func__, test_a);
return;
}
ASSERT_OK_EQ(r, 1); ASSERT_OK_EQ(r, 1);
ASSERT_OK_ZERO(cg_create(test_a)); ASSERT_OK_ZERO(cg_create(test_a));

View File

@ -51,7 +51,7 @@ int main(int argc, char *argv[]) {
log_info("Reducing limit by one to %"PRIu64"", limit-1); log_info("Reducing limit by one to %"PRIu64"", limit-1);
r = procfs_tasks_set_limit(limit-1); r = procfs_tasks_set_limit(limit-1);
if (IN_SET(r, -ENOENT, -EROFS) || ERRNO_IS_PRIVILEGE(r)) if (r == -ENOENT || ERRNO_IS_NEG_FS_WRITE_REFUSED(r))
return log_tests_skipped_errno(r, "can't set tasks limit"); return log_tests_skipped_errno(r, "can't set tasks limit");
assert_se(r >= 0); assert_se(r >= 0);

View File

@ -54,14 +54,14 @@ TEST(sysctl_read) {
assert_se(STR_IN_SET(s, "0", "1")); assert_se(STR_IN_SET(s, "0", "1"));
r = sysctl_write_ip_property(AF_INET, "lo", "forwarding", s, NULL); r = sysctl_write_ip_property(AF_INET, "lo", "forwarding", s, NULL);
assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r) || r == -EROFS); assert_se(r >= 0 || ERRNO_IS_NEG_FS_WRITE_REFUSED(r));
s = mfree(s); s = mfree(s);
assert_se(sysctl_read_ip_property(AF_INET, NULL, "ip_forward", &s)); assert_se(sysctl_read_ip_property(AF_INET, NULL, "ip_forward", &s));
assert_se(STR_IN_SET(s, "0", "1")); assert_se(STR_IN_SET(s, "0", "1"));
r = sysctl_write_ip_property(AF_INET, NULL, "ip_forward", s, NULL); r = sysctl_write_ip_property(AF_INET, NULL, "ip_forward", s, NULL);
assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r) || r == -EROFS); assert_se(r >= 0 || ERRNO_IS_NEG_FS_WRITE_REFUSED(r));
s = mfree(s); s = mfree(s);
assert_se(sysctl_read("kernel/hostname", &s) >= 0); assert_se(sysctl_read("kernel/hostname", &s) >= 0);
@ -69,7 +69,7 @@ TEST(sysctl_read) {
ASSERT_STREQ(s, u.nodename); ASSERT_STREQ(s, u.nodename);
r = sysctl_write("kernel/hostname", s); r = sysctl_write("kernel/hostname", s);
assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r) || r == -EROFS); assert_se(r >= 0 || ERRNO_IS_NEG_FS_WRITE_REFUSED(r));
} }
DEFINE_TEST_MAIN(LOG_INFO); DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -1181,18 +1181,32 @@ static int ssh_authorized_keys(int argc, char *argv[], void *userdata) {
return r; return r;
} }
static int load_credential_one(int credential_dir_fd, const char *name, int userdb_dir_fd) { static int load_credential_one(
int credential_dir_fd,
const char *name,
int userdb_dir_persist_fd,
int userdb_dir_transient_fd) {
int r; int r;
assert(credential_dir_fd >= 0); assert(credential_dir_fd >= 0);
assert(name); assert(name);
assert(userdb_dir_fd >= 0); assert(userdb_dir_persist_fd >= 0);
assert(userdb_dir_transient_fd >= 0);
const char *user = startswith(name, "userdb.user."); const char *suffix = startswith(name, "userdb.");
const char *group = startswith(name, "userdb.group."); if (!suffix)
return 0;
const char *transient = startswith(suffix, "transient."),
*user = startswith(transient ?: suffix, "user."),
*group = startswith(transient ?: suffix, "group.");
if (!user && !group) if (!user && !group)
return 0; return 0;
const char *userdb_dir = transient ? "/run/userdb" : "/etc/userdb";
int userdb_dir_fd = transient ? userdb_dir_transient_fd : userdb_dir_persist_fd;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
unsigned line = 0, column = 0; unsigned line = 0, column = 0;
r = sd_json_parse_file_at(NULL, credential_dir_fd, name, SD_JSON_PARSE_SENSITIVE, &v, &line, &column); r = sd_json_parse_file_at(NULL, credential_dir_fd, name, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
@ -1350,12 +1364,12 @@ static int load_credential_one(int credential_dir_fd, const char *name, int user
r = write_string_file_at(userdb_dir_fd, fn, formatted, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); r = write_string_file_at(userdb_dir_fd, fn, formatted, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to write JSON record to /etc/userdb/%s: %m", fn); return log_error_errno(r, "Failed to write JSON record to %s/%s: %m", userdb_dir, fn);
if (symlinkat(fn, userdb_dir_fd, link) < 0) if (symlinkat(fn, userdb_dir_fd, link) < 0)
return log_error_errno(errno, "Failed to create symlink from %s to %s: %m", link, fn); return log_error_errno(errno, "Failed to create symlink from %s to %s: %m", link, fn);
log_info("Installed /etc/userdb/%s from credential.", fn); log_info("Installed %s/%s from credential.", userdb_dir, fn);
if ((ur && !sd_json_variant_is_blank_object(ur_privileged->json)) || if ((ur && !sd_json_variant_is_blank_object(ur_privileged->json)) ||
(gr && !sd_json_variant_is_blank_object(gr_privileged->json))) { (gr && !sd_json_variant_is_blank_object(gr_privileged->json))) {
@ -1371,7 +1385,7 @@ static int load_credential_one(int credential_dir_fd, const char *name, int user
r = write_string_file_at(userdb_dir_fd, fn, formatted, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_MODE_0600); r = write_string_file_at(userdb_dir_fd, fn, formatted, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_MODE_0600);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to write JSON record to /etc/userdb/%s: %m", fn); return log_error_errno(r, "Failed to write JSON record to %s/%s: %m", userdb_dir, fn);
link = mfree(link); link = mfree(link);
@ -1386,7 +1400,7 @@ static int load_credential_one(int credential_dir_fd, const char *name, int user
if (symlinkat(fn, userdb_dir_fd, link) < 0) if (symlinkat(fn, userdb_dir_fd, link) < 0)
return log_error_errno(errno, "Failed to create symlink from %s to %s: %m", link, fn); return log_error_errno(errno, "Failed to create symlink from %s to %s: %m", link, fn);
log_info("Installed /etc/userdb/%s from credential.", fn); log_info("Installed %s/%s from credential.", userdb_dir, fn);
} }
if (ur) if (ur)
@ -1399,7 +1413,7 @@ static int load_credential_one(int credential_dir_fd, const char *name, int user
if (fd < 0) if (fd < 0)
return log_error_errno(errno, "Failed to create %s: %m", membership); return log_error_errno(errno, "Failed to create %s: %m", membership);
log_info("Installed /etc/userdb/%s from credential.", membership); log_info("Installed %s/%s from credential.", userdb_dir, membership);
} }
else else
STRV_FOREACH(u, gr->members) { STRV_FOREACH(u, gr->members) {
@ -1411,7 +1425,7 @@ static int load_credential_one(int credential_dir_fd, const char *name, int user
if (fd < 0) if (fd < 0)
return log_error_errno(errno, "Failed to create %s: %m", membership); return log_error_errno(errno, "Failed to create %s: %m", membership);
log_info("Installed /etc/userdb/%s from credential.", membership); log_info("Installed %s/%s from credential.", userdb_dir, membership);
} }
if (ur && user_record_disposition(ur) == USER_REGULAR) { if (ur && user_record_disposition(ur) == USER_REGULAR) {
@ -1461,13 +1475,21 @@ static int load_credentials(int argc, char *argv[], void *userdata) {
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to enumerate credentials: %m"); return log_error_errno(r, "Failed to enumerate credentials: %m");
_cleanup_close_ int userdb_dir_fd = xopenat_full( _cleanup_close_ int userdb_persist_dir_fd = xopenat_full(
AT_FDCWD, "/etc/userdb", AT_FDCWD, "/etc/userdb",
/* open_flags= */ O_DIRECTORY|O_CREAT|O_CLOEXEC, /* open_flags= */ O_DIRECTORY|O_CREAT|O_CLOEXEC,
/* xopen_flags= */ XO_LABEL, /* xopen_flags= */ XO_LABEL,
/* mode= */ 0755); /* mode= */ 0755);
if (userdb_dir_fd < 0) if (userdb_persist_dir_fd < 0)
return log_error_errno(userdb_dir_fd, "Failed to open %s: %m", "/etc/userdb/"); return log_error_errno(userdb_persist_dir_fd, "Failed to open /etc/userdb/: %m");
_cleanup_close_ int userdb_transient_dir_fd = xopenat_full(
AT_FDCWD, "/run/userdb",
/* open_flags= */ O_DIRECTORY|O_CREAT|O_CLOEXEC,
/* xopen_flags= */ XO_LABEL,
/* mode= */ 0755);
if (userdb_transient_dir_fd < 0)
return log_error_errno(userdb_transient_dir_fd, "Failed to open /run/userdb/: %m");
FOREACH_ARRAY(i, des->entries, des->n_entries) { FOREACH_ARRAY(i, des->entries, des->n_entries) {
struct dirent *de = *i; struct dirent *de = *i;
@ -1475,7 +1497,11 @@ static int load_credentials(int argc, char *argv[], void *userdata) {
if (de->d_type != DT_REG) if (de->d_type != DT_REG)
continue; continue;
RET_GATHER(r, load_credential_one(credential_dir_fd, de->d_name, userdb_dir_fd)); RET_GATHER(r, load_credential_one(
credential_dir_fd,
de->d_name,
userdb_persist_dir_fd,
userdb_transient_dir_fd));
} }
return r; return r;

View File

@ -0,0 +1,316 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
# shellcheck disable=SC2016
set -eux
set -o pipefail
MACHINE_ID="$(</etc/machine-id)"
TEST_MSG_PREFIX="JOURNAL-RELOAD TEST"
SYSLOG_ID="$(systemd-id128 new)"
write_to_journal() {
local rand_val
rand_val=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 14)
echo "$TEST_MSG_PREFIX $rand_val" | systemd-cat -t "$SYSLOG_ID"
journalctl --sync
echo "$rand_val"
}
verify_archived_journals() {
local msg="$1"
local journal_path_prefix="$2"
local journal_dir="/$journal_path_prefix/log/journal/$MACHINE_ID"
for journal_file in "$journal_dir"/*; do
filename=$(basename -- "$journal_file")
if [[ "$filename" != "system.journal" ]]; then
if journalctl --file="$journal_file" | grep -q "$msg"; then
echo "Message present in archived journal: $filename"
exit 0
fi
fi
done
exit 1
}
# shellcheck disable=SC2317
verify_journal() {
local msg="$1"
local entry_expected="$2"
local test_name="$3"
local journal_path_prefix="$4"
local path="/$journal_path_prefix/log/journal/$MACHINE_ID/system.journal"
if [ ! -e "$path" ] || ! grep -Fxq "MESSAGE=$TEST_MSG_PREFIX $msg" "$path"; then
if [ "$entry_expected" == true ]; then
echo "$test_name ERROR: Message not present in $journal_path_prefix journal. Checking archived journals..."
if ! verify_archived_journals "$msg" "$journal_path_prefix"; then
echo "$test_name ERROR: Message also not present in archived journals"
cleanup
exit 1
fi
fi
else
if [ "$entry_expected" == false ]; then
echo "$test_name ERROR: Message present in $journal_path_prefix journal"
cleanup
exit 1
fi
fi
}
verify_journals() {
local msg="$1"
local runtime_expected="$2"
local system_expected="$3"
local test_name="$4"
local failed=false
if ! verify_journal "$msg" "$runtime_expected" "$test_name" "run"; then
failed=true
fi
if ! verify_journal "$msg" "$system_expected" "$test_name" "var"; then
failed=true
fi
if [ "$failed" == true ]; then
cleanup
exit 1
fi
}
get_num_archived_journals() {
local journal_path_prefix="$1"
local journal_dir="/$journal_path_prefix/log/journal/$MACHINE_ID/"
num_journal_files=$(find "$journal_dir" -type f -name "*.journal" ! -name "system.journal" | wc -l)
echo "$num_journal_files"
}
cleanup() {
rm /run/systemd/journald.conf.d/reload.conf
journalctl --vacuum-size=1M
systemctl daemon-reload
systemctl reload systemd-journald.service
}
# Start clean slate.
mkdir -p /run/systemd/journald.conf.d
cat <<EOF >/run/systemd/journald.conf.d/reload.conf
[Journal]
Storage=persistent
EOF
systemctl daemon-reload
systemctl restart systemd-journald.service
# Add entries in system.
journalctl --flush
rand_val1=$(write_to_journal)
verify_journals "$rand_val1" false true "Confirming test setup after flush."
# Reload journald (persistent->persistent)
systemctl reload systemd-journald.service
# Reload should persist persistent journal.
verify_journals "$rand_val1" false true "Persistent->Persistent System Reload: "
rand_val1=$(write_to_journal)
verify_journals "$rand_val1" false true "Persistent->Persistent System Post-Reload: "
# Add entries in runtime
journalctl --relinquish
rand_val2=$(write_to_journal)
verify_journals "$rand_val2" true false "Confirming test setup after relinquish."
# Reload journald (persistent->persistent)
systemctl reload systemd-journald.service
# System journal entries should stay in system journal, runtime in runtime.
verify_journals "$rand_val1" false true "Persistent->Persistent Runtime Reload 1: "
verify_journals "$rand_val2" true false "Persistent->Persistent Runtime Reload 2: "
# Write new message and confirm it's written to runtime.
rand_val=$(write_to_journal)
verify_journals "$rand_val" true false "Persistent->Persistent New Message After Reload: "
# Flush and confirm that messages are written to system.
journalctl --flush
rand_val=$(write_to_journal)
verify_journals "$rand_val" false true "Persistent->Volatile New Message Before Reload: "
# Test persistent->volatile
cat <<EOF >/run/systemd/journald.conf.d/reload.conf
[Journal]
Storage=volatile
EOF
# Confirm old message exists where it was written to (storage->storage).
systemctl reload systemd-journald.service
verify_journals "$rand_val" false true "Persistent->Volatile Reload: "
# Confirm that messages are written to only runtime journal.
rand_val=$(write_to_journal)
verify_journals "$rand_val" true false "Persistent->Volatile New Message After Reload: "
# Test volatile works and logs are NOT getting written to system journal despite flush.
journalctl --flush
rand_val=$(write_to_journal)
verify_journals "$rand_val" true false "Persistent->Volatile New Message After Flush: "
# Test that the new limits (e.g., RuntimeMaxUse) take effect on reload.
# Write 1M of data to runtime journal
max_size=1048656 # (1 * 1024 * 1024) = 1048576, but centos has a different minimum value for some reason.
set +x
dd if=/dev/urandom bs=1M count=5 | base64 | systemd-cat -t "$SYSLOG_ID"
set -x
journalctl --vacuum-size=2M
total_size=$(du -sb "/run/log/journal/$MACHINE_ID" | cut -f1)
if [ "$total_size" -lt "$max_size" ]; then
echo "ERROR: Journal size does not exceed RuntimeMaxUse limit"
cleanup
exit 1
fi
# Reload with RuntimeMaxUse=1M.
cat <<EOF >/run/systemd/journald.conf.d/reload.conf
[Journal]
Storage=volatile
RuntimeMaxUse=1M
EOF
# systemctl daemon-reload
systemctl reload systemd-journald.service
sleep 15 # Wait for RuntimeMaxUse change to take effect.
# Confirm that runtime journal size shrunk to <=1M.
total_size=$(du -sb "/run/log/journal/$MACHINE_ID" | cut -f1)
if [ "$total_size" -gt "$max_size" ]; then
echo "ERROR: Journal size exceeds RuntimeMaxUse limit"
cleanup
exit 1
fi
# Prepare for volatile->persistent by getting rid of runtime limit. Otherwise, it will not write.
cat <<EOF >/run/systemd/journald.conf.d/reload.conf
[Journal]
Storage=volatile
EOF
systemctl daemon-reload
systemctl reload systemd-journald.service
sleep 15 # Wait for RuntimeMaxUse change to take effect.
journalctl --vacuum-size=1M
sleep 5
rand_val=$(write_to_journal)
verify_journals "$rand_val" true false "Volatile->Persistent New Message Before Reload: "
# Reload volatile->persistent
cat <<EOF >/run/systemd/journald.conf.d/reload.conf
[Journal]
Storage=persistent
EOF
systemctl reload systemd-journald.service
# Confirm that previous message is still in runtime journal.
verify_journals "$rand_val" true false "Volatile->Persistent Reload: "
# Confirm that new messages are written to runtime journal.
rand_val=$(write_to_journal)
verify_journals "$rand_val" true false "Volatile->Persistent New Message After Reload: "
# Confirm that flushing writes to system journal.
journalctl --flush
verify_journals "$rand_val" false true "Volatile->Persistent New Message After Flush: "
set +x
dd if=/dev/urandom bs=1M count=5 | base64 | systemd-cat -t "$SYSLOG_ID"
set -x
max_size=$((2 * 1024 * 1024))
total_size=$(du -sb "/var/log/journal/$MACHINE_ID" | cut -f1)
if [ "$total_size" -lt "$max_size" ]; then
echo "ERROR: Journal size does not exceed SystemMaxUse limit"
cleanup
exit 1
fi
# Ensure reloading without limit does not interfere with SystemMaxUse test.
systemctl reload systemd-journald.service
total_size=$(du -sb "/var/log/journal/$MACHINE_ID" | cut -f1)
if [ "$total_size" -lt "$max_size" ]; then
echo "ERROR: Journal size does not exceed SystemMaxUse limit"
cleanup
exit 1
fi
# Write to storage to prepare for SystemMaxFiles test.
journalctl --flush
num_var_journals=$(get_num_archived_journals "var")
limit_var_journals=3
if [ "$num_var_journals" -lt "$limit_var_journals" ]; then
echo "Creating archive files."
for (( i=0; i<=num_var_journals; i++ ))
do
echo "$TEST_MSG_PREFIX" | systemd-cat -t "$SYSLOG_ID"
journalctl --rotate
done
num_var_journals=$(get_num_archived_journals "var")
if [ "$num_var_journals" -lt "$limit_var_journals" ]; then
echo "ERROR: Number of journal files in /var/log/journal/$MACHINE_ID/ is less than $limit_var_journals"
cleanup
exit 1
fi
fi
# Reload with less SystemMaxUse and SystemMaxFiles.
cat <<EOF >/run/systemd/journald.conf.d/reload.conf
[Journal]
Storage=persistent
RuntimeMaxUse=2M
SystemMaxUse=2M
SystemMaxFiles=3
EOF
systemctl daemon-reload
systemctl reload systemd-journald.service
# New system journal needs to be created with the new configuration for change to take effect.
journalctl --flush
# Check SystemMaxFiles
num_var_journals=$(get_num_archived_journals "var")
if [ "$num_var_journals" -gt "$limit_var_journals" ]; then
echo "ERROR: Number of journal files in /var/log/journal/$MACHINE_ID/ is greater than $limit_var_journals"
cleanup
exit 1
fi
sleep 15
# Check SystemMaxUse
total_size=$(du -sb "/var/log/journal/$MACHINE_ID" | cut -f1)
if [ "$total_size" -gt "$max_size" ]; then
echo "ERROR: Journal size exceeds SystemMaxUse limit"
cleanup
exit 1
fi
rm /run/systemd/journald.conf.d/reload.conf
journalctl --vacuum-size=1M
systemctl daemon-reload
systemctl reload systemd-journald.service

View File

@ -57,7 +57,7 @@ StandardOutput=null
SystemCallArchitectures=native SystemCallArchitectures=native
SystemCallErrorNumber=EPERM SystemCallErrorNumber=EPERM
SystemCallFilter=@system-service SystemCallFilter=@system-service
Type=notify Type=notify-reload
PassEnvironment=TERM PassEnvironment=TERM
{{SERVICE_WATCHDOG}} {{SERVICE_WATCHDOG}}

View File

@ -26,6 +26,8 @@ RemainAfterExit=yes
ExecStart=userdbctl load-credentials ExecStart=userdbctl load-credentials
ImportCredential=userdb.user.* ImportCredential=userdb.user.*
ImportCredential=userdb.group.* ImportCredential=userdb.group.*
ImportCredential=userdb.transient.user.*
ImportCredential=userdb.transient.group.*
[Install] [Install]
WantedBy=sysinit.target WantedBy=sysinit.target