1
0
mirror of https://github.com/systemd/systemd synced 2026-02-25 16:54:44 +01:00

Compare commits

..

No commits in common. "61b31f7999107e6a5dbda76049dc37f8061c082c" and "a2a78602b08bcec571fb081bdeb1d8854d91f93e" have entirely different histories.

41 changed files with 502 additions and 756 deletions

9
NEWS
View File

@ -55,14 +55,7 @@ CHANGES WITH 260 in spe:
Device nodes should not be owned by a non-system user/group. It is
recommended to check udev rules files with 'udevadm verify' and/or
'udevadm test' commands.
* systemd-repart will now make use of mkfs.xfs's support for
populating XFS filesystems from a directory. This support was
added in xfsprogs 6.17.0 released 20 October 2025. As there is no
proper way to detect whether mkfs.xfs supports populating from a
directory or not, we make use of it unconditionally and have dropped
support for the old way using protofiles.
'udevadm test' commands .
New system interfaces and components:

1
TODO
View File

@ -967,6 +967,7 @@ Features:
* Varlinkification of the following command line tools, to open them up to
other programs via IPC:
- bootctl
- coredumpcl
- systemd-bless-boot
- systemd-measure

View File

@ -120,16 +120,11 @@ node /org/freedesktop/sysupdate1/target/host {
in t flags,
out s json);
CheckNew(out s new_version);
Acquire(in s new_version,
in t flags,
out s new_version,
out t job_id,
out o job_path);
Install(in s new_version,
in t flags,
out s new_version,
out t job_id,
out o job_path);
Update(in s new_version,
in t flags,
out s new_version,
out t job_id,
out o job_path);
Vacuum(out u instances,
out u disabled_transfers);
GetAppStream(out as appstream);
@ -168,9 +163,7 @@ node /org/freedesktop/sysupdate1/target/host {
<variablelist class="dbus-method" generated="True" extra-ref="CheckNew()"/>
<variablelist class="dbus-method" generated="True" extra-ref="Acquire()"/>
<variablelist class="dbus-method" generated="True" extra-ref="Install()"/>
<variablelist class="dbus-method" generated="True" extra-ref="Update()"/>
<variablelist class="dbus-method" generated="True" extra-ref="Vacuum()"/>
@ -252,8 +245,7 @@ node /org/freedesktop/sysupdate1/target/host {
<listitem><para>A boolean indicating whether this version is incomplete, which means that it is
missing some file. Note that only installed incomplete versions will be offered by the service;
versions that are incomplete on the server-side are completely ignored. Incomplete versions can
be repaired in-place by calling <function>Acquire()</function> and <function>Install()</function>
on that version.</para></listitem>
be repaired in-place by calling <function>Update()</function> on that version.</para></listitem>
</varlistentry>
<varlistentry>
@ -269,25 +261,16 @@ node /org/freedesktop/sysupdate1/target/host {
<function>Describe()</function> to query more information about the version returned by this method.
</para>
<para><function>Acquire()</function> downloads an update for this target, if one is available. If a
<varname>new_version</varname> is specified, that is the version that gets downloaded. Otherwise, the
latest version is downloaded. Call <function>Install()</function> to install the acquired update.
The <varname>flags</varname> argument is added for future extensibility. No flags are currently
defined, and the argument is required to be set to <literal>0</literal>. This method pulls both
metadata and payload data from the network.</para>
<para><function>Install()</function> installs an already-acquired update for this target. If a
<varname>new_version</varname> is specified, that is the version that gets installed, assuming it has
already been acquired. Otherwise, the latest acquired version is installed. The
<varname>flags</varname> argument is added for future extensibility. No flags are currently defined,
and the argument is required to be set to <literal>0</literal>.</para>
<para>Unlike all the other methods in this interface, <function>Acquire()</function> and
<function>Install()</function> do not wait for their jobs to complete. Instead, they return the job's
numeric ID and object path as soon as the job begins, so that the caller can listen for progress
updates or cancel the operation. These methods also return the version the target will be updated to,
for cases where no version was specified by the caller. Listen for the Manager's
<function>JobRemoved()</function> signal to detect when the job is complete.</para>
<para><function>Update()</function> installs an update for this target. If a
<varname>new_version</varname> is specified, that is the version that gets installed. Otherwise, the
latest version is installed. The <varname>flags</varname> argument is added for future
extensibility. No flags are currently defined, and the argument is required to be set to
<literal>0</literal>. Unlike all the other methods in this interface, <function>Update()</function>
does not wait for its job to complete. Instead, it returns the job's numeric ID and object path as soon
as the job begins, so that the caller can listen for progress updates or cancel the operation. This
method also returns the version the target will be updated to, for cases where no version was specified
by the caller. This method pulls both metadata and payload data from the network. Listen for the
Manager's <function>JobRemoved()</function> signal to detect when the job is complete.</para>
<para><function>Vacuum()</function> deletes old installed versions of this target to free up space.
It returns the number of instances that have been deleted.</para>
@ -364,7 +347,7 @@ node /org/freedesktop/sysupdate1/target/host {
preemptive decisions to be made about features that are planned to appear in future releases of the OS.
The drop-in will have a filename of <literal>50-systemd-sysupdate-enabled.conf</literal>.
This method only changes configuration files; to actually apply the changes, clients will need to
call <function>Acquire()</function> and <function>Install()</function>.
call <function>Update()</function>.
Depending on the exact needs of the client, it can choose to update the system to the latest available
version, or it can extend the newest existing installation in-place (by passing in the version returned
by <varname>GetVersion()</varname>).
@ -414,8 +397,7 @@ node /org/freedesktop/sysupdate1/target/host {
use the polkit action <interfacename>org.freedesktop.sysupdate1.check</interfacename>.
By default, this action is permitted without administrator authentication.</para>
<para><function>Acquire()</function> and <function>Install()</function>
use the polkit action
<para><function>Update()</function> uses the polkit action
<interfacename>org.freedesktop.sysupdate1.update</interfacename> when no version is specified.
By default, this action is permitted without administrator authentication. When a version is
specified, <interfacename>org.freedesktop.sysupdate1.update-to-version</interfacename> is
@ -514,15 +496,14 @@ node /org/freedesktop/sysupdate1/job/_1 {
<para>The <varname>Type</varname> property exposes the type of operation (one of:
<literal>list</literal>, <literal>describe</literal>, <literal>check-new</literal>,
<literal>acquire</literal>, <literal>install</literal>, <literal>vacuum</literal>, or
<literal>describe-feature</literal>).</para>
<literal>update</literal>, <literal>vacuum</literal>, or <literal>describe-feature</literal>).</para>
<para>The <varname>Offline</varname> property exposes whether the job is permitted to access
the network or not.</para>
<para>The <varname>Progress</varname> property exposes the current progress of the job as a value
between 0 and 100. It is only available for <literal>acquire</literal> and <literal>install</literal>
jobs; for all other jobs it is always 0.</para>
between 0 and 100. It is only available for <literal>update</literal> jobs; for all other jobs
it is always 0.</para>
</refsect2>
<refsect2>
@ -581,8 +562,7 @@ node /org/freedesktop/sysupdate1/job/_1 {
<para><function>List()</function>,
<function>Describe()</function>,
<function>CheckNew()</function>,
<function>Acquire()</function>,
<function>Install()</function>,
<function>Update()</function>,
<function>Vacuum()</function>,
<function>GetAppStream()</function>,
<function>GetVersion()</function>,

View File

@ -497,6 +497,17 @@
in a user namespace with the current user mapped to the root user to make sure the files and
directories in the image are owned by the root user.</para>
<para>Note that when populating XFS filesystems with <command>systemd-repart</command> and loop
devices are not available, populating XFS filesystems with files containing spaces, tabs or newlines
might fail on old versions of
<citerefentry project='man-pages'><refentrytitle>mkfs.xfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
due to limitations of its protofile format.</para>
<para>Note that when populating XFS filesystems with <command>systemd-repart</command> and loop
devices are not available, extended attributes will not be copied into generated XFS filesystems
due to limitations <citerefentry project='man-pages'><refentrytitle>mkfs.xfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
protofile format.</para>
<para>This option cannot be combined with <varname>CopyBlocks=</varname>.</para>
<para>When
@ -506,9 +517,6 @@
<option>--image=</option> or <option>--root=</option> switches are used, the source paths are taken
relative to the specified root directory or disk image root.</para>
<para>Note that when <varname>CopyFiles=</varname> is used with <varname>Format=xfs</varname>,
<command>xfsprogs</command> 6.17 or newer is required.</para>
<xi:include href="version-info.xml" xpointer="v247"/></listitem>
</varlistentry>

View File

@ -1797,17 +1797,6 @@
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ConditionPathIsSocket=</varname></term>
<listitem><para><varname>ConditionPathIsSocket=</varname> is similar to
<varname>ConditionPathExists=</varname> but verifies that a certain path exists and is a
socket.</para>
<xi:include href="version-info.xml" xpointer="v260"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ConditionDirectoryNotEmpty=</varname></term>
@ -2063,7 +2052,6 @@
<term><varname>AssertPathIsMountPoint=</varname></term>
<term><varname>AssertPathIsReadWrite=</varname></term>
<term><varname>AssertPathIsEncrypted=</varname></term>
<term><varname>AssertPathIsSocket=</varname></term>
<term><varname>AssertDirectoryNotEmpty=</varname></term>
<term><varname>AssertFileNotEmpty=</varname></term>
<term><varname>AssertFileIsExecutable=</varname></term>

View File

@ -895,6 +895,7 @@ foreach option : ['adm-gid',
'video-gid',
'wheel-gid',
'systemd-journal-gid',
'systemd-journal-uid',
'systemd-network-uid',
'systemd-resolve-uid',
'systemd-timesync-uid']

View File

@ -328,6 +328,8 @@ option('wheel-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "wheel" group')
option('systemd-journal-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the systemd-journal group')
option('systemd-journal-uid', type : 'integer', value : 0,
description : 'soft-static allocation for the systemd-journal user')
option('systemd-network-uid', type : 'integer', value : 0,
description : 'soft-static allocation for the systemd-network user')
option('systemd-resolve-uid', type : 'integer', value : 0,

View File

@ -97,7 +97,6 @@ typedef enum RuntimeScope RuntimeScope;
typedef enum TimestampStyle TimestampStyle;
typedef enum UnitActiveState UnitActiveState;
typedef enum UnitDependency UnitDependency;
typedef enum UnitNameMangle UnitNameMangle;
typedef enum UnitType UnitType;
typedef enum WaitFlags WaitFlags;

View File

@ -366,7 +366,6 @@ Unit.ConditionPathIsSymbolicLink, config_parse_unit_condition_path,
Unit.ConditionPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, conditions)
Unit.ConditionPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, conditions)
Unit.ConditionPathIsEncrypted, config_parse_unit_condition_path, CONDITION_PATH_IS_ENCRYPTED, offsetof(Unit, conditions)
Unit.ConditionPathIsSocket, config_parse_unit_condition_path, CONDITION_PATH_IS_SOCKET, offsetof(Unit, conditions)
Unit.ConditionDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, conditions)
Unit.ConditionFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, conditions)
Unit.ConditionFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, conditions)
@ -402,7 +401,6 @@ Unit.AssertPathIsSymbolicLink, config_parse_unit_condition_path,
Unit.AssertPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, asserts)
Unit.AssertPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, asserts)
Unit.AssertPathIsEncrypted, config_parse_unit_condition_path, CONDITION_PATH_IS_ENCRYPTED, offsetof(Unit, asserts)
Unit.AssertPathIsSocket, config_parse_unit_condition_path, CONDITION_PATH_IS_SOCKET, offsetof(Unit, asserts)
Unit.AssertDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, asserts)
Unit.AssertFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, asserts)
Unit.AssertFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, asserts)

