Compare commits

...

12 Commits

Author SHA1 Message Date
Adrian Vovk d89e3244e1
Merge 3e2b6fd389 into c946b13575 2024-11-22 15:34:28 -06:00
Christian Hesse c946b13575 link README.logs from tmpfiles.d/legacy.conf only if available
The file README.logs is installed only if SysVInit support is enabled.
Thus the link should depend on it as well.
2024-11-22 18:33:20 +00:00
Lennart Poettering e39cbb1442 varlink: apparently on old kernels SO_PEERPIDFD returns EINVAL 2024-11-23 03:09:49 +09:00
Marco Tomaschett bc4a027f9c
hwdb: add support for PineTab2 to 60-sensor.hwdb (#35304)
Add accelerometer support for PineTab2
2024-11-23 03:08:06 +09:00
Lennart Poettering d209e197f8
userdbctl: two trivial fixlets (#35296)
Fixes: #35294
2024-11-22 16:06:01 +01:00
Antonio Alvarez Feijoo 9ed090230e tpm2-util: fix parameter name 2024-11-22 16:04:16 +01:00
Lennart Poettering 47c5ca237b userdbctl: respect selected disposition also when showing gid boundaries
Follow-up for: ad5de3222f
2024-11-22 11:28:30 +01:00
Lennart Poettering 7f8a4f12df userdbctl: fix counting
Fixes: #35294
2024-11-22 11:28:28 +01:00
Lennart Poettering e412fc5e04 userbdctl: show 'mapped' user range only inside of userns
Outside of userns the concept makes no sense, there cannot be users
mapped from further outside.
2024-11-22 11:28:17 +01:00
Adrian Vovk 3e2b6fd389
sysupdate: Add --stream flag for major version bumps
Basically, distros that maintain more than one release stream (i.e.
multiple stable versions, a beta channel, etc) can put the others'
transfer definitions into /usr/lib/sysupdate@$STREAM.d/, and the admin
can pass --stream= on the CLI to switch to this new stream.

Part of https://github.com/systemd/systemd/issues/33345

SQUASHME: s/--next/--streams=<stream>/
2024-10-30 22:00:30 -04:00
Adrian Vovk 6f6d29290e
Warn admins about danger of overriding sysupdate.d
Overriding settings of systemd-sysupdate is quite dangerous - OS vendor
might make a change that is incompatible with the settings in /etc, and
unlike most other config incompatibilities this has the potential to
accidentally wipe the wrong partition during an update. Not good. So
let's warn admins.
2024-10-30 21:54:49 -04:00
Adrian Vovk 19bac76b1a
sysupdate: Document components more visibly
Right now components aren't particularly well documented. Let's make
the feature a bit more visible.
2024-10-30 21:54:48 -04:00
9 changed files with 313 additions and 29 deletions

View File

@ -953,6 +953,15 @@ sensor:modalias:acpi:MXC6655*:dmi:*:svnDefaultstring*:pnP612F:*
sensor:modalias:acpi:SMO8500*:dmi:*:svnPEAQ:pnPEAQPMMC1010MD99187:* sensor:modalias:acpi:SMO8500*:dmi:*:svnPEAQ:pnPEAQPMMC1010MD99187:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1 ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
#########################################
# Pine64
#########################################
# PineTab2
sensor:modalias:of:NaccelerometerT_null_Csilan,sc7a20:*
ACCEL_MOUNT_MATRIX=0, 0, -1; 1, 0, 0; 0, -1, 0
######################################### #########################################
# Pipo # Pipo
######################################### #########################################

View File

@ -190,6 +190,17 @@
<xi:include href="version-info.xml" xpointer="v251"/></listitem> <xi:include href="version-info.xml" xpointer="v251"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>streams</option></term>
<listitem><para>Lists streams that can be updated. This enumerates the
<filename>/var/cache/systemd/sysupdate@*.d/</filename> and <filename>/usr/lib/sysupdate@*.d/</filename>
directories that contain transfer definitions. This command is useful to list possible parameters
for <option>--stream=</option> (see below).</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" /> <xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" /> <xi:include href="standard-options.xml" xpointer="version" />
</variablelist> </variablelist>
@ -225,7 +236,8 @@
updated together in a synchronous fashion. Simply define multiple transfer files within the same updated together in a synchronous fashion. Simply define multiple transfer files within the same
<filename>sysupdate.d/</filename> directory for these cases.</para> <filename>sysupdate.d/</filename> directory for these cases.</para>
<para>This option may not be combined with <option>--definitions=</option>.</para> <para>This option may not be combined with <option>--definitions=</option> or
<option>--stream=</option>.</para>
<xi:include href="version-info.xml" xpointer="v251"/></listitem> <xi:include href="version-info.xml" xpointer="v251"/></listitem>
</varlistentry> </varlistentry>
@ -237,11 +249,29 @@
are read from this directory instead of <filename>/usr/lib/sysupdate.d/*.conf</filename>, are read from this directory instead of <filename>/usr/lib/sysupdate.d/*.conf</filename>,
<filename>/etc/sysupdate.d/*.conf</filename>, and <filename>/run/sysupdate.d/*.conf</filename>.</para> <filename>/etc/sysupdate.d/*.conf</filename>, and <filename>/run/sysupdate.d/*.conf</filename>.</para>
<para>This option may not be combined with <option>--component=</option>.</para> <para>This option may not be combined with <option>--component=</option> or
<option>--stream=</option>.</para>
<xi:include href="version-info.xml" xpointer="v251"/></listitem> <xi:include href="version-info.xml" xpointer="v251"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--stream=</option></term>
<listitem><para>Selects the update stream to use. Takes a stream name as argument. This alters
the search logic for transfer definitions to look in
<filename>/usr/lib/sysupdate@<replaceable>stream</replaceable>.d/</filename> and
<filename>/var/cache/systemd/sysupdate@<replaceable>stream</replaceable>.d/</filename> instead of
<filename>/usr/lib/sysupdate.d</filename>.
Note that administrator-controlled directories (i.e. <filename>/etc/sysupdate.d/</filename>, etc) are
still loaded as usual.</para>
<para>This option may not be combined with <option>--definitions=</option> or
<option>--component=</option>.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--root=</option></term> <term><option>--root=</option></term>

View File

@ -65,6 +65,52 @@
<citerefentry><refentrytitle>sysupdate.features</refentrytitle><manvolnum>5</manvolnum></citerefentry>. <citerefentry><refentrytitle>sysupdate.features</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para> </para>
<para>Sometimes, distributions need to update certain parts of themselves independently from the normal
update cycle.
For example, the <ulink url="https://github.com/rhboot/shim">UEFI Shim loader</ulink> (necessary for
UEFI Secure Boot support in many cases) has its own release cycle, requires code signatures from a
third-party, and in general is not tied to a distribution's update cycle.
Support for this scenario is provided by "components", which allow distributions to define transfer
definitions that receive updates independently from the base OS.
Components are defined in <filename>/usr/lib/sysupdate.<replaceable>component</replaceable>.d/</filename>,
and have corresponding override directories for administrators (i.e.
<filename>/etc/sysupdate.<replaceable>component</replaceable>.d/</filename>, etc).</para>
<para>Some distributions may wish to maintain multiple update "streams" at a time, for example to offer
a beta/nightly update channel, or to distribute security updates to multiple major versions at a time.
Users of such distributions may wish to remain on their current stream, and switch streams at some future
point in time.
A distribution with multiple update streams should ship the transfer definitions for each stream in the
<filename>/usr/lib/sysupdate@<replaceable>stream</replaceable>.d/</filename> or
<filename>/var/cache/systemd/sysupdate@<replaceable>stream</replaceable>.d/</filename> directories.
For example, a distribution with multiple stable branches can ship the next major release's transfer
definitions in the current release's
<filename>/usr/lib/sysupdate@foobarOS-<replaceable>next</replaceable>.d/</filename> directory, and users
can switch to it by updating the <literal>foobarOS-<replaceable>next</replaceable></literal> stream.
How exactly these stream definition directories are delivered is up to distributions: they can stabilize
transfer definitions a version in advance and ship the stream definitions from day one, or they can ship
these files as part of a regular security patch that users will install anyway, or they can use a
component as described above to update the stream definitions under <filename>/var/cache/</filename>
independently from the host system.
Note that the presence of a stream definition directory does not imply the availability of an upgrade on
that stream; it just defines where to look and if an update is found on the remote how to install it.
Also note that the normal administrator override files (i.e. transfer definitions, feature definitions,
or drop-ins found in <filname>/etc/sysupdate.d/</filname>, <filename>/run/sysupdate.d/</filename>, etc)
are applied over top of the definitions found in the stream definition directory.
This is done because a the stream definition directory turns into the normal definition directory
(<filename>/usr/lib/sysupdate.d/</filename>) when that stream is switched to.</para>
<para>System Administrators must take <emphasis>extreme</emphasis> care when overriding any transfer or
optional feature definitions, other than to turn on or off features!
As with any configuration defined in <filename>/usr</filename> and overridden in
<filename>/etc</filename>, an update to the host system can break the administrator overrides.
However, <command>systemd-sysupdate</command> is uniquely destructive: a broken configuration could
prevent the system from updating (best case), or completely destroy an installation by wiping the wrong
partition.
Distributions must take care to avoid breaking systems where overrides exist only to turn on or off
optional features; supporting (or choosing not to) everything else is up to distribution policy.
<emphasis>You have been warned.</emphasis></para>
<para>Each <filename>*.transfer</filename> file contains three sections: [Transfer], [Source] and [Target].</para> <para>Each <filename>*.transfer</filename> file contains three sections: [Transfer], [Source] and [Target].</para>
</refsect1> </refsect1>

View File

@ -64,12 +64,18 @@
"/usr/local/lib/" n "\0" \ "/usr/local/lib/" n "\0" \
"/usr/lib/" n "\0" "/usr/lib/" n "\0"
#define CONF_PATHS(n) \ #define CONF_PATHS_ADMIN(n) \
"/etc/" n, \ "/etc/" n, \
"/run/" n, \ "/run/" n
#define CONF_PATHS_SYSTEM(n) \
"/usr/local/lib/" n, \ "/usr/local/lib/" n, \
"/usr/lib/" n "/usr/lib/" n
#define CONF_PATHS(n) \
CONF_PATHS_ADMIN(n), \
CONF_PATHS_SYSTEM(n)
#define CONF_PATHS_STRV(n) \ #define CONF_PATHS_STRV(n) \
STRV_MAKE(CONF_PATHS(n)) STRV_MAKE(CONF_PATHS(n))

View File

@ -16,7 +16,7 @@ int varlink_get_peer_pidref(sd_varlink *v, PidRef *ret) {
int pidfd = sd_varlink_get_peer_pidfd(v); int pidfd = sd_varlink_get_peer_pidfd(v);
if (pidfd < 0) { if (pidfd < 0) {
if (!ERRNO_IS_NEG_NOT_SUPPORTED(pidfd)) if (!ERRNO_IS_NEG_NOT_SUPPORTED(pidfd) && pidfd != -EINVAL)
return pidfd; return pidfd;
pid_t pid; pid_t pid;

View File

@ -392,7 +392,7 @@ int tpm2_make_pcr_json_array(uint32_t pcr_mask, sd_json_variant **ret);
int tpm2_parse_pcr_json_array(sd_json_variant *v, uint32_t *ret); int tpm2_parse_pcr_json_array(sd_json_variant *v, uint32_t *ret);
int tpm2_make_luks2_json(int keyslot, uint32_t hash_pcr_mask, uint16_t pcr_bank, const struct iovec *pubkey, uint32_t pubkey_pcr_mask, uint16_t primary_alg, const struct iovec blobs[], size_t n_blobs, const struct iovec policy_hash[], size_t n_policy_hash, const struct iovec *salt, const struct iovec *srk, const struct iovec *pcrlock_nv, TPM2Flags flags, sd_json_variant **ret); int tpm2_make_luks2_json(int keyslot, uint32_t hash_pcr_mask, uint16_t pcr_bank, const struct iovec *pubkey, uint32_t pubkey_pcr_mask, uint16_t primary_alg, const struct iovec blobs[], size_t n_blobs, const struct iovec policy_hash[], size_t n_policy_hash, const struct iovec *salt, const struct iovec *srk, const struct iovec *pcrlock_nv, TPM2Flags flags, sd_json_variant **ret);
int tpm2_parse_luks2_json(sd_json_variant *v, int *ret_keyslot, uint32_t *ret_hash_pcr_mask, uint16_t *ret_pcr_bank, struct iovec *ret_pubkey, uint32_t *ret_pubkey_pcr_mask, uint16_t *ret_primary_alg, struct iovec **ret_blobs, size_t *ret_n_blobs, struct iovec **ret_policy_hash, size_t *ret_n_policy_hash, struct iovec *ret_salt, struct iovec *ret_srk, struct iovec *pcrlock_nv, TPM2Flags *ret_flags); int tpm2_parse_luks2_json(sd_json_variant *v, int *ret_keyslot, uint32_t *ret_hash_pcr_mask, uint16_t *ret_pcr_bank, struct iovec *ret_pubkey, uint32_t *ret_pubkey_pcr_mask, uint16_t *ret_primary_alg, struct iovec **ret_blobs, size_t *ret_n_blobs, struct iovec **ret_policy_hash, size_t *ret_n_policy_hash, struct iovec *ret_salt, struct iovec *ret_srk, struct iovec *ret_pcrlock_nv, TPM2Flags *ret_flags);
/* Default to PCR 7 only */ /* Default to PCR 7 only */
#define TPM2_PCR_INDEX_DEFAULT UINT32_C(7) #define TPM2_PCR_INDEX_DEFAULT UINT32_C(7)

View File

@ -47,6 +47,7 @@ char *arg_root = NULL;
static char *arg_image = NULL; static char *arg_image = NULL;
static bool arg_reboot = false; static bool arg_reboot = false;
static char *arg_component = NULL; static char *arg_component = NULL;
static char *arg_stream = NULL;
static int arg_verify = -1; static int arg_verify = -1;
static ImagePolicy *arg_image_policy = NULL; static ImagePolicy *arg_image_policy = NULL;
static bool arg_offline = false; static bool arg_offline = false;
@ -56,6 +57,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_component, freep); STATIC_DESTRUCTOR_REGISTER(arg_component, freep);
STATIC_DESTRUCTOR_REGISTER(arg_stream, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
STATIC_DESTRUCTOR_REGISTER(arg_transfer_source, freep); STATIC_DESTRUCTOR_REGISTER(arg_transfer_source, freep);
@ -180,7 +182,54 @@ static int context_read_definitions(Context *c, const char* node) {
if (arg_definitions) if (arg_definitions)
dirs = strv_new(arg_definitions); dirs = strv_new(arg_definitions);
else if (arg_component) { else if (arg_stream) {
/* Ultimately we end up with a search path along the lines of: /etc/sysupdate.d/,
* /run/sysupdate.d/, /var/cache/systemd/sysupdate@<stream>.d, /usr/lib/sysupdate@<stream>.d.
* This is very unusual! It seems wrong! But this is the correct behavior. When a
* `systemd-sysupdate --stream=` update is completed, /usr/lib/sysupdate@<stream>.d (or its
* /var/cache alternative) turns into /usr/lib/sysupdate.d, but the admin overrides remain
* untouched. So if we did this any differently, we'd end up in a situation where the admin's
* settings are ignored when first installing a major upgrade but then suddenly considered
* again once the update is completed. In my opinion, that behavior would be more unexpected
* and dangerous than what is implemented here!
*
* Is this a big and surprising footgun for the admin? Yes. But frankly, so is overriding
* anything relating to sysupdate. If an admin has overrides that do anything other than
* turning on/off optional features, they've already aimed a ballistic missile at their
* installation. It'll detonate either immediately when trying to switch streams (as
* implemented now), or when updating to the first patch of the new stream (the alternative);
* the installation is doomed either way. And failing immediately during a major OS upgrade
* seems a lot more preferable, and something that admins will be more prepared for, than a
* subsequent security patch suddenly bricking installations. */
char **admin = STRV_MAKE(CONF_PATHS_ADMIN("sysupdate.d"));
char **system = STRV_MAKE("/var/cache/systemd/", CONF_PATHS_SYSTEM(""));
size_t i = 0;
dirs = new0(char*, strv_length(admin) + strv_length(system) + 1);
if (!dirs)
return log_oom();
STRV_FOREACH(dir, admin) {
char *d;
d = strdup(*dir);
if (!d)
return log_oom();
dirs[i++] = d;
}
STRV_FOREACH(dir, system) {
char *j;
j = strjoin(*dir, "sysupdate@", arg_stream, ".d");
if (!j)
return log_oom();
dirs[i++] = j;
}
} else if (arg_component) {
char **l = CONF_PATHS_STRV(""); char **l = CONF_PATHS_STRV("");
size_t i = 0; size_t i = 0;
@ -247,6 +296,11 @@ static int context_read_definitions(Context *c, const char* node) {
"No transfer definitions for component '%s' found.", "No transfer definitions for component '%s' found.",
arg_component); arg_component);
if (arg_stream)
return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
"No transfer definitions for stream '%s' found.",
arg_stream);
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
"No transfer definitions found."); "No transfer definitions found.");
} }
@ -1532,22 +1586,45 @@ static int component_name_valid(const char *c) {
return filename_is_valid(j); return filename_is_valid(j);
} }
static int verb_components(int argc, char **argv, void *userdata) { static int stream_name_valid(const char *s) {
_cleanup_free_ char *j = NULL;
/* See if the specified string enclosed in the directory prefix+suffix would be a valid file name */
if (isempty(s))
return false;
if (string_has_cc(s, NULL))
return false;
if (!utf8_is_valid(s))
return false;
j = strjoin("sysupdate@", s, ".d");
if (!j)
return -ENOMEM;
return filename_is_valid(j);
}
static int walk_search_paths(char **paths, bool component, char ***ret, bool *ret_has_default) {
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
_cleanup_set_free_ Set *names = NULL; _cleanup_set_free_ Set *names = NULL;
_cleanup_free_ char **z = NULL; /* We use simple free() rather than strv_free() here, since set_free() will free the strings for us */ _cleanup_free_ char **names_strv = NULL; /* free() b/c the set still owns the values */
char **l = CONF_PATHS_STRV(""); _cleanup_strv_free_ char **names_dup = NULL;
bool has_default_component = false; bool has_default = false;
int r; int r;
assert(argc <= 1); assert(paths);
assert(ret);
assert(ret_has_default);
r = process_image(/* ro= */ false, &mounted_dir, &loop_device); r = process_image(/* ro= */ false, &mounted_dir, &loop_device);
if (r < 0) if (r < 0)
return r; return r;
STRV_FOREACH(i, l) { STRV_FOREACH(i, paths) {
_cleanup_closedir_ DIR *d = NULL; _cleanup_closedir_ DIR *d = NULL;
_cleanup_free_ char *p = NULL; _cleanup_free_ char *p = NULL;
@ -1577,11 +1654,11 @@ static int verb_components(int argc, char **argv, void *userdata) {
continue; continue;
if (streq(de->d_name, "sysupdate.d")) { if (streq(de->d_name, "sysupdate.d")) {
has_default_component = true; has_default = true;
continue; continue;
} }
e = startswith(de->d_name, "sysupdate."); e = startswith(de->d_name, component ? "sysupdate." : "sysupdate@");
if (!e) if (!e)
continue; continue;
@ -1593,26 +1670,51 @@ static int verb_components(int argc, char **argv, void *userdata) {
if (!n) if (!n)
return log_oom(); return log_oom();
r = component_name_valid(n); if (component)
r = component_name_valid(n);
else
r = stream_name_valid(n);
if (r < 0) if (r < 0)
return log_error_errno(r, "Unable to validate component name: %m"); return log_error_errno(r, "Unable to validate %s name: %m",
component ? "component" : "stream");
if (r == 0) if (r == 0)
continue; continue;
r = set_ensure_consume(&names, &string_hash_ops_free, TAKE_PTR(n)); r = set_ensure_consume(&names, &string_hash_ops_free, TAKE_PTR(n));
if (r < 0 && r != -EEXIST) if (r < 0 && r != -EEXIST)
return log_error_errno(r, "Failed to add component to set: %m"); return log_error_errno(r, "Failed to add %s to set: %m",
component ? "component" : "stream");
} }
} }
z = set_get_strv(names); names_strv = set_get_strv(names);
if (!z) if (!names_strv)
return log_oom(); return log_oom();
strv_sort(z); names_dup = strv_copy(names_strv);
if (!names_dup)
return log_oom();
strv_sort(names_dup);
*ret = TAKE_PTR(names_dup);
*ret_has_default = has_default;
return 0;
}
static int verb_components(int argc, char **argv, void *userdata) {
_cleanup_strv_free_ char **names = NULL;
bool has_default_component = false;
int r;
assert(argc <= 1);
r = walk_search_paths(CONF_PATHS_STRV(""), true, &names, &has_default_component);
if (r < 0)
return r;
if (!sd_json_format_enabled(arg_json_format_flags)) { if (!sd_json_format_enabled(arg_json_format_flags)) {
if (!has_default_component && set_isempty(names)) { if (!has_default_component && strv_isempty(names)) {
log_info("No components defined."); log_info("No components defined.");
return 0; return 0;
} }
@ -1621,13 +1723,53 @@ static int verb_components(int argc, char **argv, void *userdata) {
printf("%s<default>%s\n", printf("%s<default>%s\n",
ansi_highlight(), ansi_normal()); ansi_highlight(), ansi_normal());
STRV_FOREACH(i, z) STRV_FOREACH(i, names)
puts(*i); puts(*i);
} else { } else {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL; _cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL;
r = sd_json_buildo(&json, SD_JSON_BUILD_PAIR_BOOLEAN("default", has_default_component), r = sd_json_buildo(&json, SD_JSON_BUILD_PAIR_BOOLEAN("default", has_default_component),
SD_JSON_BUILD_PAIR_STRV("components", z)); SD_JSON_BUILD_PAIR_STRV("components", names));
if (r < 0)
return log_error_errno(r, "Failed to create JSON: %m");
r = sd_json_variant_dump(json, arg_json_format_flags, stdout, NULL);
if (r < 0)
return log_error_errno(r, "Failed to print JSON: %m");
}
return 0;
}
static int verb_streams(int argc, char **argv, void *userdata) {
char **dirs = STRV_MAKE("/var/cache/systemd/", CONF_PATHS_SYSTEM(""));
_cleanup_strv_free_ char **names = NULL;
bool has_default_stream = false;
int r;
assert(argc <= 1);
r = walk_search_paths(dirs, false, &names, &has_default_stream);
if (r < 0)
return r;
if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
if (!has_default_stream && strv_isempty(names)) {
log_info("No streams defined.");
return 0;
}
if (has_default_stream)
printf("%s<default>%s\n",
ansi_highlight(), ansi_normal());
STRV_FOREACH(i, names)
puts(*i);
} else {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL;
r = sd_json_buildo(&json, SD_JSON_BUILD_PAIR_BOOLEAN("default", has_default_stream),
SD_JSON_BUILD_PAIR_STRV("streams", names));
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to create JSON: %m"); return log_error_errno(r, "Failed to create JSON: %m");
@ -1659,11 +1801,13 @@ static int verb_help(int argc, char **argv, void *userdata) {
" currently booted\n" " currently booted\n"
" reboot Reboot if a newer version is installed than booted\n" " reboot Reboot if a newer version is installed than booted\n"
" components Show list of components\n" " components Show list of components\n"
" streams Show list of streams\n"
" -h --help Show this help\n" " -h --help Show this help\n"
" --version Show package version\n" " --version Show package version\n"
"\n%3$sOptions:%4$s\n" "\n%3$sOptions:%4$s\n"
" -C --component=NAME Select component to update\n" " -C --component=NAME Select component to update\n"
" --definitions=DIR Find transfer definitions in specified directory\n" " --definitions=DIR Find transfer definitions in specified directory\n"
" --stream=STREAM Select stream to switch to\n"
" --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\n" " --image-policy=POLICY\n"
@ -1698,6 +1842,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_NO_LEGEND, ARG_NO_LEGEND,
ARG_SYNC, ARG_SYNC,
ARG_DEFINITIONS, ARG_DEFINITIONS,
ARG_STREAM,
ARG_JSON, ARG_JSON,
ARG_ROOT, ARG_ROOT,
ARG_IMAGE, ARG_IMAGE,
@ -1714,6 +1859,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "no-pager", no_argument, NULL, ARG_NO_PAGER }, { "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND }, { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "definitions", required_argument, NULL, ARG_DEFINITIONS }, { "definitions", required_argument, NULL, ARG_DEFINITIONS },
{ "stream", required_argument, NULL, ARG_STREAM },
{ "instances-max", required_argument, NULL, 'm' }, { "instances-max", required_argument, NULL, 'm' },
{ "sync", required_argument, NULL, ARG_SYNC }, { "sync", required_argument, NULL, ARG_SYNC },
{ "json", required_argument, NULL, ARG_JSON }, { "json", required_argument, NULL, ARG_JSON },
@ -1770,6 +1916,24 @@ static int parse_argv(int argc, char *argv[]) {
return r; return r;
break; break;
case ARG_STREAM:
if (isempty(optarg)) {
arg_stream = mfree(arg_stream);
break;
}
r = stream_name_valid(optarg);
if (r < 0)
return log_error_errno(r, "Failed to determine if stream name is valid: %m");
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Stream name invalid: %s", optarg);
r = free_and_strdup_warn(&arg_stream, optarg);
if (r < 0)
return r;
break;
case ARG_JSON: case ARG_JSON:
r = parse_json_argument(optarg, &arg_json_format_flags); r = parse_json_argument(optarg, &arg_json_format_flags);
if (r <= 0) if (r <= 0)
@ -1856,6 +2020,12 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_definitions && arg_component) if (arg_definitions && arg_component)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --definitions= and --component= switches may not be combined."); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --definitions= and --component= switches may not be combined.");
if (arg_definitions && arg_stream)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --definitions= and --stream= switches may not be combined.");
if (arg_component && arg_stream)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --component= and --stream= switches may not be combined.");
return 1; return 1;
} }
@ -1864,6 +2034,7 @@ static int sysupdate_main(int argc, char *argv[]) {
static const Verb verbs[] = { static const Verb verbs[] = {
{ "list", VERB_ANY, 2, VERB_DEFAULT, verb_list }, { "list", VERB_ANY, 2, VERB_DEFAULT, verb_list },
{ "components", VERB_ANY, 1, 0, verb_components }, { "components", VERB_ANY, 1, 0, verb_components },
{ "streams", VERB_ANY, 1, 0, verb_streams },
{ "features", VERB_ANY, 2, 0, verb_features }, { "features", VERB_ANY, 2, 0, verb_features },
{ "check-new", VERB_ANY, 1, 0, verb_check_new }, { "check-new", VERB_ANY, 1, 0, verb_check_new },
{ "update", VERB_ANY, 2, 0, verb_update }, { "update", VERB_ANY, 2, 0, verb_update },

View File

@ -23,6 +23,7 @@
#include "user-util.h" #include "user-util.h"
#include "userdb.h" #include "userdb.h"
#include "verbs.h" #include "verbs.h"
#include "virt.h"
static enum { static enum {
OUTPUT_CLASSIC, OUTPUT_CLASSIC,
@ -139,10 +140,16 @@ static int show_user(UserRecord *ur, Table *table) {
return 0; return 0;
} }
static bool test_show_mapped(void) {
/* Show mapped user range only in environments where user mapping is a thing. */
return running_in_userns() > 0;
}
static const struct { static const struct {
uid_t first, last; uid_t first, last;
const char *name; const char *name;
UserDisposition disposition; UserDisposition disposition;
bool (*test)(void);
} uid_range_table[] = { } uid_range_table[] = {
{ {
.first = 1, .first = 1,
@ -175,11 +182,12 @@ static const struct {
.last = MAP_UID_MAX, .last = MAP_UID_MAX,
.name = "mapped", .name = "mapped",
.disposition = USER_REGULAR, .disposition = USER_REGULAR,
.test = test_show_mapped,
}, },
}; };
static int table_add_uid_boundaries(Table *table, const UIDRange *p) { static int table_add_uid_boundaries(Table *table, const UIDRange *p) {
int r; int r, n_added = 0;
assert(table); assert(table);
@ -192,6 +200,9 @@ static int table_add_uid_boundaries(Table *table, const UIDRange *p) {
if (!uid_range_covers(p, i->first, i->last - i->first + 1)) if (!uid_range_covers(p, i->first, i->last - i->first + 1))
continue; continue;
if (i->test && !i->test())
continue;
name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN), name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN),
" begin ", i->name, " users ", " begin ", i->name, " users ",
special_glyph(SPECIAL_GLYPH_ARROW_DOWN)); special_glyph(SPECIAL_GLYPH_ARROW_DOWN));
@ -249,9 +260,11 @@ static int table_add_uid_boundaries(Table *table, const UIDRange *p) {
TABLE_INT, 1); /* sort after any other entry with the same UID */ TABLE_INT, 1); /* sort after any other entry with the same UID */
if (r < 0) if (r < 0)
return table_log_add_error(r); return table_log_add_error(r);
n_added += 2;
} }
return ELEMENTSOF(uid_range_table) * 2; return n_added;
} }
static int add_unavailable_uid(Table *table, uid_t start, uid_t end) { static int add_unavailable_uid(Table *table, uid_t start, uid_t end) {
@ -565,16 +578,22 @@ static int show_group(GroupRecord *gr, Table *table) {
} }
static int table_add_gid_boundaries(Table *table, const UIDRange *p) { static int table_add_gid_boundaries(Table *table, const UIDRange *p) {
int r; int r, n_added = 0;
assert(table); assert(table);
FOREACH_ELEMENT(i, uid_range_table) { FOREACH_ELEMENT(i, uid_range_table) {
_cleanup_free_ char *name = NULL, *comment = NULL; _cleanup_free_ char *name = NULL, *comment = NULL;
if (!FLAGS_SET(arg_disposition_mask, UINT64_C(1) << i->disposition))
continue;
if (!uid_range_covers(p, i->first, i->last - i->first + 1)) if (!uid_range_covers(p, i->first, i->last - i->first + 1))
continue; continue;
if (i->test && !i->test())
continue;
name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN), name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN),
" begin ", i->name, " groups ", " begin ", i->name, " groups ",
special_glyph(SPECIAL_GLYPH_ARROW_DOWN)); special_glyph(SPECIAL_GLYPH_ARROW_DOWN));
@ -626,9 +645,11 @@ static int table_add_gid_boundaries(Table *table, const UIDRange *p) {
TABLE_INT, 1); /* sort after any other entry with the same GID */ TABLE_INT, 1); /* sort after any other entry with the same GID */
if (r < 0) if (r < 0)
return table_log_add_error(r); return table_log_add_error(r);
n_added += 2;
} }
return ELEMENTSOF(uid_range_table) * 2; return n_added;
} }
static int add_unavailable_gid(Table *table, uid_t start, uid_t end) { static int add_unavailable_gid(Table *table, uid_t start, uid_t end) {

View File

@ -13,11 +13,12 @@
d /run/lock 0755 root root - d /run/lock 0755 root root -
L /var/lock - - - - ../run/lock L /var/lock - - - - ../run/lock
{% if HAVE_SYSV_COMPAT %}
{% if CREATE_LOG_DIRS %} {% if CREATE_LOG_DIRS %}
L$ /var/log/README - - - - ../..{{DOC_DIR}}/README.logs L$ /var/log/README - - - - ../..{{DOC_DIR}}/README.logs
{% endif %} {% endif %}
{% if HAVE_SYSV_COMPAT %}
# /run/lock/subsys is used for serializing SysV service execution, and # /run/lock/subsys is used for serializing SysV service execution, and
# hence without use on SysV-less systems. # hence without use on SysV-less systems.
d /run/lock/subsys 0755 root root - d /run/lock/subsys 0755 root root -