1
0
mirror of https://github.com/systemd/systemd synced 2025-09-29 00:34:45 +02:00

Compare commits

..

No commits in common. "33f7b61ca59f9e002ee79bacd498a36eab20e6e9" and "4dbc0be2e5b2e4ad250b93f7f16194486a0a31db" have entirely different histories.

32 changed files with 328 additions and 371 deletions

View File

@ -139,7 +139,7 @@
with newer ones, for example to install a locally compiled development version of some low-level with newer ones, for example to install a locally compiled development version of some low-level
component over the immutable OS image without doing a full OS rebuild or modifying the nominally component over the immutable OS image without doing a full OS rebuild or modifying the nominally
immutable image. (e.g. "install" a locally built package with <command>DESTDIR=/var/lib/extensions/mytest immutable image. (e.g. "install" a locally built package with <command>DESTDIR=/var/lib/extensions/mytest
make install &amp;&amp; systemd-sysext refresh</command>, making it available in make install &amp;&amp; systemd-sysext --refresh</command>, making it available in
<filename>/usr/</filename> as if it was installed in the OS image itself.) This case works regardless if <filename>/usr/</filename> as if it was installed in the OS image itself.) This case works regardless if
the underlying host <filename>/usr/</filename> is managed as immutable disk image or is a traditional the underlying host <filename>/usr/</filename> is managed as immutable disk image or is a traditional
package manager controlled (i.e. writable) tree.</para> package manager controlled (i.e. writable) tree.</para>
@ -148,19 +148,12 @@
<refsect1> <refsect1>
<title>Commands</title> <title>Commands</title>
<para>The following commands are understood:</para> <para>The following command switches are understood:</para>
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><option>status</option></term> <term><option>--merge</option></term>
<term><option>-m</option></term>
<listitem><para>When invoked without any command verb, or when <option>status</option> is specified
the current merge status is shown, separately for both <filename>/usr/</filename> and
<filename>/opt/</filename>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>merge</option></term>
<listitem><para>Merges all currently installed system extension images into <listitem><para>Merges all currently installed system extension images into
<filename>/usr/</filename> and <filename>/opt/</filename>, by overmounting these hierarchies with an <filename>/usr/</filename> and <filename>/opt/</filename>, by overmounting these hierarchies with an
<literal>overlayfs</literal> file system combining the underlying hierarchies with those included in <literal>overlayfs</literal> file system combining the underlying hierarchies with those included in
@ -168,20 +161,22 @@
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>unmerge</option></term> <term><option>--unmerge</option></term>
<term><option>-u</option></term>
<listitem><para>Unmerges all currently installed system extension images from <listitem><para>Unmerges all currently installed system extension images from
<filename>/usr/</filename> and <filename>/opt/</filename>, by unmounting the <filename>/usr/</filename> and <filename>/opt/</filename>, by unmounting the
<literal>overlayfs</literal> file systems created by <option>merge</option> <literal>overlayfs</literal> file systems created by <option>--merge</option>
prior.</para></listitem> prior.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>refresh</option></term> <term><option>--refresh</option></term>
<listitem><para>A combination of <option>unmerge</option> and <option>merge</option>: if already <term><option>-R</option></term>
<listitem><para>A combination of <option>--unmerge</option> and <option>--merge</option>: if already
mounted the existing <literal>overlayfs</literal> instance is unmounted temporarily, and then mounted the existing <literal>overlayfs</literal> instance is unmounted temporarily, and then
replaced by a new version. This command is useful after installing/removing system extension images, replaced by a new version. This command is useful after installing/removing system extension images,
in order to update the <literal>overlayfs</literal> file system accordingly. If no system extensions in order to update the <literal>overlayfs</literal> file system accordingly. If no system extensions
are installed when this command is executed, the equivalent of <option>unmerge</option> is are installed when this command is executed, the equivalent of <option>--unmerge</option> is
executed, without establishing any new <literal>overlayfs</literal> instance. Note that currently executed, without establishing any new <literal>overlayfs</literal> instance. Note that currently
there's a brief moment where neither the old nor the new <literal>overlayfs</literal> file system is there's a brief moment where neither the old nor the new <literal>overlayfs</literal> file system is
mounted. This implies that all resources supplied by a system extension will briefly disappear — even mounted. This implies that all resources supplied by a system extension will briefly disappear — even
@ -189,7 +184,8 @@
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>list</option></term> <term><option>--list</option></term>
<term><option>-l</option></term>
<listitem><para>A brief list of installed extension images is shown.</para></listitem> <listitem><para>A brief list of installed extension images is shown.</para></listitem>
</varlistentry> </varlistentry>
@ -197,6 +193,9 @@
<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>
<para>When invoked without any command switches, the current merge status is shown, separately for both
<filename>/usr/</filename> and <filename>/opt/</filename>.</para>
</refsect1> </refsect1>
<refsect1> <refsect1>
@ -219,15 +218,6 @@
output style, or explicitly disabling JSON output.</para></listitem> output style, or explicitly disabling JSON output.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--force</option></term>
<listitem><para>When merging system extensions into <filename>/usr/</filename> and
<filename>/opt/</filename>, ignore version incompatibilities, i.e. force merging regardless of
whether the version information included in the extension images matches the host or
not.</para></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="no-pager" /> <xi:include href="standard-options.xml" xpointer="no-pager" />
</variablelist> </variablelist>
</refsect1> </refsect1>

View File

@ -117,20 +117,6 @@
<para>The <varname>MountAPIVFS=</varname> and <varname>PrivateUsers=</varname> settings are particularly useful <para>The <varname>MountAPIVFS=</varname> and <varname>PrivateUsers=</varname> settings are particularly useful
in conjunction with <varname>RootDirectory=</varname>. For details, see below.</para> in conjunction with <varname>RootDirectory=</varname>. For details, see below.</para>
<para>If <varname>RootDirectory=</varname>/<varname>RootImage=</varname> are used together with
<varname>NotifyAccess=</varname> the notification socket is automatically mounted from the host into
the root environment, to ensure the notification interface can work correctly.</para>
<para>Note that services using <varname>RootDirectory=</varname>/<varname>RootImage=</varname> will
not be able to log via the syslog or journal protocols to the host logging infrastructure, unless the
relevant sockets are mounted from the host, specifically:</para>
<example>
<title>Mounting logging sockets into root environment</title>
<programlisting>BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout</programlisting>
</example>
<xi:include href="system-only.xml" xpointer="singular"/></listitem> <xi:include href="system-only.xml" xpointer="singular"/></listitem>
</varlistentry> </varlistentry>

View File