View File

@ -9,7 +9,6 @@
#include "chase.h"
#include "devnum-util.h"
#include "fileio.h"
#include "glob-util.h"
#include "journal-internal.h"
#include "journalctl.h"
#include "journalctl-filter.h"
@ -72,116 +71,6 @@ static int add_dmesg(sd_journal *j) {
return sd_journal_add_conjunction(j);
}
int journal_add_unit_matches(
sd_journal *j,
MatchUnitFlag flags,
UnitNameMangle mangle_flags,
char * const *system_units,
uid_t uid,
char * const *user_units) {
_cleanup_strv_free_ char **patterns = NULL;
bool added = false;
int r;
assert(j);
if (strv_isempty(system_units) && strv_isempty(user_units))
return 0;
STRV_FOREACH(i, system_units) {
_cleanup_free_ char *u = NULL;
r = unit_name_mangle(*i, UNIT_NAME_MANGLE_GLOB | mangle_flags, &u);
if (r < 0)
return r;
if (string_is_glob(u)) {
r = strv_consume(&patterns, TAKE_PTR(u));
if (r < 0)
return r;
} else {
r = add_matches_for_unit_full(j, flags, u);
if (r < 0)
return r;
r = sd_journal_add_disjunction(j);
if (r < 0)
return r;
added = true;
}
}
if (!strv_isempty(patterns)) {
_cleanup_set_free_ Set *units = NULL;
r = get_possible_units(j, SYSTEM_UNITS_FULL, patterns, &units);
if (r < 0)
return r;
char *u;
SET_FOREACH(u, units) {
r = add_matches_for_unit_full(j, flags, u);
if (r < 0)
return r;
r = sd_journal_add_disjunction(j);
if (r < 0)
return r;
added = true;
}
}
patterns = strv_free(patterns);
STRV_FOREACH(i, user_units) {
_cleanup_free_ char *u = NULL;
r = unit_name_mangle(*i, UNIT_NAME_MANGLE_GLOB | mangle_flags, &u);
if (r < 0)
return r;
if (string_is_glob(u)) {
r = strv_consume(&patterns, TAKE_PTR(u));
if (r < 0)
return r;
} else {
r = add_matches_for_user_unit_full(j, flags, uid, u);
if (r < 0)
return r;
r = sd_journal_add_disjunction(j);
if (r < 0)
return r;
added = true;
}
}
if (!strv_isempty(patterns)) {
_cleanup_set_free_ Set *units = NULL;
r = get_possible_units(j, USER_UNITS_FULL, patterns, &units);
if (r < 0)
return r;
char *u;
SET_FOREACH(u, units) {
r = add_matches_for_user_unit_full(j, flags, uid, u);
if (r < 0)
return r;
r = sd_journal_add_disjunction(j);
if (r < 0)
return r;
added = true;
}
}
/* Complain if the user request matches but nothing whatsoever was found, since otherwise everything
* would be matched. */
if (!added)
return -ENODATA;
return sd_journal_add_conjunction(j);
}
static int add_units(sd_journal *j) {
MatchUnitFlag flags = MATCH_UNIT_ALL;
@ -193,9 +82,7 @@ static int add_units(sd_journal *j) {
if (arg_directory || arg_root || arg_file_stdin || arg_file || arg_machine)
flags &= ~MATCH_UNIT_COREDUMP_UID;
return journal_add_unit_matches(j, flags, arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
arg_system_units,
UID_INVALID, arg_user_units);
return journal_add_unit_matches(j, flags, arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, arg_system_units, arg_user_units);
}
static int add_syslog_identifier(sd_journal *j) {

View File

@ -3,12 +3,4 @@
#include "shared-forward.h"
int journal_add_unit_matches(
sd_journal *j,
MatchUnitFlag flags,
UnitNameMangle mangle_flags,
char * const *system_units,
uid_t uid,
char * const *user_units);
int add_filters(sd_journal *j, char **matches);

View File

@ -177,6 +177,108 @@ int get_possible_units(
return 0;
}
int journal_add_unit_matches(sd_journal *j, MatchUnitFlag flags, UnitNameMangle mangle_flags, char **system_units, char **user_units) {
_cleanup_strv_free_ char **patterns = NULL;
bool added = false;
int r;
assert(j);
if (strv_isempty(system_units) && strv_isempty(user_units))
return 0;
STRV_FOREACH(i, system_units) {
_cleanup_free_ char *u = NULL;
r = unit_name_mangle(*i, UNIT_NAME_MANGLE_GLOB | mangle_flags, &u);
if (r < 0)
return r;
if (string_is_glob(u)) {
r = strv_consume(&patterns, TAKE_PTR(u));
if (r < 0)
return r;
} else {
r = add_matches_for_unit_full(j, flags, u);
if (r < 0)
return r;
r = sd_journal_add_disjunction(j);
if (r < 0)
return r;
added = true;
}
}
if (!strv_isempty(patterns)) {
_cleanup_set_free_ Set *units = NULL;
r = get_possible_units(j, SYSTEM_UNITS_FULL, patterns, &units);
if (r < 0)
return r;
char *u;
SET_FOREACH(u, units) {
r = add_matches_for_unit_full(j, flags, u);
if (r < 0)
return r;
r = sd_journal_add_disjunction(j);
if (r < 0)
return r;
added = true;
}
}
patterns = strv_free(patterns);
STRV_FOREACH(i, user_units) {
_cleanup_free_ char *u = NULL;
r = unit_name_mangle(*i, UNIT_NAME_MANGLE_GLOB | mangle_flags, &u);
if (r < 0)
return r;
if (string_is_glob(u)) {
r = strv_consume(&patterns, TAKE_PTR(u));
if (r < 0)
return r;
} else {
r = add_matches_for_user_unit_full(j, flags, u);
if (r < 0)
return r;
r = sd_journal_add_disjunction(j);
if (r < 0)
return r;
added = true;
}
}
if (!strv_isempty(patterns)) {
_cleanup_set_free_ Set *units = NULL;
r = get_possible_units(j, USER_UNITS_FULL, patterns, &units);
if (r < 0)
return r;
char *u;
SET_FOREACH(u, units) {
r = add_matches_for_user_unit_full(j, flags, u);
if (r < 0)
return r;
r = sd_journal_add_disjunction(j);
if (r < 0)
return r;
added = true;
}
}
/* Complain if the user request matches but nothing whatsoever was found, since otherwise everything
* would be matched. */
if (!added)
return -ENODATA;
return sd_journal_add_conjunction(j);
}
int acquire_unit(sd_journal *j, const char *option_name, const char **ret_unit, LogIdType *ret_type) {
size_t n;
int r;

View File

@ -3,6 +3,7 @@
#include "shared-forward.h"
#include "logs-show.h"
#include "unit-name.h"
/* The lists below are supposed to return the superset of unit names possibly matched by rules added with
* add_matches_for_unit() and add_matches_for_user_unit(). */
@ -31,5 +32,6 @@ int acquire_journal(sd_journal **ret);
bool journal_boot_has_effect(sd_journal *j);
int journal_acquire_boot(sd_journal *j);
int get_possible_units(sd_journal *j, const char *fields, char * const *patterns, Set **ret);
int journal_add_unit_matches(sd_journal *j, MatchUnitFlag flags, UnitNameMangle mangle_flags, char **system_units, char **user_units);
int acquire_unit(sd_journal *j, const char *option_name, const char **ret_unit, LogIdType *ret_type);
int journal_acquire_invocation(sd_journal *j);

View File

@ -4,30 +4,25 @@
#include "sd-varlink.h"
#include "journal-internal.h"
#include "journalctl.h"
#include "journalctl-filter.h"
#include "journalctl-util.h"
#include "journalctl-varlink-server.h"
#include "json-util.h"
#include "log.h"
#include "logs-show.h"
#include "output-mode.h"
#include "runtime-scope.h"
#include "strv.h"
#include "unit-name.h" /* IWYU pragma: keep */
#include "user-util.h"
#include "varlink-util.h"
typedef struct GetEntriesParameters {
char **units;
char **user_units;
const char *namespace;
uid_t uid;
int priority;
uint64_t limit;
} GetEntriesParameters;
static void get_entries_parameters_done(GetEntriesParameters *p) {
assert(p);
p->units = strv_free(p->units);
p->user_units = strv_free(p->user_units);
}
@ -36,7 +31,6 @@ int vl_method_get_entries(sd_varlink *link, sd_json_variant *parameters, sd_varl
static const sd_json_dispatch_field dispatch_table[] = {
{ "units", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(GetEntriesParameters, units), 0 },
{ "uid", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(GetEntriesParameters, uid), 0 },
{ "userUnits", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(GetEntriesParameters, user_units), 0 },
{ "namespace", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(GetEntriesParameters, namespace), 0 },
{ "priority", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_log_level, offsetof(GetEntriesParameters, priority), 0 },
@ -45,7 +39,6 @@ int vl_method_get_entries(sd_varlink *link, sd_json_variant *parameters, sd_varl
};
_cleanup_(get_entries_parameters_done) GetEntriesParameters p = {
.uid = UID_INVALID,
.priority = -1,
};
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
@ -58,26 +51,23 @@ int vl_method_get_entries(sd_varlink *link, sd_json_variant *parameters, sd_varl
if (r != 0)
return r;
if (arg_varlink_runtime_scope == RUNTIME_SCOPE_SYSTEM && p.user_units && !uid_is_valid(p.uid))
return sd_varlink_error_invalid_parameter_name(link, "uid");
/* systemd ships with sensible defaults for the system/user services and the socket permissions so we
* do not need to do extra sd_varlink_get_peer_uid() or policykit checks here */
r = sd_journal_open_namespace(&j, p.namespace, SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE);
if (r < 0)
return r;
return log_error_errno(r, "Failed to open journal: %m");
r = journal_add_unit_matches(j, MATCH_UNIT_ALL, /* mangle_flags= */ 0, p.units, p.uid, p.user_units);
r = journal_add_unit_matches(j, MATCH_UNIT_ALL, /* mangle_flags= */ 0, p.units, p.user_units);
if (r == -ENODATA)
return sd_varlink_error(link, "io.systemd.JournalAccess.NoMatches", NULL);
return sd_varlink_error(link, SD_VARLINK_ERROR_INVALID_PARAMETER, NULL);
if (r < 0)
return r;
return log_error_errno(r, "Failed to add unit matches: %m");
if (p.priority >= 0) {
for (int i = 0; i <= p.priority; i++) {
r = journal_add_matchf(j, "PRIORITY=%d", i);
if (r < 0)
return r;
return log_error_errno(r, "Failed to add priority match: %m");
}
r = sd_journal_add_conjunction(j);
@ -88,7 +78,7 @@ int vl_method_get_entries(sd_varlink *link, sd_json_variant *parameters, sd_varl
/* this simulates "journalctl -n $p.limit" */
r = sd_journal_seek_tail(j);
if (r < 0)
return r;
return log_error_errno(r, "Failed to seek to tail: %m");
/* FIXME: this restriction should be removed eventually */
if (p.limit > 10000)
@ -96,10 +86,6 @@ int vl_method_get_entries(sd_varlink *link, sd_json_variant *parameters, sd_varl
uint64_t n = p.limit == 0 ? 100 : p.limit;
r = sd_journal_previous_skip(j, n + 1);
if (r < 0)
return r;
r = varlink_set_sentinel(link, "io.systemd.JournalAccess.NoEntries");
if (r < 0)
return r;
@ -107,9 +93,9 @@ int vl_method_get_entries(sd_varlink *link, sd_json_variant *parameters, sd_varl
for (uint64_t i = 0; i < n; i++) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *entry = NULL;
r = sd_journal_next(j);
r = sd_journal_previous(j);
if (r < 0)
return r;
return log_error_errno(r, "Failed to iterate journal: %m");
if (r == 0)
break;
@ -119,7 +105,7 @@ int vl_method_get_entries(sd_varlink *link, sd_json_variant *parameters, sd_varl
if (r == 0)
continue; /* skip corrupted entry */
r = sd_varlink_replybo(link, SD_JSON_BUILD_PAIR_VARIANT("entry", entry));
r = sd_varlink_replybo(link, SD_JSON_BUILD_PAIR("entry", SD_JSON_BUILD_VARIANT(entry)));
if (r < 0)
return r;
}

View File

@ -30,7 +30,6 @@
#include "parse-util.h"
#include "pcre2-util.h"
#include "pretty-print.h"
#include "runtime-scope.h"
#include "set.h"
#include "static-destruct.h"
#include "string-table.h"
@ -112,9 +111,7 @@ pcre2_code *arg_compiled_pattern = NULL;
PatternCompileCase arg_case = PATTERN_COMPILE_CASE_AUTO;
ImagePolicy *arg_image_policy = NULL;
bool arg_synchronize_on_exit = false;
static bool arg_varlink = false;
RuntimeScope arg_varlink_runtime_scope = _RUNTIME_SCOPE_INVALID;
STATIC_DESTRUCTOR_REGISTER(arg_cursor, freep);
STATIC_DESTRUCTOR_REGISTER(arg_cursor_file, freep);
@ -489,35 +486,7 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
if (r > 0) {
arg_varlink = true;
static const struct option varlink_options[] = {
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "user", no_argument, NULL, ARG_USER },
{}
};
while ((c = getopt_long(argc, argv, "", varlink_options, NULL)) >= 0)
switch (c) {
case ARG_SYSTEM:
arg_varlink_runtime_scope = RUNTIME_SCOPE_SYSTEM;
break;
case ARG_USER:
arg_varlink_runtime_scope = RUNTIME_SCOPE_USER;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
if (arg_varlink_runtime_scope < 0)
return log_error_errno(arg_varlink_runtime_scope, "Cannot run in Varlink mode with no runtime scope specified.");
arg_pager_flags |= PAGER_DISABLE;
return 1;
}

View File

@ -94,6 +94,3 @@ extern bool arg_synchronize_on_exit;
static inline bool arg_lines_needs_seek_end(void) {
return arg_lines >= 0 && !arg_lines_oldest;
}
/* Only used for varlink server invocation */
extern RuntimeScope arg_varlink_runtime_scope;

View File

@ -693,46 +693,20 @@ int device_clone_with_db(sd_device *device, sd_device **ret) {
return 0;
}
int device_copy_all_tags(sd_device *dest, sd_device *src) {
int r;
assert(dest);
if (!src)
return 0;
FOREACH_DEVICE_TAG(src, tag) {
r = device_add_tag(dest, tag, /* both= */ false);
if (r < 0)
return r;
}
return 0;
}
int device_cleanup_tags(sd_device *device, sd_device *original) {
int r;
void device_cleanup_tags(sd_device *device) {
assert(device);
_cleanup_set_free_ Set *saved = TAKE_PTR(device->all_tags);
device->all_tags = set_free(device->all_tags);
device->current_tags = set_free(device->current_tags);
device->property_tags_outdated = true;
device->tags_generation++;
r = device_copy_all_tags(device, original);
if (r < 0) {
set_free_and_replace(device->all_tags, saved);
return r;
}
return 0;
}
void device_cleanup_devlinks(sd_device *device) {
assert(device);
device->devlinks = set_free(device->devlinks);
set_free(device->devlinks);
device->devlinks = NULL;
device->property_devlinks_outdated = true;
device->devlinks_generation++;
}
@ -769,9 +743,15 @@ static int device_tag(sd_device *device, const char *tag, bool add) {
return 0;
}
int device_tag_index(sd_device *device, bool add) {
int device_tag_index(sd_device *device, sd_device *device_old, bool add) {
int r = 0;
if (add && device_old)
/* delete possible left-over tags */
FOREACH_DEVICE_TAG(device_old, tag)
if (!sd_device_has_tag(device, tag))
RET_GATHER(r, device_tag(device_old, tag, false));
FOREACH_DEVICE_TAG(device, tag)
RET_GATHER(r, device_tag(device, tag, add));

View File

@ -45,8 +45,7 @@ int device_add_property(sd_device *device, const char *key, const char *value);
int device_add_propertyf(sd_device *device, const char *key, const char *format, ...) _printf_(3, 4);
int device_add_tag(sd_device *device, const char *tag, bool both);
void device_remove_tag(sd_device *device, const char *tag);
int device_copy_all_tags(sd_device *dest, sd_device *src);
int device_cleanup_tags(sd_device *device, sd_device *original);
void device_cleanup_tags(sd_device *device);
void device_cleanup_devlinks(sd_device *device);
uint64_t device_get_properties_generation(sd_device *device);
@ -59,7 +58,7 @@ int device_get_properties_strv(sd_device *device, char ***ret);
int device_clone_with_db(sd_device *device, sd_device **ret);
int device_tag_index(sd_device *device, bool add);
int device_tag_index(sd_device *device, sd_device *device_old, bool add);
bool device_should_have_db(sd_device *device);
int device_has_db(sd_device *device);
int device_update_db(sd_device *device);

View File

@ -992,14 +992,6 @@ static int condition_test_path_is_encrypted(Condition *c, char **env) {
return r > 0;
}
static int condition_test_path_is_socket(Condition *c, char **env) {
assert(c);
assert(c->parameter);
assert(c->type == CONDITION_PATH_IS_SOCKET);
return is_socket(c->parameter) > 0;
}
static int condition_test_directory_not_empty(Condition *c, char **env) {
int r;
@ -1241,7 +1233,6 @@ int condition_test(Condition *c, char **env) {
[CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
[CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
[CONDITION_PATH_IS_ENCRYPTED] = condition_test_path_is_encrypted,
[CONDITION_PATH_IS_SOCKET] = condition_test_path_is_socket,
[CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
[CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
[CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
@ -1379,7 +1370,6 @@ static const char* const _condition_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
[CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
[CONDITION_PATH_IS_ENCRYPTED] = "ConditionPathIsEncrypted",
[CONDITION_PATH_IS_SOCKET] = "ConditionPathIsSocket",
[CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
[CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
[CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
@ -1435,7 +1425,6 @@ static const char* const _assert_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
[CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
[CONDITION_PATH_IS_ENCRYPTED] = "AssertPathIsEncrypted",
[CONDITION_PATH_IS_SOCKET] = "AssertPathIsSocket",
[CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
[CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
[CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",

View File

@ -34,7 +34,6 @@ typedef enum ConditionType {
CONDITION_PATH_IS_MOUNT_POINT,
CONDITION_PATH_IS_READ_WRITE,
CONDITION_PATH_IS_ENCRYPTED,
CONDITION_PATH_IS_SOCKET,
CONDITION_DIRECTORY_NOT_EMPTY,
CONDITION_FILE_NOT_EMPTY,
CONDITION_FILE_IS_EXECUTABLE,
@ -105,7 +104,6 @@ static inline bool condition_takes_path(ConditionType t) {
CONDITION_PATH_IS_MOUNT_POINT,
CONDITION_PATH_IS_READ_WRITE,
CONDITION_PATH_IS_ENCRYPTED,
CONDITION_PATH_IS_SOCKET,
CONDITION_DIRECTORY_NOT_EMPTY,
CONDITION_FILE_NOT_EMPTY,
CONDITION_FILE_IS_EXECUTABLE,

View File

@ -1193,20 +1193,17 @@ static int update_json_data_split(
return update_json_data(h, flags, name, eq + 1, size - fieldlen - 1);
}
int journal_entry_to_json(
sd_journal *j,
OutputFlags flags,
const Set *output_fields,
sd_json_variant **ret) {
int journal_entry_to_json(sd_journal *j, OutputFlags flags, const Set *output_fields, sd_json_variant **ret) {
char usecbuf[CONST_MAX(DECIMAL_STR_MAX(usec_t), DECIMAL_STR_MAX(uint64_t))];
_cleanup_(sd_json_variant_unrefp) sd_json_variant *object = NULL;
_cleanup_hashmap_free_ Hashmap *h = NULL;
sd_id128_t journal_boot_id, seqnum_id;
_cleanup_free_ char *cursor = NULL;
usec_t realtime, monotonic;
sd_json_variant **array = NULL;
JsonData *d;
uint64_t seqnum;
const char *corrupted_what = NULL;
_cleanup_hashmap_free_ Hashmap *h = NULL;
_cleanup_free_ sd_json_variant **array = NULL;
size_t n = 0;
int r;
@ -1217,32 +1214,32 @@ int journal_entry_to_json(
r = sd_journal_get_cursor(j, &cursor);
if (IN_SET(r, -EBADMSG, -EADDRNOTAVAIL)) {
corrupted_what = "cursor";
goto corrupted_skip;
log_debug_errno(r, "Unable to determine cursor of entry, assuming bad or partially written entry: %m");
return 0;
}
if (r < 0)
return log_error_errno(r, "Failed to get cursor: %m");
r = sd_journal_get_realtime_usec(j, &realtime);
if (r == -EBADMSG) {
corrupted_what = "realtime timestamp";
goto corrupted_skip;
log_debug_errno(r, "Unable to read realtime timestamp of entry, assuming bad or partially written entry: %m");
return 0;
}
if (r < 0)
return log_error_errno(r, "Failed to get realtime timestamp: %m");
r = sd_journal_get_monotonic_usec(j, &monotonic, &journal_boot_id);
if (r == -EBADMSG) {
corrupted_what = "monotonic timestamp";
goto corrupted_skip;
log_debug_errno(r, "Unable to read monotonic timestamp of entry, assuming bad or partially written entry: %m");
return 0;
}
if (r < 0)
return log_error_errno(r, "Failed to get monotonic timestamp: %m");
r = sd_journal_get_seqnum(j, &seqnum, &seqnum_id);
if (r == -EBADMSG) {
corrupted_what = "sequence number";
goto corrupted_skip;
log_debug_errno(r, "Unable to read sequence number of entry, assuming bad or partially written entry: %m");
return 0;
}
if (r < 0)
return log_error_errno(r, "Failed to get seqnum: %m");
@ -1297,32 +1294,29 @@ int journal_entry_to_json(
return r;
}
array = new(sd_json_variant*, hashmap_size(h) * 2);
array = new(sd_json_variant*, hashmap_size(h)*2);
if (!array)
return log_oom();
JsonData *d;
CLEANUP_ARRAY(array, n, sd_json_variant_unref_many);
HASHMAP_FOREACH(d, h) {
assert(sd_json_variant_elements(d->values) > 0);
array[n++] = d->name;
array[n++] = sd_json_variant_ref(d->name);
if (sd_json_variant_elements(d->values) == 1)
array[n++] = sd_json_variant_by_index(d->values, 0);
array[n++] = sd_json_variant_ref(sd_json_variant_by_index(d->values, 0));
else
array[n++] = d->values;
array[n++] = sd_json_variant_ref(d->values);
}
r = sd_json_variant_new_object(ret, array, n);
r = sd_json_variant_new_object(&object, array, n);
if (r < 0)
return log_error_errno(r, "Failed to allocate JSON object: %m");
*ret = TAKE_PTR(object);
return 1;
corrupted_skip:
log_debug_errno(r, "Unable to determine %s of entry, assuming bad or partially written entry: %m", ASSERT_PTR(corrupted_what));
*ret = NULL;
return 0;
}
static int output_json(
@ -1344,9 +1338,9 @@ static int output_json(
return r;
return sd_json_variant_dump(object,
output_mode_to_json_format_flags(mode) |
(FLAGS_SET(flags, OUTPUT_COLOR) ? SD_JSON_FORMAT_COLOR : 0),
f, NULL);
output_mode_to_json_format_flags(mode) |
(FLAGS_SET(flags, OUTPUT_COLOR) ? SD_JSON_FORMAT_COLOR : 0),
f, NULL);
}
static int output_cat_field(
@ -1767,15 +1761,13 @@ int add_matches_for_unit_full(sd_journal *j, MatchUnitFlag flags, const char *un
return r;
}
int add_matches_for_user_unit_full(sd_journal *j, MatchUnitFlag flags, uid_t uid, const char *unit) {
int add_matches_for_user_unit_full(sd_journal *j, MatchUnitFlag flags, const char *unit) {
uid_t uid = getuid();
int r;
assert(j);
assert(unit);
if (uid == UID_INVALID)
uid = getuid();
(void) (
/* Look for messages from the user service itself */
(r = journal_add_match_pair(j, "_SYSTEMD_USER_UNIT", unit)) ||
@ -2012,7 +2004,7 @@ static int set_matches_for_discover_id(
return add_matches_for_unit_full(j, /* flags= */ 0, unit);
if (type == LOG_USER_UNIT_INVOCATION_ID)
return add_matches_for_user_unit_full(j, /* flags= */ 0, UID_INVALID, unit);
return add_matches_for_user_unit_full(j, /* flags= */ 0, unit);
return -EINVAL;
}

View File

@ -57,9 +57,9 @@ int add_matches_for_unit_full(sd_journal *j, MatchUnitFlag flags, const char *un
static inline int add_matches_for_unit(sd_journal *j, const char *unit) {
return add_matches_for_unit_full(j, MATCH_UNIT_ALL, unit);
}
int add_matches_for_user_unit_full(sd_journal *j, MatchUnitFlag flags, uid_t uid, const char *unit);
int add_matches_for_user_unit_full(sd_journal *j, MatchUnitFlag flags, const char *unit);
static inline int add_matches_for_user_unit(sd_journal *j, const char *unit) {
return add_matches_for_user_unit_full(j, MATCH_UNIT_ALL, UID_INVALID, unit);
return add_matches_for_user_unit_full(j, MATCH_UNIT_ALL, unit);
}
int show_journal_by_unit(

View File

@ -4,6 +4,10 @@
#include <sys/mount.h>
#include <unistd.h>
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
#include "log.h"
#include "mkfs-util.h"
#include "mount-util.h"
@ -11,10 +15,12 @@
#include "path-util.h"
#include "process-util.h"
#include "recurse-dir.h"
#include "rm-rf.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "tmpfile-util.h"
#include "utf8.h"
int mkfs_exists(const char *fstype) {
@ -165,6 +171,154 @@ static int do_mcopy(const char *node, const char *root) {
return 0;
}
typedef struct ProtofileData {
FILE *file;
bool has_filename_with_spaces;
const char *tmpdir;
} ProtofileData;
static int protofile_print_item(
RecurseDirEvent event,
const char *path,
int dir_fd,
int inode_fd,
const struct dirent *de,
const struct statx *sx,
void *userdata) {
ProtofileData *data = ASSERT_PTR(userdata);
_cleanup_free_ char *copy = NULL;
int r;
if (event == RECURSE_DIR_LEAVE) {
fputs("$\n", data->file);
return 0;
}
if (!IN_SET(event, RECURSE_DIR_ENTER, RECURSE_DIR_ENTRY))
return RECURSE_DIR_CONTINUE;
char type = S_ISDIR(sx->stx_mode) ? 'd' :
S_ISREG(sx->stx_mode) ? '-' :
S_ISLNK(sx->stx_mode) ? 'l' :
S_ISFIFO(sx->stx_mode) ? 'p' :
S_ISBLK(sx->stx_mode) ? 'b' :
S_ISCHR(sx->stx_mode) ? 'c' : 0;
if (type == 0)
return RECURSE_DIR_CONTINUE;
/* The protofile format does not support spaces in filenames as whitespace is used as a token
* delimiter. To work around this limitation, mkfs.xfs allows escaping whitespace by using the /
* character (which isn't allowed in filenames and as such can be used to escape whitespace). See
* https://lore.kernel.org/linux-xfs/20230222090303.h6tujm7y32gjhgal@andromeda/T/#m8066b3e7d62a080ee7434faac4861d944e64493b
* for more information. */
if (strchr(de->d_name, ' ')) {
copy = strdup(de->d_name);
if (!copy)
return log_oom();
string_replace_char(copy, ' ', '/');
data->has_filename_with_spaces = true;
}
fprintf(data->file, "%s %c%c%c%03o "UID_FMT" "GID_FMT" ",
copy ?: de->d_name,
type,
sx->stx_mode & S_ISUID ? 'u' : '-',
sx->stx_mode & S_ISGID ? 'g' : '-',
(unsigned) (sx->stx_mode & 0777),
sx->stx_uid, sx->stx_gid);
if (S_ISREG(sx->stx_mode)) {
_cleanup_free_ char *p = NULL;
/* While we can escape whitespace in the filename, we cannot escape whitespace in the source
* path, so hack around that by creating a symlink to the path in a temporary directory and
* using the symlink as the source path instead. */
if (strchr(path, ' ')) {
r = tempfn_random_child(data->tmpdir, "mkfs-xfs", &p);
if (r < 0)
return log_error_errno(r, "Failed to generate random child name in %s: %m", data->tmpdir);
if (symlink(path, p) < 0)
return log_error_errno(errno, "Failed to symlink %s to %s: %m", p, path);
}
fputs(p ?: path, data->file);
} else if (S_ISLNK(sx->stx_mode)) {
_cleanup_free_ char *p = NULL;
r = readlinkat_malloc(dir_fd, de->d_name, &p);
if (r < 0)
return log_error_errno(r, "Failed to read symlink %s: %m", path);
/* If we have a symlink to a path with whitespace in it, we're out of luck, as there's no way
* to encode that in the mkfs.xfs protofile format. */
if (strchr(p, ' '))
return log_error_errno(r, "Symlinks to paths containing whitespace are not supported by mkfs.xfs: %m");
fputs(p, data->file);
} else if (S_ISBLK(sx->stx_mode) || S_ISCHR(sx->stx_mode))
fprintf(data->file, "%" PRIu32 " %" PRIu32, sx->stx_rdev_major, sx->stx_rdev_minor);
fputc('\n', data->file);
return RECURSE_DIR_CONTINUE;
}
static int make_protofile(const char *root, char **ret_path, bool *ret_has_filename_with_spaces, char **ret_tmpdir) {
_cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_(unlink_and_freep) char *p = NULL;
struct ProtofileData data = {};
const char *vt;
int r;
assert(ret_path);
assert(ret_has_filename_with_spaces);
assert(ret_tmpdir);
r = var_tmp_dir(&vt);
if (r < 0)
return log_error_errno(r, "Failed to get persistent temporary directory: %m");
r = fopen_temporary_child(vt, &f, &p);
if (r < 0)
return log_error_errno(r, "Failed to open temporary file: %m");
/* Explicitly use /tmp here because this directory cannot have spaces its path. */
r = mkdtemp_malloc("/tmp/systemd-mkfs-XXXXXX", &tmpdir);
if (r < 0)
return log_error_errno(r, "Failed to create temporary directory: %m");
data.file = f;
data.tmpdir = tmpdir;
fputs("/\n"
"0 0\n"
"d--755 0 0\n", f);
r = recurse_dir_at(AT_FDCWD, root, STATX_TYPE|STATX_MODE|STATX_UID|STATX_GID, UINT_MAX,
RECURSE_DIR_SORT, protofile_print_item, &data);
if (r < 0)
return log_error_errno(r, "Failed to recurse through %s: %m", root);
fputs("$\n", f);
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to flush %s: %m", p);
*ret_path = TAKE_PTR(p);
*ret_has_filename_with_spaces = data.has_filename_with_spaces;
*ret_tmpdir = TAKE_PTR(tmpdir);
return 0;
}
int make_filesystem(
const char *node,
const char *fstype,
@ -179,6 +333,8 @@ int make_filesystem(
_cleanup_free_ char *mkfs = NULL, *mangled_label = NULL;
_cleanup_strv_free_ char **argv = NULL, **env = NULL;
_cleanup_(rm_rf_physical_and_freep) char *protofile_tmpdir = NULL;
_cleanup_(unlink_and_freep) char *protofile = NULL;
char vol_id[CONST_MAX(SD_ID128_UUID_STRING_MAX, 8U + 1U)] = {};
int stdio_fds[3] = { -EBADF, STDERR_FILENO, STDERR_FILENO};
ForkFlags fork_flags = FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|
@ -404,8 +560,26 @@ int make_filesystem(
if (!FLAGS_SET(flags, MKFS_DISCARD) && strv_extend(&argv, "-K") < 0)
return log_oom();
if (root && strv_extend_many(&argv, "-p", root) < 0)
return log_oom();
if (root) {
bool has_filename_with_spaces = false;
_cleanup_free_ char *protofile_with_opt = NULL;
r = make_protofile(root, &protofile, &has_filename_with_spaces, &protofile_tmpdir);
if (r < 0)
return r;
/* Gross hack to make mkfs.xfs interpret slashes as spaces so we can encode filenames
* with spaces in the protofile format. */
if (has_filename_with_spaces)
protofile_with_opt = strjoin("slashes_are_spaces=1,", protofile);
else
protofile_with_opt = strdup(protofile);
if (!protofile_with_opt)
return -ENOMEM;
if (strv_extend_many(&argv, "-p", protofile_with_opt) < 0)
return log_oom();
}
if (sector_size > 0) {
if (strv_extend(&argv, "-s") < 0)

View File

@ -26,7 +26,6 @@ typedef enum DnssecMode DnssecMode;
typedef enum Fido2EnrollFlags Fido2EnrollFlags;
typedef enum KeySourceType KeySourceType;
typedef enum LabelFixFlags LabelFixFlags;
typedef enum MatchUnitFlag MatchUnitFlag;
typedef enum MountInNamespaceFlags MountInNamespaceFlags;
typedef enum NamePolicy NamePolicy;
typedef enum OutputFlags OutputFlags;

View File

@ -274,29 +274,6 @@ _noreturn_ void log_test_failed_internal(const char *file, int line, const char
})
#endif
#ifdef __COVERITY__
# define ASSERT_OK_NE(expr1, expr2) \
({ \
typeof(expr1) _expr1 = (expr1); \
typeof(expr2) _expr2 = (expr2); \
__coverity_check__(_expr1 != _expr2); \
_expr1; \
})
#else
# define ASSERT_OK_NE(expr1, expr2) \
({ \
typeof(expr1) _expr1 = (expr1); \
typeof(expr2) _expr2 = (expr2); \
if (_expr1 < 0) \
log_test_failed("Expected \"%s\" to succeed, but got error: %"PRIiMAX"/%s", \
#expr1, (intmax_t) _expr1, ERRNO_NAME(_expr1)); \
if (_expr1 == _expr2) \
log_test_failed("Expected \"%s != %s\", got %"PRIiMAX" != %"PRIiMAX, \
#expr1, #expr2, (intmax_t) _expr1, (intmax_t) _expr2); \
_expr1; \
})
#endif
/* For functions that return a boolean on success and set errno on failure. */
#ifdef __COVERITY__
# define ASSERT_OK_ERRNO(expr) \

View File

@ -7,8 +7,6 @@ static SD_VARLINK_DEFINE_METHOD_FULL(
SD_VARLINK_REQUIRES_MORE,
SD_VARLINK_FIELD_COMMENT("Show messages for the specified systemd units (e.g. ['foo.service'])."),
SD_VARLINK_DEFINE_INPUT(units, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
SD_VARLINK_FIELD_COMMENT("UID to match user units for"),
SD_VARLINK_DEFINE_INPUT(uid, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Show messages for the specified user units (e.g. ['foo.service'])."),
SD_VARLINK_DEFINE_INPUT(userUnits, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
SD_VARLINK_FIELD_COMMENT("If specified, shows the log data of the specified namespace, otherwise the default namespace."),
@ -18,9 +16,8 @@ static SD_VARLINK_DEFINE_METHOD_FULL(
SD_VARLINK_FIELD_COMMENT("Maximum number of entries to return. Defaults to 100, capped at 10000."),
SD_VARLINK_DEFINE_INPUT(limit, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The journal entry in flat JSON format, matching journalctl --output=json."),
SD_VARLINK_DEFINE_OUTPUT(entry, SD_VARLINK_OBJECT, 0));
SD_VARLINK_DEFINE_OUTPUT(entry, SD_VARLINK_OBJECT, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_ERROR(NoMatches);
static SD_VARLINK_DEFINE_ERROR(NoEntries);
SD_VARLINK_DEFINE_INTERFACE(
@ -29,7 +26,5 @@ SD_VARLINK_DEFINE_INTERFACE(
SD_VARLINK_INTERFACE_COMMENT("Journal log read APIs"),
SD_VARLINK_SYMBOL_COMMENT("Retrieve journal log entries, optionally filtered by unit, priority, etc."),
&vl_method_GetEntries,
SD_VARLINK_SYMBOL_COMMENT("No matches found for specified unit patterns"),
&vl_error_NoMatches,
SD_VARLINK_SYMBOL_COMMENT("No journal entries matched the specified filters."),
&vl_error_NoEntries);

View File

@ -64,11 +64,7 @@
<allow send_destination="org.freedesktop.sysupdate1"
send_interface="org.freedesktop.sysupdate1.Target"
send_member="Acquire"/>
<allow send_destination="org.freedesktop.sysupdate1"
send_interface="org.freedesktop.sysupdate1.Target"
send_member="Install"/>
send_member="Update"/>
<allow send_destination="org.freedesktop.sysupdate1"
send_interface="org.freedesktop.sysupdate1.Target"

View File

@ -100,8 +100,7 @@ typedef enum JobType {
JOB_LIST,
JOB_DESCRIBE,
JOB_CHECK_NEW,
JOB_ACQUIRE,
JOB_INSTALL,
JOB_UPDATE,
JOB_VACUUM,
JOB_DESCRIBE_FEATURE,
_JOB_TYPE_MAX,
@ -122,7 +121,7 @@ struct Job {
JobType type;
bool offline;
char *version; /* Passed into sysupdate for JOB_DESCRIBE, JOB_ACQUIRE and JOB_INSTALL */
char *version; /* Passed into sysupdate for JOB_DESCRIBE and JOB_UPDATE */
char *feature; /* Passed into sysupdate for JOB_DESCRIBE_FEATURE */
unsigned progress_percent;
@ -154,8 +153,7 @@ static const char* const job_type_table[_JOB_TYPE_MAX] = {
[JOB_LIST] = "list",
[JOB_DESCRIBE] = "describe",
[JOB_CHECK_NEW] = "check-new",
[JOB_ACQUIRE] = "acquire",
[JOB_INSTALL] = "install",
[JOB_UPDATE] = "update",
[JOB_VACUUM] = "vacuum",
[JOB_DESCRIBE_FEATURE] = "describe-feature",
};
@ -222,11 +220,6 @@ static int job_new(JobType type, Target *t, sd_bus_message *msg, JobComplete com
return 0;
}
/* Is Job in the set of jobs which require Target.busy to be set so they run exclusively? */
static bool job_requires_busy(Job *j) {
return IN_SET(j->type, JOB_ACQUIRE, JOB_INSTALL, JOB_VACUUM);
}
static int job_parse_child_output(int _fd, sd_json_variant **ret) {
_cleanup_close_ int fd = ASSERT_FD(_fd); /* Take ownership of the passed fd */
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
@ -339,7 +332,7 @@ static int job_on_exit(sd_event_source *s, const siginfo_t *si, void *userdata)
assert(s);
assert(si);
if (job_requires_busy(j)) {
if (IN_SET(j->type, JOB_UPDATE, JOB_VACUUM)) {
assert(j->target->busy);
j->target->busy = false;
}
@ -437,7 +430,7 @@ static int job_start(Job *j) {
assert(j);
if (job_requires_busy(j) && j->target->busy)
if (IN_SET(j->type, JOB_UPDATE, JOB_VACUUM) && j->target->busy)
return log_notice_errno(SYNTHETIC_ERRNO(EBUSY), "Target %s busy, ignoring job.", j->target->name);
stdout_fd = memfd_new("sysupdate-stdout");
@ -460,8 +453,8 @@ static int job_start(Job *j) {
NULL, /* maybe --verify=no */
NULL, /* maybe --component=, --root=, or --image= */
NULL, /* maybe --offline */
NULL, /* list, check-new, acquire, update, vacuum, features */
NULL, /* maybe version (for list, acquire, update), maybe feature (features) */
NULL, /* list, check-new, update, vacuum, features */
NULL, /* maybe version (for list, update), maybe feature (features) */
NULL
};
size_t k = 2;
@ -486,7 +479,7 @@ static int job_start(Job *j) {
if (target_arg)
cmd[k++] = target_arg;
if (j->offline || j->type == JOB_INSTALL) /* install is implemented as `update --offline` */
if (j->offline)
cmd[k++] = "--offline";
switch (j->type) {
@ -504,13 +497,8 @@ static int job_start(Job *j) {
cmd[k++] = "check-new";
break;
case JOB_ACQUIRE:
cmd[k++] = "acquire";
cmd[k++] = empty_to_null(j->version);
break;
case JOB_INSTALL:
cmd[k++] = "update"; /* install is implemented as `update --offline` */
case JOB_UPDATE:
cmd[k++] = "update";
cmd[k++] = empty_to_null(j->version);
break;
@ -559,7 +547,7 @@ static int job_start(Job *j) {
j->stdout_fd = TAKE_FD(stdout_fd);
if (job_requires_busy(j))
if (IN_SET(j->type, JOB_UPDATE, JOB_VACUUM))
j->target->busy = true;
return 0;
@ -593,8 +581,7 @@ static int job_method_cancel(sd_bus_message *msg, void *userdata, sd_bus_error *
action = "org.freedesktop.sysupdate1.check";
break;
case JOB_ACQUIRE:
case JOB_INSTALL:
case JOB_UPDATE:
if (j->version)
action = "org.freedesktop.sysupdate1.update-to-version";
else
@ -1078,91 +1065,7 @@ static int target_method_check_new(sd_bus_message *msg, void *userdata, sd_bus_e
return 1;
}
static int target_method_acquire_finished_early(
sd_bus_message *msg,
const Job *j,
sd_json_variant *json,
sd_bus_error *error) {
/* Called when job finishes w/ a successful exit code, but before any work begins.
* This happens when there is no candidate (i.e. we're already up-to-date), or
* specified update is already acquired. */
return sd_bus_error_setf(error, BUS_ERROR_NO_UPDATE_CANDIDATE,
"Job exited successfully with no work to do, assume already acquired");
}
static int target_method_acquire_detach(sd_bus_message *msg, const Job *j) {
int r;
assert(msg);
assert(j);
r = sd_bus_reply_method_return(msg, "sto", j->version, j->id, j->object_path);
if (r < 0)
return bus_log_parse_error(r);
return 0;
}
static int target_method_acquire(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
Target *t = ASSERT_PTR(userdata);
_cleanup_(job_freep) Job *j = NULL;
const char *version, *action;
uint64_t flags;
int r;
assert(msg);
r = sd_bus_message_read(msg, "st", &version, &flags);
if (r < 0)
return r;
if (flags != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be 0");
/* We dont have a separate polkit action for acquire/install as they are both effectively (part of)
* an update anyway. */
if (isempty(version))
action = "org.freedesktop.sysupdate1.update";
else
action = "org.freedesktop.sysupdate1.update-to-version";
const char *details[] = {
"class", target_class_to_string(t->class),
"name", t->name,
"version", version,
NULL
};
r = bus_verify_polkit_async(
msg,
action,
details,
&t->manager->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
r = job_new(JOB_ACQUIRE, t, msg, target_method_acquire_finished_early, &j);
if (r < 0)
return r;
j->detach_cb = target_method_acquire_detach;
j->version = strdup(version);
if (!j->version)
return -ENOMEM;
r = job_start(j);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to start job: %m");
TAKE_PTR(j);
return 1;
}
static int target_method_install_finished_early(
static int target_method_update_finished_early(
sd_bus_message *msg,
const Job *j,
sd_json_variant *json,
@ -1172,10 +1075,10 @@ static int target_method_install_finished_early(
* This happens when there is no candidate (i.e. we're already up-to-date), or
* specified update is already installed. */
return sd_bus_error_setf(error, BUS_ERROR_NO_UPDATE_CANDIDATE,
"Job exited successfully with no work to do, assume already installed");
"Job exited successfully with no work to do, assume already updated");
}
static int target_method_install_detach(sd_bus_message *msg, const Job *j) {
static int target_method_update_detach(sd_bus_message *msg, const Job *j) {
int r;
assert(msg);
@ -1188,7 +1091,7 @@ static int target_method_install_detach(sd_bus_message *msg, const Job *j) {
return 0;
}
static int target_method_install(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
static int target_method_update(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
Target *t = ASSERT_PTR(userdata);
_cleanup_(job_freep) Job *j = NULL;
const char *version, *action;
@ -1204,8 +1107,6 @@ static int target_method_install(sd_bus_message *msg, void *userdata, sd_bus_err
if (flags != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be 0");
/* We dont have a separate polkit action for acquire/install as they are both effectively (part of)
* an update anyway. */
if (isempty(version))
action = "org.freedesktop.sysupdate1.update";
else
@ -1229,10 +1130,10 @@ static int target_method_install(sd_bus_message *msg, void *userdata, sd_bus_err
if (r == 0)
return 1; /* Will call us back */
r = job_new(JOB_INSTALL, t, msg, target_method_install_finished_early, &j);
r = job_new(JOB_UPDATE, t, msg, target_method_update_finished_early, &j);
if (r < 0)
return r;
j->detach_cb = target_method_install_detach;
j->detach_cb = target_method_update_detach;
j->version = strdup(version);
if (!j->version)
@ -1679,16 +1580,10 @@ static const sd_bus_vtable target_vtable[] = {
target_method_check_new,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("Acquire",
SD_BUS_METHOD_WITH_ARGS("Update",
SD_BUS_ARGS("s", new_version, "t", flags),
SD_BUS_RESULT("s", new_version, "t", job_id, "o", job_path),
target_method_acquire,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("Install",
SD_BUS_ARGS("s", new_version, "t", flags),
SD_BUS_RESULT("s", new_version, "t", job_id, "o", job_path),
target_method_install,
target_method_update,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("Vacuum",

View File

@ -72,9 +72,6 @@ typedef struct Operation {
sd_event_source *job_interrupt_source;
sd_bus_slot *job_properties_slot;
sd_bus_slot *job_finished_slot;
/* Only used for Acquire()/Install() operations: */
char *acquired_version;
} Operation;
static Operation* operation_free(Operation *p) {
@ -89,7 +86,6 @@ static Operation* operation_free(Operation *p) {
assert_se(sd_event_exit(p->event, 0) >= 0);
free(p->job_path);
free(p->acquired_version);
sd_event_source_disable_unref(p->job_interrupt_source);
sd_bus_slot_unref(p->job_properties_slot);
@ -338,8 +334,6 @@ typedef struct DescribeParams {
bool newest;
bool available;
bool installed;
bool partial;
bool pending;
bool obsolete;
bool protected;
bool incomplete;
@ -375,8 +369,6 @@ static int parse_describe(sd_bus_message *reply, Version *ret) {
{ "newest", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, newest), 0 },
{ "available", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, available), 0 },
{ "installed", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, installed), 0 },
{ "partial", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, partial), 0 },
{ "pending", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, pending), 0 },
{ "obsolete", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, obsolete), 0 },
{ "protected", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, protected), 0 },
{ "incomplete", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, incomplete), 0 },
@ -394,8 +386,6 @@ static int parse_describe(sd_bus_message *reply, Version *ret) {
SET_FLAG(p.v.flags, UPDATE_NEWEST, p.newest);
SET_FLAG(p.v.flags, UPDATE_AVAILABLE, p.available);
SET_FLAG(p.v.flags, UPDATE_INSTALLED, p.installed);
SET_FLAG(p.v.flags, UPDATE_PARTIAL, p.partial);
SET_FLAG(p.v.flags, UPDATE_PENDING, p.pending);
SET_FLAG(p.v.flags, UPDATE_OBSOLETE, p.obsolete);
SET_FLAG(p.v.flags, UPDATE_PROTECTED, p.protected);
SET_FLAG(p.v.flags, UPDATE_INCOMPLETE, p.incomplete);
@ -814,7 +804,6 @@ static int verb_check(int argc, char **argv, void *userdata) {
}
#define UPDATE_PROGRESS_FAILED INT_MIN
#define UPDATE_PROGRESS_ACQUIRED (INT_MAX - 1)
#define UPDATE_PROGRESS_DONE INT_MAX
/* Make sure it doesn't overlap w/ errno values */
assert_cc(UPDATE_PROGRESS_FAILED < -ERRNO_MAX);
@ -864,10 +853,6 @@ static int update_render_progress(sd_event_source *source, void *userdata) {
clear_progress_bar_unbuffered(target);
fprintf(stderr, "%s: %s %s\n", target, RED_CROSS_MARK(), STRERROR(progress));
total += 100;
} else if (progress == UPDATE_PROGRESS_ACQUIRED) {
clear_progress_bar_unbuffered(target);
fprintf(stderr, "%s: %s Installing\n", target, glyph(GLYPH_DOWNLOAD));
total += 100;
} else if (progress == UPDATE_PROGRESS_DONE) {
clear_progress_bar_unbuffered(target);
fprintf(stderr, "%s: %s Done\n", target, GREEN_CHECK_MARK());
@ -935,60 +920,7 @@ static int update_properties_changed(sd_bus_message *m, void *userdata, sd_bus_e
return 0;
}
static int update_install_started(sd_bus_message *reply, void *userdata, sd_bus_error *ret_error) {
_cleanup_(operation_freep) Operation *op = ASSERT_PTR(userdata);
OrderedHashmap *map = ASSERT_PTR(op->userdata);
const sd_bus_error *e;
const char *job_path;
int r;
assert(reply);
e = sd_bus_message_get_error(reply);
if (e) {
r = -sd_bus_error_get_errno(e);
r = ordered_hashmap_replace(map, op->target_id, INT_TO_PTR(r));
if (r < 0)
log_debug_errno(r, "Failed to update hashmap: %m");
return 0;
}
r = sd_bus_message_read(reply, "sto", NULL, &op->job_id, &job_path);
if (r < 0)
return bus_log_parse_error(r);
r = free_and_strdup_warn(&op->job_path, job_path);
if (r < 0)
return r;
/* Update this job in the hashmap. */
r = ordered_hashmap_replace(map, op->target_id, INT_TO_PTR(UPDATE_PROGRESS_ACQUIRED));
if (r < 0)
log_debug_errno(r, "Failed to update hashmap: %m");
/* Register for progress notifications for this Install() D-Bus call; previously
* op->job_properties_slot was registered for progress notifications for the Acquire() D-Bus call. */
sd_bus_slot_unref(TAKE_PTR(op->job_properties_slot));
r = sd_bus_match_signal_async(
op->bus,
&op->job_properties_slot,
bus_sysupdate_mgr->destination,
job_path,
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
update_properties_changed,
NULL,
op);
if (r < 0)
return log_bus_error(r, NULL, op->target_id, "listen for PropertiesChanged");
TAKE_PTR(op); /* update_install_finished/update_interrupted take ownership of the data */
return 0;
}
static int update_install_finished(sd_bus_message *m, void *userdata, sd_bus_error *error) {
static int update_finished(sd_bus_message *m, void *userdata, sd_bus_error *error) {
_cleanup_(operation_freep) Operation *op = ASSERT_PTR(userdata);
OrderedHashmap *map = ASSERT_PTR(op->userdata);
uint64_t id;
@ -1019,64 +951,6 @@ static int update_install_finished(sd_bus_message *m, void *userdata, sd_bus_err
return 0;
}
static int update_acquire_finished(sd_bus_message *m, void *userdata, sd_bus_error *error) {
_cleanup_(operation_freep) Operation *op = ASSERT_PTR(userdata);
OrderedHashmap *map = ASSERT_PTR(op->userdata);
uint64_t id;
int r, status;
assert(m);
r = sd_bus_message_read(m, "toi", &id, NULL, &status);
if (r < 0) {
bus_log_parse_error_debug(r);
return 0;
}
if (id != op->job_id) {
TAKE_PTR(op);
return 0;
}
if (status == 0) /* success */
status = UPDATE_PROGRESS_ACQUIRED;
else if (status > 0) /* exit status without errno */
status = UPDATE_PROGRESS_FAILED; /* i.e. EXIT_FAILURE */
/* else errno */
r = ordered_hashmap_replace(map, op->target_id, INT_TO_PTR(status));
if (r < 0)
log_debug_errno(r, "Failed to update hashmap: %m");
/* Renew the JobRemoved notification for the Install() call instead. */
sd_bus_slot_unref(op->job_finished_slot);
r = bus_match_signal_async(
op->bus, &op->job_finished_slot, bus_sysupdate_mgr, "JobRemoved", update_install_finished, NULL, op);
if (r < 0)
return log_bus_error(r, NULL, op->target_id, "listen for JobRemoved");
/* With the Acquire() call finished, immediately call Install() to deploy the downloaded update.
* This reuses the same Operation struct so the progress reporting continues to be done in the same
* slot in the terminal. */
r = sd_bus_call_method_async(
op->bus,
NULL,
bus_sysupdate_mgr->destination,
op->target_path,
SYSUPDATE_TARGET_INTERFACE,
"Install",
update_install_started,
op,
"st",
op->acquired_version,
0LU);
if (r < 0)
return log_bus_error(r, NULL, op->target_id, "call Install");
TAKE_PTR(op);
return 0;
}
static int update_interrupted(sd_event_source *source, void *userdata) {
/* Since the event loop is exiting, we will never receive the JobRemoved
* signal. So, we must free the userdata here. */
@ -1085,8 +959,6 @@ static int update_interrupted(sd_event_source *source, void *userdata) {
OrderedHashmap *map = ASSERT_PTR(op->userdata);
int r;
/* This call should work regardless of whether were cancelling the Acquire() call or the Install()
* call. */
r = sd_bus_call_method(op->bus,
bus_sysupdate_mgr->destination,
op->job_path,
@ -1105,7 +977,7 @@ static int update_interrupted(sd_event_source *source, void *userdata) {
return 0;
}
static int update_acquire_started(sd_bus_message *reply, void *userdata, sd_bus_error *ret_error) {
static int update_started(sd_bus_message *reply, void *userdata, sd_bus_error *ret_error) {
_cleanup_(operation_freep) Operation *op = ASSERT_PTR(userdata);
OrderedHashmap *map = ASSERT_PTR(op->userdata);
const sd_bus_error *e;
@ -1136,12 +1008,6 @@ static int update_acquire_started(sd_bus_message *reply, void *userdata, sd_bus_
op->job_path = strdup(job_path);
if (!op->job_path)
return log_oom();
/* Store the version for the subsequent Install() call */
op->acquired_version = strdup(new_version);
if (!op->acquired_version)
return log_oom();
if (isempty(new_version))
new_version = "latest";
@ -1181,7 +1047,7 @@ static int update_acquire_started(sd_bus_message *reply, void *userdata, sd_bus_
if (r < 0)
return log_bus_error(r, NULL, op->target_id, "listen for PropertiesChanged");
TAKE_PTR(op); /* update_acquire_finished/update_interrupted take ownership of the data */
TAKE_PTR(op); /* update_finished/update_interrupted take ownership of the data */
return 0;
}
@ -1228,7 +1094,7 @@ static int do_update(sd_bus *bus, char **targets) {
/* Sign up for notification when the associated job finishes */
r = bus_match_signal_async(
op->bus, &op->job_finished_slot, bus_sysupdate_mgr, "JobRemoved", update_acquire_finished, NULL, op);
op->bus, &op->job_finished_slot, bus_sysupdate_mgr, "JobRemoved", update_finished, NULL, op);
if (r < 0)
return log_bus_error(r, NULL, op->target_id, "listen for JobRemoved");
@ -1238,14 +1104,14 @@ static int do_update(sd_bus *bus, char **targets) {
bus_sysupdate_mgr->destination,
target_paths[i],
SYSUPDATE_TARGET_INTERFACE,
"Acquire",
update_acquire_started,
"Update",
update_started,
op,
"st",
versions[i],
0LU);
if (r < 0)
return log_bus_error(r, NULL, targets[i], "call Acquire");
return log_bus_error(r, NULL, targets[i], "call Update");
TAKE_PTR(op);
remaining++;

View File

@ -1105,7 +1105,6 @@ TEST(unit_properties) {
"ConditionPathIsMountPoint=|foo",
"ConditionPathIsReadWrite=|foo",
"ConditionPathIsEncrypted=|foo",
"ConditionPathIsSocket=|foo",
"ConditionDirectoryNotEmpty=|foo",
"ConditionFileNotEmpty=|foo",
"ConditionFileIsExecutable=|foo",
@ -1140,7 +1139,6 @@ TEST(unit_properties) {
"AssertPathIsMountPoint=|foo",
"AssertPathIsReadWrite=|foo",
"AssertPathIsEncrypted=|foo",
"AssertPathIsSocket=|foo",
"AssertDirectoryNotEmpty=|foo",
"AssertFileNotEmpty=|foo",
"AssertFileIsExecutable=|foo",

View File

@ -110,16 +110,6 @@ TEST(condition_test_path) {
ASSERT_OK_ZERO(condition_test(condition, environ));
condition_free(condition);
if (access("/run/dbus/system_bus_socket", F_OK) >= 0) {
ASSERT_NOT_NULL((condition = condition_new(CONDITION_PATH_IS_SOCKET, "/run/dbus/system_bus_socket", false, false)));
ASSERT_OK_POSITIVE(condition_test(condition, environ));
condition_free(condition);
}
ASSERT_NOT_NULL((condition = condition_new(CONDITION_PATH_IS_SOCKET, "/sys", false, false)));
ASSERT_OK_ZERO(condition_test(condition, environ));
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_PATH_IS_SYMBOLIC_LINK, "/dev/stdout", false, false)));
ASSERT_OK_POSITIVE(condition_test(condition, environ));
condition_free(condition);
@ -197,15 +187,15 @@ TEST(condition_test_ac_power) {
Condition *condition;
ASSERT_NOT_NULL((condition = condition_new(CONDITION_AC_POWER, "true", false, false)));
ASSERT_OK_EQ(condition_test(condition, environ), on_ac_power());
assert_se(condition_test(condition, environ) == on_ac_power());
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_AC_POWER, "false", false, false)));
ASSERT_OK_NE(condition_test(condition, environ), on_ac_power());
assert_se(condition_test(condition, environ) != on_ac_power());
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_AC_POWER, "false", false, true)));
ASSERT_OK_EQ(condition_test(condition, environ), on_ac_power());
assert_se(condition_test(condition, environ) == on_ac_power());
condition_free(condition);
}
@ -724,8 +714,8 @@ TEST(condition_test_credential) {
_cleanup_free_ char *d1 = NULL, *d2 = NULL, *j = NULL;
Condition *condition;
ASSERT_OK(free_and_strdup(&d1, getenv("CREDENTIALS_DIRECTORY")));
ASSERT_OK(free_and_strdup(&d2, getenv("ENCRYPTED_CREDENTIALS_DIRECTORY")));
assert_se(free_and_strdup(&d1, getenv("CREDENTIALS_DIRECTORY")) >= 0);
assert_se(free_and_strdup(&d2, getenv("ENCRYPTED_CREDENTIALS_DIRECTORY")) >= 0);
ASSERT_OK_ERRNO(unsetenv("CREDENTIALS_DIRECTORY"));
ASSERT_OK_ERRNO(unsetenv("ENCRYPTED_CREDENTIALS_DIRECTORY"));
@ -739,8 +729,8 @@ TEST(condition_test_credential) {
ASSERT_OK_ZERO(condition_test(condition, environ));
condition_free(condition);
ASSERT_OK(mkdtemp_malloc(NULL, &n1));
ASSERT_OK(mkdtemp_malloc(NULL, &n2));
assert_se(mkdtemp_malloc(NULL, &n1) >= 0);
assert_se(mkdtemp_malloc(NULL, &n2) >= 0);
ASSERT_OK_ERRNO(setenv("CREDENTIALS_DIRECTORY", n1, /* overwrite= */ true));
ASSERT_OK_ERRNO(setenv("ENCRYPTED_CREDENTIALS_DIRECTORY", n2, /* overwrite= */ true));
@ -750,20 +740,20 @@ TEST(condition_test_credential) {
condition_free(condition);
ASSERT_NOT_NULL((j = path_join(n1, "existing")));
ASSERT_OK(touch(j));
assert_se(touch(j) >= 0);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_CREDENTIAL, "existing", /* trigger= */ false, /* negate= */ false)));
ASSERT_OK_POSITIVE(condition_test(condition, environ));
condition_free(condition);
free(j);
ASSERT_NOT_NULL((j = path_join(n2, "existing-encrypted")));
ASSERT_OK(touch(j));
assert_se(touch(j) >= 0);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_CREDENTIAL, "existing-encrypted", /* trigger= */ false, /* negate= */ false)));
ASSERT_OK_POSITIVE(condition_test(condition, environ));
condition_free(condition);
ASSERT_OK(set_unset_env("CREDENTIALS_DIRECTORY", d1, /* overwrite= */ true));
ASSERT_OK(set_unset_env("ENCRYPTED_CREDENTIALS_DIRECTORY", d2, /* overwrite= */ true));
assert_se(set_unset_env("CREDENTIALS_DIRECTORY", d1, /* overwrite= */ true) >= 0);
assert_se(set_unset_env("ENCRYPTED_CREDENTIALS_DIRECTORY", d2, /* overwrite= */ true) >= 0);
}
#if defined(__i386__) || defined(__x86_64__)
@ -792,36 +782,36 @@ TEST(condition_test_security) {
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_SECURITY, "selinux", false, true)));
ASSERT_OK_NE(condition_test(condition, environ), mac_selinux_use());
assert_se(condition_test(condition, environ) != mac_selinux_use());
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_SECURITY, "apparmor", false, false)));
ASSERT_OK_EQ(condition_test(condition, environ), mac_apparmor_use());
assert_se(condition_test(condition, environ) == mac_apparmor_use());
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_SECURITY, "tomoyo", false, false)));
ASSERT_OK_EQ(condition_test(condition, environ), mac_tomoyo_use());
assert_se(condition_test(condition, environ) == mac_tomoyo_use());
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_SECURITY, "ima", false, false)));
ASSERT_OK_EQ(condition_test(condition, environ), use_ima());
assert_se(condition_test(condition, environ) == use_ima());
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_SECURITY, "smack", false, false)));
ASSERT_OK_EQ(condition_test(condition, environ), mac_smack_use());
assert_se(condition_test(condition, environ) == mac_smack_use());
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_SECURITY, "audit", false, false)));
ASSERT_OK_EQ(condition_test(condition, environ), use_audit());
assert_se(condition_test(condition, environ) == use_audit());
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_SECURITY, "uefi-secureboot", false, false)));
ASSERT_OK_EQ(condition_test(condition, environ), is_efi_secure_boot());
assert_se(condition_test(condition, environ) == is_efi_secure_boot());
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_SECURITY, "cvm", false, false)));
ASSERT_OK_EQ(condition_test(condition, environ),
(detect_confidential_virtualization() != CONFIDENTIAL_VIRTUALIZATION_NONE));
assert_se(condition_test(condition, environ) ==
(detect_confidential_virtualization() != CONFIDENTIAL_VIRTUALIZATION_NONE));
condition_free(condition);
}
@ -854,19 +844,19 @@ TEST(condition_test_virtualization) {
ASSERT_NOT_NULL((condition = condition_new(CONDITION_VIRTUALIZATION, "container", false, false)));
r = condition_test(condition, environ);
log_info("ConditionVirtualization=container → %i", r);
ASSERT_OK_EQ(r, !!detect_container());
assert_se(r == !!detect_container());
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_VIRTUALIZATION, "vm", false, false)));
r = condition_test(condition, environ);
log_info("ConditionVirtualization=vm → %i", r);
ASSERT_OK_EQ(r, (detect_vm() && !detect_container()));
assert_se(r == (detect_vm() && !detect_container()));
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_VIRTUALIZATION, "private-users", false, false)));
r = condition_test(condition, environ);
log_info("ConditionVirtualization=private-users → %i", r);
ASSERT_OK_EQ(r, !!running_in_userns());
assert_se(r == !!running_in_userns());
condition_free(condition);
NULSTR_FOREACH(virt,
@ -973,12 +963,13 @@ TEST(condition_test_group) {
ASSERT_OK_POSITIVE(r);
condition_free(condition);
ngroups_max = ASSERT_OK_ERRNO(sysconf(_SC_NGROUPS_MAX));
ASSERT_GT(ngroups_max, 0);
ngroups_max = sysconf(_SC_NGROUPS_MAX);
assert_se(ngroups_max > 0);
gids = newa(gid_t, ngroups_max);
ngroups = ASSERT_OK_ERRNO(getgroups(ngroups_max, gids));
ngroups = getgroups(ngroups_max, gids);
assert_se(ngroups >= 0);
max_gid = getgid();
for (i = 0; i < ngroups; i++) {
@ -1028,12 +1019,15 @@ TEST(condition_test_group) {
static void test_condition_test_cpus_one(const char *s, bool result) {
Condition *condition;
int r;
log_debug("%s=%s", condition_type_to_string(CONDITION_CPUS), s);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_CPUS, s, false, false)));
ASSERT_OK_EQ(condition_test(condition, environ), result);
r = condition_test(condition, environ);
assert_se(r >= 0);
assert_se(r == result);
condition_free(condition);
}
@ -1041,7 +1035,8 @@ TEST(condition_test_cpus) {
_cleanup_free_ char *t = NULL;
int cpus;
cpus = ASSERT_OK(cpus_in_affinity_mask());
cpus = cpus_in_affinity_mask();
assert_se(cpus >= 0);
test_condition_test_cpus_one("> 0", true);
test_condition_test_cpus_one(">= 0", true);
@ -1057,39 +1052,42 @@ TEST(condition_test_cpus) {
test_condition_test_cpus_one("!= 100000", true);
test_condition_test_cpus_one("<= 100000", true);
ASSERT_OK(asprintf(&t, "= %i", cpus));
assert_se(asprintf(&t, "= %i", cpus) >= 0);
test_condition_test_cpus_one(t, true);
t = mfree(t);
ASSERT_OK(asprintf(&t, "<= %i", cpus));
assert_se(asprintf(&t, "<= %i", cpus) >= 0);
test_condition_test_cpus_one(t, true);
t = mfree(t);
ASSERT_OK(asprintf(&t, ">= %i", cpus));
assert_se(asprintf(&t, ">= %i", cpus) >= 0);
test_condition_test_cpus_one(t, true);
t = mfree(t);
ASSERT_OK(asprintf(&t, "!= %i", cpus));
assert_se(asprintf(&t, "!= %i", cpus) >= 0);
test_condition_test_cpus_one(t, false);
t = mfree(t);
ASSERT_OK(asprintf(&t, "< %i", cpus));
assert_se(asprintf(&t, "< %i", cpus) >= 0);
test_condition_test_cpus_one(t, false);
t = mfree(t);
ASSERT_OK(asprintf(&t, "> %i", cpus));
assert_se(asprintf(&t, "> %i", cpus) >= 0);
test_condition_test_cpus_one(t, false);
t = mfree(t);
}
static void test_condition_test_memory_one(const char *s, bool result) {
Condition *condition;
int r;
log_debug("%s=%s", condition_type_to_string(CONDITION_MEMORY), s);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_MEMORY, s, false, false)));
ASSERT_OK_EQ(condition_test(condition, environ), result);
r = condition_test(condition, environ);
assert_se(r >= 0);
assert_se(r == result);
condition_free(condition);
}
@ -1134,39 +1132,42 @@ TEST(condition_test_memory) {
test_condition_test_memory_one("!= 100 T 1 G", true);
test_condition_test_memory_one("<= 100 T 1 G", true);
ASSERT_OK(asprintf(&t, "= %" PRIu64, memory));
assert_se(asprintf(&t, "= %" PRIu64, memory) >= 0);
test_condition_test_memory_one(t, true);
t = mfree(t);
ASSERT_OK(asprintf(&t, "<= %" PRIu64, memory));
assert_se(asprintf(&t, "<= %" PRIu64, memory) >= 0);
test_condition_test_memory_one(t, true);
t = mfree(t);
ASSERT_OK(asprintf(&t, ">= %" PRIu64, memory));
assert_se(asprintf(&t, ">= %" PRIu64, memory) >= 0);
test_condition_test_memory_one(t, true);
t = mfree(t);
ASSERT_OK(asprintf(&t, "!= %" PRIu64, memory));
assert_se(asprintf(&t, "!= %" PRIu64, memory) >= 0);
test_condition_test_memory_one(t, false);
t = mfree(t);
ASSERT_OK(asprintf(&t, "< %" PRIu64, memory));
assert_se(asprintf(&t, "< %" PRIu64, memory) >= 0);
test_condition_test_memory_one(t, false);
t = mfree(t);
ASSERT_OK(asprintf(&t, "> %" PRIu64, memory));
assert_se(asprintf(&t, "> %" PRIu64, memory) >= 0);
test_condition_test_memory_one(t, false);
t = mfree(t);
}
static void test_condition_test_environment_one(const char *s, bool result) {
Condition *condition;
int r;
log_debug("%s=%s", condition_type_to_string(CONDITION_ENVIRONMENT), s);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_ENVIRONMENT, s, false, false)));
ASSERT_OK_EQ(condition_test(condition, environ), result);
r = condition_test(condition, environ);
assert_se(r >= 0);
assert_se(r == result);
condition_free(condition);
}
@ -1462,11 +1463,13 @@ TEST(condition_test_kernel_module_loaded) {
Condition *condition;
int r;
condition = ASSERT_NOT_NULL(condition_new(CONDITION_KERNEL_MODULE_LOADED, "", /* trigger= */ false, /* negate= */ false));
condition = condition_new(CONDITION_KERNEL_MODULE_LOADED, "", /* trigger= */ false, /* negate= */ false);
assert_se(condition);
ASSERT_OK_ZERO(condition_test(condition, environ));
condition_free(condition);
condition = ASSERT_NOT_NULL(condition_new(CONDITION_KERNEL_MODULE_LOADED, "..", /* trigger= */ false, /* negate= */ false));
condition = condition_new(CONDITION_KERNEL_MODULE_LOADED, "..", /* trigger= */ false, /* negate= */ false);
assert_se(condition);
ASSERT_OK_ZERO(condition_test(condition, environ));
condition_free(condition);
@ -1474,7 +1477,8 @@ TEST(condition_test_kernel_module_loaded) {
return (void) log_tests_skipped("/sys/module not available, skipping.");
FOREACH_STRING(m, "random", "vfat", "fat", "cec", "binfmt_misc", "binfmt-misc") {
condition = ASSERT_NOT_NULL(condition_new(CONDITION_KERNEL_MODULE_LOADED, m, /* trigger= */ false, /* negate= */ false));
condition = condition_new(CONDITION_KERNEL_MODULE_LOADED, m, /* trigger= */ false, /* negate= */ false);
assert_se(condition);
r = condition_test(condition, environ);
ASSERT_OK(r);
condition_free(condition);
@ -1482,7 +1486,8 @@ TEST(condition_test_kernel_module_loaded) {
log_notice("kmod %s is loaded: %s", m, yes_no(r));
}
condition = ASSERT_NOT_NULL(condition_new(CONDITION_KERNEL_MODULE_LOADED, "idefinitelydontexist", /* trigger= */ false, /* negate= */ false));
condition = condition_new(CONDITION_KERNEL_MODULE_LOADED, "idefinitelydontexist", /* trigger= */ false, /* negate= */ false);
assert_se(condition);
ASSERT_OK_ZERO(condition_test(condition, environ));
condition_free(condition);
}

View File

@ -270,20 +270,6 @@ static int dump_event_json(UdevEvent *event, sd_json_format_flags_t flags, FILE
return r;
}
tags = strv_free(tags);
FOREACH_DEVICE_CURRENT_TAG(dev, tag) {
r = strv_extend(&tags, tag);
if (r < 0)
return r;
}
if (!strv_isempty(tags)) {
r = sd_json_variant_set_field_strv(&v, "currentTags", strv_sort(tags));
if (r < 0)
return r;
}
char **properties;
if (device_get_properties_strv(dev, &properties) >= 0 && !strv_isempty(properties)) {
r = sd_json_variant_set_field_strv(&v, "properties", strv_sort(properties));
@ -431,12 +417,6 @@ int dump_event(UdevEvent *event, sd_json_format_flags_t flags, FILE *f) {
fprintf(f, " %s\n", tag);
}
if (sd_device_get_current_tag_first(dev)) {
fprintf(f, "%sCurrent Tags:%s\n", ansi_highlight(), ansi_normal());
FOREACH_DEVICE_CURRENT_TAG(dev, tag)
fprintf(f, " %s\n", tag);
}
char **properties;
if (device_get_properties_strv(dev, &properties) >= 0 && !strv_isempty(properties)) {
bool space = true;

View File

@ -63,7 +63,7 @@ int device_broadcast_on_error(sd_device *dev, sd_device_monitor *monitor) {
/* delete state from disk */
(void) device_delete_db(dev);
(void) device_tag_index(dev, /* add= */ false);
(void) device_tag_index(dev, /* device_old= */ NULL, /* add= */ false);
r = device_monitor_send(monitor, /* destination= */ NULL, dev);
if (r < 0) {

View File

@ -310,7 +310,7 @@ static int event_execute_rules_on_remove(UdevEvent *event, UdevRules *rules) {
log_device_debug_errno(dev, r, "Failed to read database under /run/udev/data/: %m");
if (EVENT_MODE_DESTRUCTIVE(event)) {
r = device_tag_index(dev, /* add= */ false);
r = device_tag_index(dev, NULL, false);
if (r < 0)
log_device_debug_errno(dev, r, "Failed to remove corresponding tag files under /run/udev/tag/, ignoring: %m");
@ -329,6 +329,23 @@ static int event_execute_rules_on_remove(UdevEvent *event, UdevRules *rules) {
return r;
}
static int copy_all_tags(sd_device *d, sd_device *s) {
int r;
assert(d);
if (!s)
return 0;
FOREACH_DEVICE_TAG(s, tag) {
r = device_add_tag(d, tag, false);
if (r < 0)
return r;
}
return 0;
}
static int update_clone(UdevEvent *event) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev_db_clone);
int r;
@ -381,7 +398,7 @@ int udev_event_execute_rules(UdevEvent *event, UdevRules *rules) {
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to clone sd_device object: %m");
r = device_copy_all_tags(dev, event->dev_db_clone);
r = copy_all_tags(dev, event->dev_db_clone);
if (r < 0)
log_device_warning_errno(dev, r, "Failed to copy all tags from old database entry, ignoring: %m");
@ -416,7 +433,7 @@ int udev_event_execute_rules(UdevEvent *event, UdevRules *rules) {
if (EVENT_MODE_DESTRUCTIVE(event)) {
/* (re)write database file */
r = device_tag_index(dev, /* add= */ true);
r = device_tag_index(dev, event->dev_db_clone, true);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to update tags under /run/udev/tag/: %m");
}

View File

@ -2900,11 +2900,8 @@ static int udev_rule_apply_token_to_event(
assert(IN_SET(token->op, OP_ASSIGN, OP_ADD));
if (token->op == OP_ASSIGN) {
r = device_cleanup_tags(dev, event->dev_db_clone);
if (r < 0)
log_event_warning_errno(event, token, r, "Failed to clear previously assigned tags, ignoring: %m");
}
if (token->op == OP_ASSIGN)
device_cleanup_tags(dev);
r = device_add_tag(dev, buf, /* both= */ true);
if (r == -ENOMEM)

View File

@ -5,4 +5,5 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
g systemd-journal {{SYSTEMD_JOURNAL_GID}} -
g systemd-journal {{SYSTEMD_JOURNAL_GID}} -
u! systemd-journal {{SYSTEMD_JOURNAL_UID}} "systemd Journal"

View File

@ -21,8 +21,6 @@ if [[ ! -x "$SYSUPDATE" ]]; then
exit 77
fi
have_updatectl=$([[ -x "$SYSUPDATED" ]] && command -v updatectl)
# Loopback devices may not be supported. They are used because sfdisk cannot
# change the sector size of a file, and we want to test both 512 and 4096 byte
# sectors. If loopback devices are not supported, we can only test one sector
@ -114,16 +112,6 @@ update_now() {
elif [[ "$update_type" == "split" ]]; then
"$SYSUPDATE" --verify=no acquire
"$SYSUPDATE" --verify=no update
elif [[ "$update_type" == "updatectl" ]]; then
if $have_updatectl; then
systemctl start systemd-sysupdated
updatectl update
else
# Gracefully fall back to sysupdate
"$SYSUPDATE" --verify=no update
fi
else
exit 1
fi
(! "$SYSUPDATE" --verify=no check-new)
}
@ -162,14 +150,8 @@ verify_version_current() {
cmp "$WORKDIR/source/dir-$version/bar.txt" "$WORKDIR/dirs/current/bar.txt"
}
verify_object_fields() {
local updatectl_output="${1:?}"
[[ "${updatectl_output}" != *"Unrecognized object field"* ]] || exit 1
}
for sector_size in "${SECTOR_SIZES[@]}"; do
for update_type in monolithic split-offline split updatectl; do
for update_type in monolithic split-offline split; do
# Disk size of:
# - 1MB for GPT
# - 4 partitions of 2048 sectors each
@ -370,7 +352,7 @@ EOF
# Create sixth version, update using updatectl and verify it replaced the
# correct version
new_version "$sector_size" v6
if $have_updatectl; then
if [[ -x "$SYSUPDATED" ]] && command -v updatectl; then
systemctl start systemd-sysupdated
"$SYSUPDATE" --verify=no check-new
updatectl update
@ -388,12 +370,12 @@ EOF
# testing for specific output, but this will at least catch obvious crashes
# and allow updatectl to run under the various sanitizers. We create a
# component so that updatectl has multiple targets to list.
if $have_updatectl; then
if [[ -x "$SYSUPDATED" ]] && command -v updatectl; then
mkdir -p /run/sysupdate.test.d/
cp "$CONFIGDIR/01-first.transfer" /run/sysupdate.test.d/01-first.transfer
verify_object_fields "$(updatectl list 2>&1)"
verify_object_fields "$(updatectl list host 2>&1)"
verify_object_fields "$(updatectl list host@v6 2>&1)"
updatectl list
updatectl list host
updatectl list host@v6
updatectl check
rm -r /run/sysupdate.test.d
fi

View File

@ -13,10 +13,7 @@ Documentation=man:journalctl(1)
DefaultDependencies=no
Conflicts=shutdown.target
Before=shutdown.target
RequiresMountsFor=/var/log/journal
[Service]
ExecStart=journalctl --system
DynamicUser=yes
User=systemd-journal-access
SupplementaryGroups=systemd-journal
ExecStart=journalctl
User=systemd-journal

View File

@ -22,3 +22,6 @@ FileDescriptorName=varlink
SocketMode=0600
Accept=yes
MaxConnectionsPerSource=16
[Install]
WantedBy=sockets.target

View File

@ -17,3 +17,4 @@ Symlinks=%t/varlink/registry/io.systemd.JournalAccess
FileDescriptorName=varlink
SocketMode=0600
Accept=yes
MaxConnectionsPerSource=16

View File

@ -12,4 +12,4 @@ Description=Journal Log Access Service
Documentation=man:journalctl(1)
[Service]
ExecStart=journalctl --user
ExecStart=journalctl