@ -1022,6 +1022,7 @@ static int list_dependencies(sd_bus *bus, const char *name) {
static int analyze_critical_chain(int argc, char *argv[], void *userdata) { static int analyze_critical_chain(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(unit_times_freep) struct unit_times *times = NULL; _cleanup_(unit_times_freep) struct unit_times *times = NULL;
struct unit_times *u;
Hashmap *h; Hashmap *h;
int n, r; int n, r;
@ -1037,7 +1038,7 @@ static int analyze_critical_chain(int argc, char *argv[], void *userdata) {
if (!h) if (!h)
return log_oom(); return log_oom();
for (struct unit_times *u = times; u->has_data; u++) { for (u = times; u->has_data; u++) {
r = hashmap_put(h, u->name, u); r = hashmap_put(h, u->name, u);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to add entry to hashmap: %m"); return log_error_errno(r, "Failed to add entry to hashmap: %m");
@ -1064,6 +1065,7 @@ static int analyze_blame(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(unit_times_freep) struct unit_times *times = NULL; _cleanup_(unit_times_freep) struct unit_times *times = NULL;
_cleanup_(table_unrefp) Table *table = NULL; _cleanup_(table_unrefp) Table *table = NULL;
struct unit_times *u;
TableCell *cell; TableCell *cell;
int n, r; int n, r;
@ -1103,7 +1105,7 @@ static int analyze_blame(int argc, char *argv[], void *userdata) {
if (r < 0) if (r < 0)
return r; return r;
for (struct unit_times *u = times; u->has_data; u++) { for (u = times; u->has_data; u++) {
if (u->time <= 0) if (u->time <= 0)
continue; continue;

View File

@ -673,7 +673,7 @@ int btrfs_qgroup_get_quota(const char *path, uint64_t qgroupid, BtrfsQuotaInfo *
int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret) { int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret) {
uint64_t level, lowest = (uint64_t) -1, lowest_qgroupid = 0; uint64_t level, lowest = (uint64_t) -1, lowest_qgroupid = 0;
_cleanup_free_ uint64_t *qgroups = NULL; _cleanup_free_ uint64_t *qgroups = NULL;
int r, n; int r, n, i;
assert(fd >= 0); assert(fd >= 0);
assert(ret); assert(ret);
@ -703,7 +703,7 @@ int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret)
if (n < 0) if (n < 0)
return n; return n;
for (int i = 0; i < n; i++) { for (i = 0; i < n; i++) {
uint64_t id; uint64_t id;
r = btrfs_qgroupid_split(qgroups[i], &level, &id); r = btrfs_qgroupid_split(qgroups[i], &level, &id);
@ -824,6 +824,7 @@ int btrfs_qgroup_set_limit_fd(int fd, uint64_t qgroupid, uint64_t referenced_max
.lim.max_rfer = referenced_max, .lim.max_rfer = referenced_max,
.lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER, .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
}; };
unsigned c;
int r; int r;
assert(fd >= 0); assert(fd >= 0);
@ -842,7 +843,7 @@ int btrfs_qgroup_set_limit_fd(int fd, uint64_t qgroupid, uint64_t referenced_max
args.qgroupid = qgroupid; args.qgroupid = qgroupid;
for (unsigned c = 0;; c++) { for (c = 0;; c++) {
if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0) { if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0) {
if (errno == EBUSY && c < 10) { if (errno == EBUSY && c < 10) {
@ -923,6 +924,7 @@ static int qgroup_create_or_destroy(int fd, bool b, uint64_t qgroupid) {
.create = b, .create = b,
.qgroupid = qgroupid, .qgroupid = qgroupid,
}; };
unsigned c;
int r; int r;
r = btrfs_is_filesystem(fd); r = btrfs_is_filesystem(fd);
@ -931,7 +933,7 @@ static int qgroup_create_or_destroy(int fd, bool b, uint64_t qgroupid) {
if (r == 0) if (r == 0)
return -ENOTTY; return -ENOTTY;
for (unsigned c = 0;; c++) { for (c = 0;; c++) {
if (ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args) < 0) { if (ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args) < 0) {
/* On old kernels if quota is not enabled, we get EINVAL. On newer kernels we get /* On old kernels if quota is not enabled, we get EINVAL. On newer kernels we get
@ -966,7 +968,7 @@ int btrfs_qgroup_destroy(int fd, uint64_t qgroupid) {
int btrfs_qgroup_destroy_recursive(int fd, uint64_t qgroupid) { int btrfs_qgroup_destroy_recursive(int fd, uint64_t qgroupid) {
_cleanup_free_ uint64_t *qgroups = NULL; _cleanup_free_ uint64_t *qgroups = NULL;
uint64_t subvol_id; uint64_t subvol_id;
int n, r; int i, n, r;
/* Destroys the specified qgroup, but unassigns it from all /* Destroys the specified qgroup, but unassigns it from all
* its parents first. Also, it recursively destroys all * its parents first. Also, it recursively destroys all
@ -981,7 +983,7 @@ int btrfs_qgroup_destroy_recursive(int fd, uint64_t qgroupid) {
if (n < 0) if (n < 0)
return n; return n;
for (int i = 0; i < n; i++) { for (i = 0; i < n; i++) {
uint64_t id; uint64_t id;
r = btrfs_qgroupid_split(qgroups[i], NULL, &id); r = btrfs_qgroupid_split(qgroups[i], NULL, &id);
@ -1041,6 +1043,7 @@ static int qgroup_assign_or_unassign(int fd, bool b, uint64_t child, uint64_t pa
.src = child, .src = child,
.dst = parent, .dst = parent,
}; };
unsigned c;
int r; int r;
r = btrfs_is_filesystem(fd); r = btrfs_is_filesystem(fd);
@ -1049,7 +1052,7 @@ static int qgroup_assign_or_unassign(int fd, bool b, uint64_t child, uint64_t pa
if (r == 0) if (r == 0)
return -ENOTTY; return -ENOTTY;
for (unsigned c = 0;; c++) { for (c = 0;; c++) {
r = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args); r = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args);
if (r < 0) { if (r < 0) {
if (errno == EBUSY && c < 10) { if (errno == EBUSY && c < 10) {
@ -1348,7 +1351,7 @@ int btrfs_qgroup_copy_limits(int fd, uint64_t old_qgroupid, uint64_t new_qgroupi
static int copy_quota_hierarchy(int fd, uint64_t old_subvol_id, uint64_t new_subvol_id) { static int copy_quota_hierarchy(int fd, uint64_t old_subvol_id, uint64_t new_subvol_id) {
_cleanup_free_ uint64_t *old_qgroups = NULL, *old_parent_qgroups = NULL; _cleanup_free_ uint64_t *old_qgroups = NULL, *old_parent_qgroups = NULL;
bool copy_from_parent = false, insert_intermediary_qgroup = false; bool copy_from_parent = false, insert_intermediary_qgroup = false;
int n_old_qgroups, n_old_parent_qgroups, r; int n_old_qgroups, n_old_parent_qgroups, r, i;
uint64_t old_parent_id; uint64_t old_parent_id;
assert(fd >= 0); assert(fd >= 0);
@ -1372,8 +1375,9 @@ static int copy_quota_hierarchy(int fd, uint64_t old_subvol_id, uint64_t new_sub
return n_old_parent_qgroups; return n_old_parent_qgroups;
} }
for (int i = 0; i < n_old_qgroups; i++) { for (i = 0; i < n_old_qgroups; i++) {
uint64_t id; uint64_t id;
int j;
r = btrfs_qgroupid_split(old_qgroups[i], NULL, &id); r = btrfs_qgroupid_split(old_qgroups[i], NULL, &id);
if (r < 0) if (r < 0)
@ -1388,7 +1392,7 @@ static int copy_quota_hierarchy(int fd, uint64_t old_subvol_id, uint64_t new_sub
break; break;
} }
for (int j = 0; j < n_old_parent_qgroups; j++) for (j = 0; j < n_old_parent_qgroups; j++)
if (old_parent_qgroups[j] == old_qgroups[i]) if (old_parent_qgroups[j] == old_qgroups[i])
/* The old subvolume shared a common /* The old subvolume shared a common
* parent qgroup with its parent * parent qgroup with its parent
@ -1876,11 +1880,12 @@ int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool insert_intermed
if (insert_intermediary_qgroup) { if (insert_intermediary_qgroup) {
uint64_t lowest = 256, new_qgroupid; uint64_t lowest = 256, new_qgroupid;
bool created = false; bool created = false;
int i;
/* Determine the lowest qgroup that the parent /* Determine the lowest qgroup that the parent
* subvolume is assigned to. */ * subvolume is assigned to. */
for (int i = 0; i < n; i++) { for (i = 0; i < n; i++) {
uint64_t level; uint64_t level;
r = btrfs_qgroupid_split(qgroups[i], &level, NULL); r = btrfs_qgroupid_split(qgroups[i], &level, NULL);
@ -1905,7 +1910,7 @@ int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool insert_intermed
if (r >= 0) if (r >= 0)
changed = created = true; changed = created = true;
for (int i = 0; i < n; i++) { for (i = 0; i < n; i++) {
r = btrfs_qgroup_assign(fd, new_qgroupid, qgroups[i]); r = btrfs_qgroup_assign(fd, new_qgroupid, qgroups[i]);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
if (created) if (created)

View File

@ -432,7 +432,7 @@ int manager_varlink_init(Manager *m) {
if (!MANAGER_IS_SYSTEM(m)) if (!MANAGER_IS_SYSTEM(m))
return 0; return 0;
r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA); r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to allocate varlink server object: %m"); return log_error_errno(r, "Failed to allocate varlink server object: %m");

View File

@ -3223,7 +3223,6 @@ static int apply_mount_namespace(
context->root_verity, context->root_verity,
propagate_dir, propagate_dir,
incoming_dir, incoming_dir,
root_dir || root_image ? params->notify_socket : NULL,
DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK, DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK,
error_path); error_path);

View File

@ -384,8 +384,6 @@ struct ExecParameters {
/* An fd that is closed by the execve(), and thus will result in EOF when the execve() is done */ /* An fd that is closed by the execve(), and thus will result in EOF when the execve() is done */
int exec_fd; int exec_fd;
const char *notify_socket;
}; };
#include "unit.h" #include "unit.h"

View File

@ -1302,8 +1302,7 @@ static size_t namespace_calculate_mounts(
const char* var_tmp_dir, const char* var_tmp_dir,
const char *creds_path, const char *creds_path,
const char* log_namespace, const char* log_namespace,
bool setup_propagate, bool setup_propagate) {
const char* notify_socket) {
size_t protect_home_cnt; size_t protect_home_cnt;
size_t protect_system_cnt = size_t protect_system_cnt =
@ -1330,6 +1329,7 @@ static size_t namespace_calculate_mounts(
n_bind_mounts + n_bind_mounts +
n_mount_images + n_mount_images +
n_temporary_filesystems + n_temporary_filesystems +
(setup_propagate ? 1 : 0) + /* /run/systemd/incoming */
ns_info->private_dev + ns_info->private_dev +
(ns_info->protect_kernel_tunables ? ELEMENTSOF(protect_kernel_tunables_table) : 0) + (ns_info->protect_kernel_tunables ? ELEMENTSOF(protect_kernel_tunables_table) : 0) +
(ns_info->protect_kernel_modules ? ELEMENTSOF(protect_kernel_modules_table) : 0) + (ns_info->protect_kernel_modules ? ELEMENTSOF(protect_kernel_modules_table) : 0) +
@ -1339,9 +1339,7 @@ static size_t namespace_calculate_mounts(
(ns_info->protect_hostname ? 2 : 0) + (ns_info->protect_hostname ? 2 : 0) +
(namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0) + (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0) +
(creds_path ? 2 : 1) + (creds_path ? 2 : 1) +
!!log_namespace + !!log_namespace;
setup_propagate + /* /run/systemd/incoming */
!!notify_socket;
} }
static void normalize_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) { static void normalize_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) {
@ -1493,7 +1491,6 @@ int setup_namespace(
const char *verity_data_path, const char *verity_data_path,
const char *propagate_dir, const char *propagate_dir,
const char *incoming_dir, const char *incoming_dir,
const char *notify_socket,
DissectImageFlags dissect_image_flags, DissectImageFlags dissect_image_flags,
char **error_path) { char **error_path) {
@ -1596,8 +1593,7 @@ int setup_namespace(
tmp_dir, var_tmp_dir, tmp_dir, var_tmp_dir,
creds_path, creds_path,
log_namespace, log_namespace,
setup_propagate, setup_propagate);
notify_socket);
if (n_mounts > 0) { if (n_mounts > 0) {
m = mounts = new0(MountEntry, n_mounts); m = mounts = new0(MountEntry, n_mounts);
@ -1775,14 +1771,6 @@ int setup_namespace(
.read_only = true, .read_only = true,
}; };
if (notify_socket)
*(m++) = (MountEntry) {
.path_const = notify_socket,
.source_const = notify_socket,
.mode = BIND_MOUNT,
.read_only = true,
};
assert(mounts + n_mounts == m); assert(mounts + n_mounts == m);
/* Prepend the root directory where that's necessary */ /* Prepend the root directory where that's necessary */

View File

@ -129,7 +129,6 @@ int setup_namespace(
const char *root_verity, const char *root_verity,
const char *propagate_dir, const char *propagate_dir,
const char *incoming_dir, const char *incoming_dir,
const char *notify_socket,
DissectImageFlags dissected_image_flags, DissectImageFlags dissected_image_flags,
char **error_path); char **error_path);

View File

@ -1474,13 +1474,10 @@ static int service_spawn(
if (!our_env) if (!our_env)
return -ENOMEM; return -ENOMEM;
if (service_exec_needs_notify_socket(s, flags)) { if (service_exec_needs_notify_socket(s, flags))
if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0) if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0)
return -ENOMEM; return -ENOMEM;
exec_params.notify_socket = UNIT(s)->manager->notify_socket;
}
if (s->main_pid > 0) if (s->main_pid > 0)
if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0) if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0)
return -ENOMEM; return -ENOMEM;

View File

@ -956,7 +956,7 @@ static int manager_bind_varlink(Manager *m) {
assert(m); assert(m);
assert(!m->varlink_server); assert(!m->varlink_server);
r = varlink_server_new(&m->varlink_server, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA); r = varlink_server_new(&m->varlink_server, VARLINK_SERVER_ACCOUNT_UID);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to allocate varlink server object: %m"); return log_error_errno(r, "Failed to allocate varlink server object: %m");

View File

@ -2033,7 +2033,7 @@ static int server_open_varlink(Server *s, const char *socket, int fd) {
assert(s); assert(s);
r = varlink_server_new(&s->varlink_server, VARLINK_SERVER_ROOT_ONLY|VARLINK_SERVER_INHERIT_USERDATA); r = varlink_server_new(&s->varlink_server, VARLINK_SERVER_ROOT_ONLY);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -732,13 +732,15 @@ _public_ int sd_device_monitor_filter_add_match_subsystem_devtype(sd_device_moni
return -ENOMEM; return -ENOMEM;
} }
r = hashmap_ensure_put(&m->subsystem_filter, NULL, s, d); r = hashmap_ensure_allocated(&m->subsystem_filter, NULL);
if (r < 0) if (r < 0)
return r; return r;
TAKE_PTR(s); r = hashmap_put(m->subsystem_filter, s, d);
TAKE_PTR(d); if (r < 0)
return r;
s = d = NULL;
m->filter_uptodate = false; m->filter_uptodate = false;
return 0; return 0;

View File

@ -629,6 +629,10 @@ static int event_make_signal_data(
return 0; return 0;
} }
} else { } else {
r = hashmap_ensure_allocated(&e->signal_data, &uint64_hash_ops);
if (r < 0)
return r;
d = new(struct signal_data, 1); d = new(struct signal_data, 1);
if (!d) if (!d)
return -ENOMEM; return -ENOMEM;
@ -639,7 +643,7 @@ static int event_make_signal_data(
.priority = priority, .priority = priority,
}; };
r = hashmap_ensure_put(&e->signal_data, &uint64_hash_ops, &d->priority, d); r = hashmap_put(e->signal_data, &d->priority, d);
if (r < 0) { if (r < 0) {
free(d); free(d);
return r; return r;
@ -1723,6 +1727,10 @@ static int event_make_inotify_data(
fd = fd_move_above_stdio(fd); fd = fd_move_above_stdio(fd);
r = hashmap_ensure_allocated(&e->inotify_data, &uint64_hash_ops);
if (r < 0)
return r;
d = new(struct inotify_data, 1); d = new(struct inotify_data, 1);
if (!d) if (!d)
return -ENOMEM; return -ENOMEM;
@ -1733,7 +1741,7 @@ static int event_make_inotify_data(
.priority = priority, .priority = priority,
}; };
r = hashmap_ensure_put(&e->inotify_data, &uint64_hash_ops, &d->priority, d); r = hashmap_put(e->inotify_data, &d->priority, d);
if (r < 0) { if (r < 0) {
d->fd = safe_close(d->fd); d->fd = safe_close(d->fd);
free(d); free(d);

View File

@ -63,7 +63,7 @@ static bool journal_pid_changed(sd_journal *j) {
} }
static int journal_put_error(sd_journal *j, int r, const char *path) { static int journal_put_error(sd_journal *j, int r, const char *path) {
_cleanup_free_ char *copy = NULL; char *copy;
int k; int k;
/* Memorize an error we encountered, and store which /* Memorize an error we encountered, and store which
@ -80,21 +80,27 @@ static int journal_put_error(sd_journal *j, int r, const char *path) {
if (r >= 0) if (r >= 0)
return r; return r;
k = hashmap_ensure_allocated(&j->errors, NULL);
if (k < 0)
return k;
if (path) { if (path) {
copy = strdup(path); copy = strdup(path);
if (!copy) if (!copy)
return -ENOMEM; return -ENOMEM;
} } else
copy = NULL;
k = hashmap_ensure_put(&j->errors, NULL, INT_TO_PTR(r), copy); k = hashmap_put(j->errors, INT_TO_PTR(r), copy);
if (k < 0) { if (k < 0) {
free(copy);
if (k == -EEXIST) if (k == -EEXIST)
return 0; return 0;
return k; return k;
} }
TAKE_PTR(copy);
return 0; return 0;
} }

View File

@ -217,6 +217,10 @@ int manager_write_brightness(
return 0; return 0;
} }
r = hashmap_ensure_allocated(&m->brightness_writers, &brightness_writer_hash_ops);
if (r < 0)
return log_oom();
w = new(BrightnessWriter, 1); w = new(BrightnessWriter, 1);
if (!w) if (!w)
return log_oom(); return log_oom();
@ -230,12 +234,9 @@ int manager_write_brightness(
if (!w->path) if (!w->path)
return log_oom(); return log_oom();
r = hashmap_ensure_put(&m->brightness_writers, &brightness_writer_hash_ops, w->path, w); r = hashmap_put(m->brightness_writers, w->path, w);
if (r == -ENOMEM)
return log_oom();
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to add brightness writer to hashmap: %m"); return log_error_errno(r, "Failed to add brightness writer to hashmap: %m");
w->manager = m; w->manager = m;
r = set_add_message(&w->current_messages, message); r = set_add_message(&w->current_messages, message);

View File

@ -390,6 +390,10 @@ static int image_object_find(sd_bus *bus, const char *path, const char *interfac
return 1; return 1;
} }
r = hashmap_ensure_allocated(&m->image_cache, &image_hash_ops);
if (r < 0)
return r;
if (!m->image_cache_defer_event) { if (!m->image_cache_defer_event) {
r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_flush_cache, m); r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_flush_cache, m);
if (r < 0) if (r < 0)
@ -412,7 +416,7 @@ static int image_object_find(sd_bus *bus, const char *path, const char *interfac
image->userdata = m; image->userdata = m;
r = hashmap_ensure_put(&m->image_cache, &image_hash_ops, image->name, image); r = hashmap_put(m->image_cache, image->name, image);
if (r < 0) { if (r < 0) {
image_unref(image); image_unref(image);
return r; return r;

View File

@ -388,7 +388,7 @@ int manager_varlink_init(Manager *m) {
if (m->varlink_server) if (m->varlink_server)
return 0; return 0;
r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA); r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to allocate varlink server object: %m"); return log_error_errno(r, "Failed to allocate varlink server object: %m");

View File

@ -1544,9 +1544,11 @@ int config_parse_address_generation_type(
token->prefix = buffer.in6; token->prefix = buffer.in6;
} }
r = ordered_set_ensure_put(&network->ipv6_tokens, &ipv6_token_hash_ops, token); r = ordered_set_ensure_allocated(&network->ipv6_tokens, &ipv6_token_hash_ops);
if (r == -ENOMEM) if (r < 0)
return log_oom(); return log_oom();
r = ordered_set_put(network->ipv6_tokens, token);
if (r == -EEXIST) if (r == -EEXIST)
log_syntax(unit, LOG_DEBUG, filename, line, r, log_syntax(unit, LOG_DEBUG, filename, line, r,
"IPv6 token '%s' is duplicated, ignoring: %m", rvalue); "IPv6 token '%s' is duplicated, ignoring: %m", rvalue);

View File

@ -805,9 +805,11 @@ int config_parse_stacked_netdev(const char *unit,
if (!name) if (!name)
return log_oom(); return log_oom();
r = hashmap_ensure_put(h, &string_hash_ops, name, INT_TO_PTR(kind)); r = hashmap_ensure_allocated(h, &string_hash_ops);
if (r == -ENOMEM) if (r < 0)
return log_oom(); return log_oom();
r = hashmap_put(*h, name, INT_TO_PTR(kind));
if (r < 0) if (r < 0)
log_syntax(unit, LOG_WARNING, filename, line, r, log_syntax(unit, LOG_WARNING, filename, line, r,
"Cannot add NetDev '%s' to network, ignoring assignment: %m", name); "Cannot add NetDev '%s' to network, ignoring assignment: %m", name);
@ -815,7 +817,7 @@ int config_parse_stacked_netdev(const char *unit,
log_syntax(unit, LOG_DEBUG, filename, line, r, log_syntax(unit, LOG_DEBUG, filename, line, r,
"NetDev '%s' specified twice, ignoring.", name); "NetDev '%s' specified twice, ignoring.", name);
else else
TAKE_PTR(name); name = NULL;
return 0; return 0;
} }

View File

@ -2,6 +2,8 @@
[Service] [Service]
MountAPIVFS=yes MountAPIVFS=yes
TemporaryFileSystem=/run
BindReadOnlyPaths=/run/systemd/notify
BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
BindReadOnlyPaths=/etc/machine-id BindReadOnlyPaths=/etc/machine-id
BindReadOnlyPaths=/etc/resolv.conf BindReadOnlyPaths=/etc/resolv.conf

View File

@ -2,6 +2,8 @@
[Service] [Service]
MountAPIVFS=yes MountAPIVFS=yes
TemporaryFileSystem=/run
BindReadOnlyPaths=/run/systemd/notify
BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
BindReadOnlyPaths=/etc/machine-id BindReadOnlyPaths=/etc/machine-id
BindReadOnlyPaths=/run/dbus/system_bus_socket BindReadOnlyPaths=/run/dbus/system_bus_socket

View File

@ -2,6 +2,8 @@
[Service] [Service]
MountAPIVFS=yes MountAPIVFS=yes
TemporaryFileSystem=/run
BindReadOnlyPaths=/run/systemd/notify
BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
BindReadOnlyPaths=/etc/machine-id BindReadOnlyPaths=/etc/machine-id
DynamicUser=yes DynamicUser=yes

View File

@ -2,5 +2,6 @@
[Service] [Service]
MountAPIVFS=yes MountAPIVFS=yes
BindPaths=/run
BindReadOnlyPaths=/etc/machine-id BindReadOnlyPaths=/etc/machine-id
BindReadOnlyPaths=/etc/resolv.conf BindReadOnlyPaths=/etc/resolv.conf

View File

@ -269,13 +269,11 @@ static int vl_method_resolve_hostname(Varlink *link, JsonVariant *parameters, Va
_cleanup_(lookup_parameters_destroy) LookupParameters p = { _cleanup_(lookup_parameters_destroy) LookupParameters p = {
.family = AF_UNSPEC, .family = AF_UNSPEC,
}; };
Manager *m = userdata;
DnsQuery *q; DnsQuery *q;
Manager *m;
int r; int r;
assert(link); assert(link);
m = varlink_server_get_userdata(varlink_get_server(link));
assert(m); assert(m);
if (FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) if (FLAGS_SET(flags, VARLINK_METHOD_ONEWAY))
@ -449,13 +447,11 @@ static int vl_method_resolve_address(Varlink *link, JsonVariant *parameters, Var
_cleanup_(lookup_parameters_destroy) LookupParameters p = { _cleanup_(lookup_parameters_destroy) LookupParameters p = {
.family = AF_UNSPEC, .family = AF_UNSPEC,
}; };
Manager *m = userdata;
DnsQuery *q; DnsQuery *q;
Manager *m;
int r; int r;
assert(link); assert(link);
m = varlink_server_get_userdata(varlink_get_server(link));
assert(m); assert(m);
if (FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) if (FLAGS_SET(flags, VARLINK_METHOD_ONEWAY))

View File

@ -2137,9 +2137,7 @@ int varlink_server_add_connection(VarlinkServer *server, int fd, Varlink **ret)
return r; return r;
v->fd = fd; v->fd = fd;
if (server->flags & VARLINK_SERVER_INHERIT_USERDATA) v->userdata = server->userdata;
v->userdata = server->userdata;
if (ucred_acquired) { if (ucred_acquired) {
v->ucred = ucred; v->ucred = ucred;
v->ucred_acquired = true; v->ucred_acquired = true;

View File

@ -41,12 +41,11 @@ typedef enum VarlinkMethodFlags {
} VarlinkMethodFlags; } VarlinkMethodFlags;
typedef enum VarlinkServerFlags { typedef enum VarlinkServerFlags {
VARLINK_SERVER_ROOT_ONLY = 1 << 0, /* Only accessible by root */ VARLINK_SERVER_ROOT_ONLY = 1 << 0, /* Only accessible by root */
VARLINK_SERVER_MYSELF_ONLY = 1 << 1, /* Only accessible by our own UID */ VARLINK_SERVER_MYSELF_ONLY = 1 << 1, /* Only accessible by our own UID */
VARLINK_SERVER_ACCOUNT_UID = 1 << 2, /* Do per user accounting */ VARLINK_SERVER_ACCOUNT_UID = 1 << 2, /* Do per user accounting */
VARLINK_SERVER_INHERIT_USERDATA = 1 << 3, /* Initialize Varlink connection userdata from VarlinkServer userdata */
_VARLINK_SERVER_FLAGS_ALL = (1 << 4) - 1, _VARLINK_SERVER_FLAGS_ALL = (1 << 3) - 1,
} VarlinkServerFlags; } VarlinkServerFlags;
typedef int (*VarlinkMethod)(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata); typedef int (*VarlinkMethod)(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata);

View File

@ -29,13 +29,18 @@
#include "stat-util.h" #include "stat-util.h"
#include "terminal-util.h" #include "terminal-util.h"
#include "user-util.h" #include "user-util.h"
#include "verbs.h"
static enum {
ACTION_STATUS,
ACTION_MERGE,
ACTION_UNMERGE,
ACTION_REFRESH,
ACTION_LIST,
} arg_action = ACTION_STATUS;
static char **arg_hierarchies = NULL; /* "/usr" + "/opt" by default */ static char **arg_hierarchies = NULL; /* "/usr" + "/opt" by default */
static char *arg_root = NULL; static char *arg_root = NULL;
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
static PagerFlags arg_pager_flags = 0; static PagerFlags arg_pager_flags = 0;
static bool arg_force = false;
STATIC_DESTRUCTOR_REGISTER(arg_hierarchies, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_hierarchies, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
@ -144,15 +149,7 @@ static int unmerge(void) {
return ret; return ret;
} }
static int verb_unmerge(int argc, char **argv, void *userdata) { static int status(void) {
if (!have_effective_cap(CAP_SYS_ADMIN))
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
return unmerge();
}
static int verb_status(int argc, char **argv, void *userdata) {
_cleanup_(table_unrefp) Table *t = NULL; _cleanup_(table_unrefp) Table *t = NULL;
int r, ret = 0; int r, ret = 0;
char **p; char **p;
@ -402,76 +399,6 @@ static int strverscmpp(char *const* a, char *const* b) {
return strverscmp(*a, *b); return strverscmp(*a, *b);
} }
static int validate_version(
const char *root,
const char *name,
const char *host_os_release_id,
const char *host_os_release_version_id,
const char *host_os_release_sysext_level) {
_cleanup_free_ char *extension_release_id = NULL, *extension_release_version_id = NULL, *extension_release_sysext_level = NULL;
int r;
assert(root);
assert(name);
if (arg_force) {
log_debug("Force mode enabled, skipping version validation.");
return 1;
}
/* Insist that extension images do not overwrite the underlying OS release file (it's fine if
* they place one in /etc/os-release, i.e. where things don't matter, as they aren't
* merged.) */
r = chase_symlinks("/usr/lib/os-release", root, CHASE_PREFIX_ROOT, NULL, NULL);
if (r < 0) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to determine whether /usr/lib/os-release exists in the extension image: %m");
} else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Extension image contains /usr/lib/os-release file, which is not allowed (it may carry /etc/os-release), refusing.");
/* Now that we can look into the extension image, let's see if the OS version is compatible */
r = parse_extension_release(
root,
name,
"ID", &extension_release_id,
"VERSION_ID", &extension_release_version_id,
"SYSEXT_LEVEL", &extension_release_sysext_level,
NULL);
if (r == -ENOENT) {
log_notice_errno(r, "Extension '%s' carries no extension-release data, ignoring extension.", name);
return 0;
}
if (r < 0)
return log_error_errno(r, "Failed to acquire 'os-release' data of extension '%s': %m", name);
if (!streq_ptr(host_os_release_id, extension_release_id)) {
log_notice("Extension '%s' is for OS '%s', but running on '%s', ignoring extension.",
name, strna(extension_release_id), strna(host_os_release_id));
return 0;
}
/* If the extension has a sysext API level declared, then it must match the host API
* level. Otherwise, compare OS version as a whole */
if (extension_release_sysext_level) {
if (!streq_ptr(host_os_release_sysext_level, extension_release_sysext_level)) {
log_notice("Extension '%s' is for sysext API level '%s', but running on sysext API level '%s', ignoring extension.",
name, extension_release_sysext_level, strna(host_os_release_sysext_level));
return 0;
}
} else {
if (!streq_ptr(host_os_release_version_id, extension_release_version_id)) {
log_notice("Extension '%s' is for OS version '%s', but running on OS version '%s', ignoring extension.",
name, extension_release_version_id, strna(host_os_release_version_id));
return 0;
}
}
log_debug("Version info of extension '%s' matches host.", name);
return 1;
}
static int merge_subprocess(Hashmap *images, const char *workspace) { static int merge_subprocess(Hashmap *images, const char *workspace) {
_cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL, *host_os_release_sysext_level = NULL, _cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL, *host_os_release_sysext_level = NULL,
*buf = NULL; *buf = NULL;
@ -513,7 +440,8 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
/* Let's now mount all images */ /* Let's now mount all images */
HASHMAP_FOREACH(img, images) { HASHMAP_FOREACH(img, images) {
_cleanup_free_ char *p = NULL; _cleanup_free_ char *p = NULL,
*extension_release_id = NULL, *extension_release_version_id = NULL, *extension_release_sysext_level = NULL;
p = path_join(workspace, "extensions", img->name); p = path_join(workspace, "extensions", img->name);
if (!p) if (!p)
@ -595,17 +523,57 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
assert_not_reached("Unsupported image type"); assert_not_reached("Unsupported image type");
} }
r = validate_version( /* Insist that extension images do not overwrite the underlying OS release file (it's fine if
* they place one in /etc/os-release, i.e. where things don't matter, as they aren't
* merged.) */
r = chase_symlinks("/usr/lib/os-release", p, CHASE_PREFIX_ROOT, NULL, NULL);
if (r < 0) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to determine whether /usr/lib/os-release exists in the extension image: %m");
} else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Extension image contains /usr/lib/os-release file, which is not allowed (it may carry /etc/os-release), refusing.");
/* Now that we can look into the extension image, let's see if the OS version is compatible */
r = parse_extension_release(
p, p,
img->name, img->name,
host_os_release_id, "ID", &extension_release_id,
host_os_release_version_id, "VERSION_ID", &extension_release_version_id,
host_os_release_sysext_level); "SYSEXT_LEVEL", &extension_release_sysext_level,
if (r < 0) NULL);
return r; if (r == -ENOENT) {
if (r == 0) { log_notice_errno(r, "Extension '%s' carries no extension-release data, ignoring extension.", img->name);
n_ignored++; n_ignored++;
continue; continue;
} else if (r < 0)
return log_error_errno(r, "Failed to acquire 'os-release' data of extension '%s': %m", img->name);
else {
if (!streq_ptr(host_os_release_id, extension_release_id)) {
log_notice("Extension '%s' is for OS '%s', but running on '%s', ignoring extension.",
img->name, strna(extension_release_id), strna(host_os_release_id));
n_ignored++;
continue;
}
/* If the extension has a sysext API level declared, then it must match the host API level. Otherwise, compare OS version as a whole */
if (extension_release_sysext_level) {
if (!streq_ptr(host_os_release_sysext_level, extension_release_sysext_level)) {
log_notice("Extension '%s' is for sysext API level '%s', but running on sysext API level '%s', ignoring extension.",
img->name, extension_release_sysext_level, strna(host_os_release_sysext_level));
n_ignored++;
continue;
}
} else {
if (!streq_ptr(host_os_release_version_id, extension_release_version_id)) {
log_notice("Extension '%s' is for OS version '%s', but running on OS version '%s', ignoring extension.",
img->name, extension_release_version_id, strna(host_os_release_version_id));
n_ignored++;
continue;
}
}
log_debug("Version info of extension '%s' matches host.", img->name);
} }
/* Noice! This one is an extension we want. */ /* Noice! This one is an extension we want. */
@ -743,134 +711,7 @@ static int merge(Hashmap *images) {
return r != 123; /* exit code 123 means: didn't do anything */ return r != 123; /* exit code 123 means: didn't do anything */
} }
static int verb_merge(int argc, char **argv, void *userdata) { static int help(void) {
_cleanup_(hashmap_freep) Hashmap *images = NULL;
char **p;
int r;
if (!have_effective_cap(CAP_SYS_ADMIN))
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
images = hashmap_new(&image_hash_ops);
if (!images)
return log_oom();
r = image_discover(IMAGE_EXTENSION, arg_root, images);
if (r < 0)
return log_error_errno(r, "Failed to discover extension images: %m");
/* In merge mode fail if things are already merged. (In --refresh mode below we'll unmerge if we find
* things are already merged...) */
STRV_FOREACH(p, arg_hierarchies) {
_cleanup_free_ char *resolved = NULL;
r = chase_symlinks(*p, arg_root, CHASE_PREFIX_ROOT, &resolved, NULL);
if (r == -ENOENT) {
log_debug_errno(r, "Hierarchy '%s%s' does not exist, ignoring.", strempty(arg_root), *p);
continue;
}
if (r < 0)
return log_error_errno(r, "Failed to resolve path to hierarchy '%s%s': %m", strempty(arg_root), *p);
r = is_our_mount_point(resolved);
if (r < 0)
return r;
if (r > 0)
return log_error_errno(SYNTHETIC_ERRNO(EBUSY),
"Hierarchy '%s' is already merged.", *p);
}
return merge(images);
}
static int verb_refresh(int argc, char **argv, void *userdata) {
_cleanup_(hashmap_freep) Hashmap *images = NULL;
int r;
if (!have_effective_cap(CAP_SYS_ADMIN))
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
images = hashmap_new(&image_hash_ops);
if (!images)
return log_oom();
r = image_discover(IMAGE_EXTENSION, arg_root, images);
if (r < 0)
return log_error_errno(r, "Failed to discover extension images: %m");
r = merge(images); /* Returns > 0 if it did something, i.e. a new overlayfs is mounted now. When it
* does so it implicitly unmounts any overlayfs placed there before. Returns == 0
* if it did nothing, i.e. no extension images found. In this case the old
* overlayfs remains in place if there was one. */
if (r < 0)
return r;
if (r == 0) /* No images found? Then unmerge. The goal of --refresh is after all that after having
* called there's a guarantee that the merge status matches the installed extensions. */
r = unmerge();
/* Net result here is that:
*
* 1. If an overlayfs was mounted before and no extensions exist anymore, we'll have unmerged things.
*
* 2. If an overlayfs was mounted before, and there are still extensions installed' we'll have
* unmerged and then merged things again.
*
* 3. If an overlayfs so far wasn't mounted, and there are extensions installed, we'll have it
* mounted now.
*
* 4. If there was no overlayfs mount so far, and no extensions installed, we implement a NOP.
*/
return 0;
}
static int verb_list(int argc, char **argv, void *userdata) {
_cleanup_(hashmap_freep) Hashmap *images = NULL;
_cleanup_(table_unrefp) Table *t = NULL;
Image *img;
int r;
images = hashmap_new(&image_hash_ops);
if (!images)
return log_oom();
r = image_discover(IMAGE_EXTENSION, arg_root, images);
if (r < 0)
return log_error_errno(r, "Failed to discover extension images: %m");
if ((arg_json_format_flags & JSON_FORMAT_OFF) && hashmap_isempty(images)) {
log_info("No OS extensions found.");
return 0;
}
t = table_new("name", "type", "path", "time");
if (!t)
return log_oom();
HASHMAP_FOREACH(img, images) {
r = table_add_many(
t,
TABLE_STRING, img->name,
TABLE_STRING, image_type_to_string(img->type),
TABLE_PATH, img->path,
TABLE_TIMESTAMP, img->mtime != 0 ? img->mtime : img->crtime);
if (r < 0)
return table_log_add_error(r);
}
(void) table_set_sort(t, (size_t) 0, (size_t) -1);
if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
(void) pager_open(arg_pager_flags);
r = table_print_json(t, stdout, arg_json_format_flags);
if (r < 0)
return table_log_print_error(r);
return 0;
}
static int verb_help(int argc, char **argv, void *userdata) {
_cleanup_free_ char *link = NULL; _cleanup_free_ char *link = NULL;
int r; int r;
@ -881,19 +722,17 @@ static int verb_help(int argc, char **argv, void *userdata) {
printf("%1$s [OPTIONS...] [DEVICE]\n" printf("%1$s [OPTIONS...] [DEVICE]\n"
"\n%5$sMerge extension images into /usr/ and /opt/ hierarchies.%6$s\n" "\n%5$sMerge extension images into /usr/ and /opt/ hierarchies.%6$s\n"
"\n%3$sCommands:%4$s\n" "\n%3$sCommands:%4$s\n"
" status Show current merge status (default)\n"
" merge Merge extensions into /usr/ and /opt/\n"
" unmerge Unmerge extensions from /usr/ and /opt/\n"
" refresh Unmerge/merge extensions again\n"
" list List installed extensions\n"
" -h --help Show this help\n" " -h --help Show this help\n"
" --version Show package version\n" " --version Show package version\n"
" -m --merge Merge extensions into /usr/ and /opt/\n"
" -u --unmerge Unmerge extensions from /usr/ and /opt/\n"
" -R --refresh Unmerge/merge extensions again\n"
" -l --list List all OS images\n"
"\n%3$sOptions:%4$s\n" "\n%3$sOptions:%4$s\n"
" --no-pager Do not pipe output into a pager\n" " --no-pager Do not pipe output into a pager\n"
" --root=PATH Operate relative to root path\n" " --root=PATH Operate relative to root path\n"
" --json=pretty|short|off\n" " --json=pretty|short|off\n"
" Generate JSON output\n" " Generate JSON output\n"
" --force Ignore version incompatibilities\n"
"\nSee the %2$s for details.\n" "\nSee the %2$s for details.\n"
, program_invocation_short_name , program_invocation_short_name
, link , link
@ -909,9 +748,12 @@ static int parse_argv(int argc, char *argv[]) {
enum { enum {
ARG_VERSION = 0x100, ARG_VERSION = 0x100,
ARG_NO_PAGER, ARG_NO_PAGER,
ARG_MERGE,
ARG_UNMERGE,
ARG_REFRESH,
ARG_LIST,
ARG_ROOT, ARG_ROOT,
ARG_JSON, ARG_JSON,
ARG_FORCE,
}; };
static const struct option options[] = { static const struct option options[] = {
@ -919,8 +761,11 @@ static int parse_argv(int argc, char *argv[]) {
{ "version", no_argument, NULL, ARG_VERSION }, { "version", no_argument, NULL, ARG_VERSION },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER }, { "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "root", required_argument, NULL, ARG_ROOT }, { "root", required_argument, NULL, ARG_ROOT },
{ "merge", no_argument, NULL, 'm' },
{ "unmerge", no_argument, NULL, 'u' },
{ "refresh", no_argument, NULL, 'R' },
{ "list", no_argument, NULL, 'l' },
{ "json", required_argument, NULL, ARG_JSON }, { "json", required_argument, NULL, ARG_JSON },
{ "force", no_argument, NULL, ARG_FORCE },
{} {}
}; };
@ -929,12 +774,12 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0); assert(argc >= 0);
assert(argv); assert(argv);
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) while ((c = getopt_long(argc, argv, "hmuRl", options, NULL)) >= 0)
switch (c) { switch (c) {
case 'h': case 'h':
return verb_help(argc, argv, NULL); return help();
case ARG_VERSION: case ARG_VERSION:
return version(); return version();
@ -943,6 +788,22 @@ static int parse_argv(int argc, char *argv[]) {
arg_pager_flags |= PAGER_DISABLE; arg_pager_flags |= PAGER_DISABLE;
break; break;
case 'm':
arg_action = ACTION_MERGE;
break;
case 'u':
arg_action = ACTION_UNMERGE;
break;
case 'R':
arg_action = ACTION_REFRESH;
break;
case 'l':
arg_action = ACTION_LIST;
break;
case ARG_ROOT: case ARG_ROOT:
r = parse_path_argument_and_warn(optarg, false, &arg_root); r = parse_path_argument_and_warn(optarg, false, &arg_root);
if (r < 0) if (r < 0)
@ -956,10 +817,6 @@ static int parse_argv(int argc, char *argv[]) {
break; break;
case ARG_FORCE:
arg_force = true;
break;
case '?': case '?':
return -EINVAL; return -EINVAL;
@ -967,6 +824,10 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option"); assert_not_reached("Unhandled option");
} }
if (argc - optind > 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Unexpected argument.");
return 1; return 1;
} }
@ -1010,25 +871,13 @@ static int parse_env(void) {
return 0; return 0;
} }
static int sysext_main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
{ "merge", VERB_ANY, 1, 0, verb_merge },
{ "unmerge", VERB_ANY, 1, 0, verb_unmerge },
{ "refresh", VERB_ANY, 1, 0, verb_refresh },
{ "list", VERB_ANY, 1, 0, verb_list },
{ "help", VERB_ANY, 1, 0, verb_help },
{}
};
return dispatch_verb(argc, argv, verbs, NULL);
}
static int run(int argc, char *argv[]) { static int run(int argc, char *argv[]) {
_cleanup_(hashmap_freep) Hashmap *images = NULL;
int r; int r;
log_setup_cli(); log_show_color(true);
log_parse_environment();
log_open();
r = parse_argv(argc, argv); r = parse_argv(argc, argv);
if (r <= 0) if (r <= 0)
@ -1044,7 +893,126 @@ static int run(int argc, char *argv[]) {
return log_oom(); return log_oom();
} }
return sysext_main(argc, argv); /* Given that things deep down in the child process will fail, let's catch the no-privilege issue
* early on */
if (!IN_SET(arg_action, ACTION_STATUS, ACTION_LIST) && !have_effective_cap(CAP_SYS_ADMIN))
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
if (arg_action == ACTION_STATUS)
return status();
if (arg_action == ACTION_UNMERGE)
return unmerge();
images = hashmap_new(&image_hash_ops);
if (!images)
return log_oom();
r = image_discover(IMAGE_EXTENSION, arg_root, images);
if (r < 0)
return log_error_errno(r, "Failed to discover extension images: %m");
switch (arg_action) {
case ACTION_LIST: {
_cleanup_(table_unrefp) Table *t = NULL;
Image *img;
if ((arg_json_format_flags & JSON_FORMAT_OFF) && hashmap_isempty(images)) {
log_info("No OS extensions found.");
return 0;
}
t = table_new("name", "type", "path", "time");
if (!t)
return log_oom();
HASHMAP_FOREACH(img, images) {
r = table_add_many(
t,
TABLE_STRING, img->name,
TABLE_STRING, image_type_to_string(img->type),
TABLE_PATH, img->path,
TABLE_TIMESTAMP, img->mtime != 0 ? img->mtime : img->crtime);
if (r < 0)
return table_log_add_error(r);
}
(void) table_set_sort(t, (size_t) 0, (size_t) -1);
if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
(void) pager_open(arg_pager_flags);
r = table_print_json(t, stdout, arg_json_format_flags);
if (r < 0)
return table_log_print_error(r);
r = 0;
break;
}
case ACTION_MERGE: {
char **p;
/* In merge mode fail if things are already merged. (In --refresh mode below we'll unmerge if
* we find things are already merged...) */
STRV_FOREACH(p, arg_hierarchies) {
_cleanup_free_ char *resolved = NULL;
r = chase_symlinks(*p, arg_root, CHASE_PREFIX_ROOT, &resolved, NULL);
if (r == -ENOENT) {
log_debug_errno(r, "Hierarchy '%s%s' does not exist, ignoring.", strempty(arg_root), *p);
continue;
}
if (r < 0)
return log_error_errno(r, "Failed to resolve path to hierarchy '%s%s': %m", strempty(arg_root), *p);
r = is_our_mount_point(resolved);
if (r < 0)
return r;
if (r > 0)
return log_error_errno(SYNTHETIC_ERRNO(EBUSY),
"Hierarchy '%s' is already merged.", *p);
}
r = merge(images);
break;
}
case ACTION_REFRESH:
r = merge(images); /* Returns > 0 if it did something, i.e. a new overlayfs is mounted
* now. When it does so it implicitly unmounts any overlayfs placed there
* before. Returns == 0 if it did nothing, i.e. no extension images
* found. In this case the old overlayfs remains in place if there was
* one. */
if (r < 0)
return r;
if (r == 0) /* No images found? Then unmerge. The goal of --refresh is after all that after
* having called there's a guarantee that the merge status matches the installed
* extensions. */
r = unmerge();
/* Net result here is that:
*
* 1. If an overlayfs was mounted before and no extensions exist anymore, we'll have unmerged
* things.
*
* 2. If an overlayfs was mounted before, and there are still extensions installed' we'll
* have unmerged and then merged things again.
*
* 3. If an overlayfs so far wasn't mounted, and there are extensions installed, we'll have
* it mounted now.
*
* 4. If there was no overlayfs mount so far, and no extensions installed, we implement a
* NOP.
*/
break;
default:
assert_not_reached("Uneexpected action");
}
return r;
} }
DEFINE_MAIN_FUNCTION(run); DEFINE_MAIN_FUNCTION(run);

View File

@ -174,7 +174,6 @@ static void test_protect_kernel_logs(void) {
NULL, NULL,
NULL, NULL,
NULL, NULL,
NULL,
0, 0,
NULL); NULL);
assert_se(r == 0); assert_se(r == 0);

View File

@ -89,7 +89,6 @@ int main(int argc, char *argv[]) {
NULL, NULL,
NULL, NULL,
NULL, NULL,
NULL,
0, 0,
NULL); NULL);
if (r < 0) { if (r < 0) {

View File

@ -2147,17 +2147,19 @@ static int udev_rule_apply_token_to_event(
if (IN_SET(token->op, OP_ASSIGN, OP_ASSIGN_FINAL)) if (IN_SET(token->op, OP_ASSIGN, OP_ASSIGN_FINAL))
ordered_hashmap_clear_free_key(event->run_list); ordered_hashmap_clear_free_key(event->run_list);
r = ordered_hashmap_ensure_allocated(&event->run_list, NULL);
if (r < 0)
return log_oom();
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
cmd = strdup(buf); cmd = strdup(buf);
if (!cmd) if (!cmd)
return log_oom(); return log_oom();
r = ordered_hashmap_ensure_put(&event->run_list, NULL, cmd, token->data); r = ordered_hashmap_put(event->run_list, cmd, token->data);
if (r == -ENOMEM)
return log_oom();
if (r < 0) if (r < 0)
return log_rule_error_errno(dev, rules, r, "Failed to store command '%s': %m", cmd); return log_oom();
TAKE_PTR(cmd); TAKE_PTR(cmd);

View File

@ -24,8 +24,8 @@ ConditionDirectoryNotEmpty=|/usr/lib/extensions
[Service] [Service]
Type=oneshot Type=oneshot
RemainAfterExit=yes RemainAfterExit=yes
ExecStart=systemd-sysext merge ExecStart=systemd-sysext --merge
ExecStop=systemd-sysext unmerge ExecStop=systemd-sysext --unmerge
[Install] [Install]
WantedBy=sysinit.target WantedBy=sysinit.target