1
0
mirror of https://github.com/systemd/systemd synced 2026-04-24 07:55:12 +02:00

Compare commits

..

25 Commits

Author SHA1 Message Date
Luca Boccassi
996e6d0d89
nspawn: free D-Bus error before fallback (#39131)
Otherwise the next call fails:

Got message type=error sender=:1.5 destination=:1.470 path=n/a 
    interface=n/a member=n/a cookie=151 reply_cookie=3 signature=s
    error-name=org.freedesktop.DBus.Error.UnknownMethod
    error-message=Unknown method CreateMachineEx or interface
    org.freedesktop.machine1.Manager. Assertion
'!bus_error_is_dirty(reterr_error)' failed at
    src/libsystemd/sd-bus/sd-bus.c:2396, function sd_bus_call(). Aborting.

Follow-up for d80af3b97b94e450a002ddac13874a953f0eab3e
2025-09-26 14:00:48 +09:00
Yu Watanabe
0f44a6c64a
core: assorted fixes and cleanups for cgroup (#39094) 2025-09-26 13:56:25 +09:00
Yu Watanabe
10b2d19219
timer: fix unexpected triggering of service immediately after restart of a timer (#38868)
Fixes: #31231
Fixes: #35805
2025-09-26 09:51:36 +09:00
Yu Watanabe
e8a5cda471 core/bpf-firewall: replace unnecessary unit_setup_cgroup_runtime() with unit_get_cgroup_runtime()
Except for the test, bpf_firewall_compile() is only called by the following:
  cgroup_context_apply() -> cgroup_apply_firewall() -> bpf_firewall_compile()
and in the early stage of cgroup_context_apply(), it checks if the cgroup
runtime exists. Hence, it is not necessary to try to allocate the
runtime in bpf_firewall_compile().
2025-09-26 09:28:12 +09:00
Yu Watanabe
13b0e7fc6d core/bpf-firewall: make failures in loading custom BPF program not critical
All other resource control features work as 'best-effort', and failures
in applying them are handled gracefully. However, unlike the other features,
we tested if the BPF programs can be loaded and refuse execution on failure.

Moreover, the previous behavior of testing loading BPF programs had
inconsistency: the test was silently skipped if the cgroup for the unit does
not exist yet, but tested when the cgroup already exists.

Let's not handle failures in loading custom BPF programs as critical, but
gracefully ignore them, like we do for the other resource control features.

Follow-up for fab347489fcfafbc8367c86afc637ce1b81ae59e.
2025-09-26 09:28:12 +09:00
Yu Watanabe
8b4ee3d68d core/unit: fail earlier before spawning executor when we failed to realize cgroup
Before 23ac08115af83e3a0a937fa207fc52511aba2ffa, even if we failed to
create the cgroup for a unit, a cgroup runtime object for the cgroup is
created with the cgroup path. Hence, the creation of cgroup is failed,
execution of the unit will fail in posix_spawn_wrapper() and logged
something like the following:
```
systemd[1]: testservice.service: Failed to create cgroup /testslice.slice/testservice.service: Cannot allocate memory
systemd[1]: testservice.service: Failed to spawn executor: No such file or directory
systemd[1]: testservice.service: Failed to spawn 'start' task: No such file or directory
systemd[1]: testservice.service: Failed with result 'resources'.
systemd[1]: Failed to start testservice.service.
```

However, after the commit, when we failed to create the cgroup, a cgroup
runtime object is not created, hence NULL will be assigned to
ExecParameters.cgroup_path in unit_set_exec_params().
Hence, the unit process will be invoked in the init.scope.
```
systemd[1]: testservice.service: Failed to create cgroup /testslice.slice/testservice.service: Cannot allocate memory
systemd[1]: Starting testservice.service...
cat[1094]: 0::/init.scope
systemd[1]: testservice.service: Deactivated successfully.
systemd[1]: Finished testservice.service.
```
where the test service calls 'cat /proc/self/cgroup'.

To fix the issue, let's fail earlier when we failed to create cgroup.

Follow-up for 23ac08115af83e3a0a937fa207fc52511aba2ffa (v258).
2025-09-26 09:28:12 +09:00
Lennart Poettering
b3243f4bee libmount: make dlopen() dependency 2025-09-26 08:29:37 +09:00
Yu Watanabe
b97b8fd4e3
machined: introduce per-user instance (#39123)
This is the first part of #38728, just the machined stuff, no the
importd stuff.

This definitely makes sense of its own, hence let's get this in first.

The original PR contains a tescase that tests machined + importd in
combination. This PR here hence is without a testcase, but it's there,
just in the other PR.

This looks large and is large, but do note that much of the machined
changes are very repetitive: they conditionalize PK checks to the system
version, as PK doesn't make sense in the use rversion.
2025-09-26 08:16:18 +09:00
Yu Watanabe
a925cea9f2
core/cgroup: make sure deserialized accounting data is not voided (#39130) 2025-09-26 07:54:48 +09:00
Yu Watanabe
22fc89f54d man: fix typo
Follow-up for 73ee723aa77bfb9f9988b80228b646d65d1770b2.
2025-09-26 07:24:17 +09:00
Yu Watanabe
5c60d3011f integration tests: do not adjust log level in the test script
We passes log level through kernel command line. It is not necessary to
set to debug level at the beginning, and set to info at the end.
This is important when a test has several subtests. If a subtest sets
log level to info at the end, then subsequent tests may not generate any
useful logs.
2025-09-26 07:20:04 +09:00
Matteo Croce
ba8801a076 libseccomp: fix build error
When HAVE_SECCOMP is not set, a build error happens:

../src/analyze/analyze-security.c: In function ‘get_security_info’:
../src/analyze/analyze-security.c:2449:13: error: unused variable ‘r’ [-Werror=unused-variable]
 2449 |         int r;
      |             ^
cc1: some warnings being treated as errors

Fix it by removing the sometimes unused variable.
2025-09-26 07:19:29 +09:00
Mike Yuan
055a5a236c
core/cgroup: make sure deserialized accounting data is not voided
Currently, cgroup_path is (de-)serialized after all the cached
accounting data. This is bogus though, since unit_set_cgroup_path()
destroys the CGroupRuntime object and starts afresh, discarding
all deserialized values. This matters especially for IP accounting,
whose BPF maps get recreated on reload/reexec and the previous values
are exclusively retrievable from deserialization. Let's hence swap things
around and serialize cgroup_path first, accounting data only afterwards.
2025-09-25 22:55:14 +02:00
Mike Yuan
e99f741cdf
core/cgroup: realign macro line continuation 2025-09-25 22:55:14 +02:00
Lennart Poettering
d9c5566c0f machined: make image locking runtime scope aware, too
We cannot create an image lock in /run if we are unpriv, hence create it
in $XDG_RUNTIME_DIR instead.
2025-09-25 22:43:59 +02:00
Lennart Poettering
1966baf2d6 machinectl: add support for user-scoped operation 2025-09-25 22:43:59 +02:00
Lennart Poettering
f5fa86f9f8 machined: allow running in --user mode 2025-09-25 22:43:59 +02:00
Lennart Poettering
e1b3319b6c discover-image: support runtime scope also for .nspawn settings files and the pool dir
discover-image.[ch] largely already supports per-scope operations, let's
extend this however to also cover finding .nspawn settings files and
managing the pool dir.
2025-09-25 22:43:59 +02:00
Lennart Poettering
b7f6bdbbd3 service-util: add generic parser for runtime scope 2025-09-25 22:43:59 +02:00
Lennart Poettering
0011ecd6fa path-lookup: add runtime_directory_generic() helper 2025-09-25 22:43:59 +02:00
Lennart Poettering
abf518a8dc runtime-scope: add runtime_scope_to_socket_mode() helper 2025-09-25 22:43:59 +02:00
Lukas Nykryn
3fc44a0f68 timer: don't run service immediately after restart of a timer
When a timer is restarted, don't reset the last_trigger field.
This prevents the timer from triggering immediately.

Fixes: #31231
2025-09-25 12:55:04 +02:00
Frantisek Sumsal
f4c3c107d9 test: check the next elapse timer timestamp after deserialization
When deserializing a serialized timer unit with RandomizedDelaySec= set,
systemd should use the last inactive exit timestamp instead of current
realtime to calculate the new next elapse, so the timer unit actually
runs in the given calendar window.

Provides coverage for:
  - https://github.com/systemd/systemd/issues/18678
  - https://github.com/systemd/systemd/pull/27752
2025-09-25 12:55:04 +02:00
Frantisek Sumsal
5730a400fd test: restarting elapsed timer shouldn't trigger the corresponding service
Provides coverage for:
  - https://github.com/systemd/systemd/issues/31231
  - https://github.com/systemd/systemd/issues/35805
2025-09-24 09:52:36 +02:00
Frantisek Sumsal
953c347fb6 test: rename TEST-53-ISSUE-16347 to TEST-53-TIMER
And split the existing test into a separate subtest.
2025-09-24 09:52:36 +02:00
120 changed files with 1536 additions and 876 deletions

View File

@ -347,7 +347,7 @@
<varlistentry>
<term><option>--chrome=</option></term>
<listitem><para>Takes a boolean argument. By default the initial setup scren will show reverse color
<listitem><para>Takes a boolean argument. By default the initial setup screen will show reverse color
"chrome" bars at the top and and the bottom of the terminal screen, which may be disabled by setting
this option to false.</para>

View File

@ -1149,7 +1149,7 @@ NFTSet=cgroup:inet:filter:my_service user:inet:filter:serviceuser
one more restricted, depending on the use case.</para>
<para>Note that these settings might not be supported on some systems (for example if eBPF control group
support is not enabled in the underlying kernel or container manager). These settings will fail the service in
support is not enabled in the underlying kernel or container manager). These settings will have no effect in
that case. If compatibility with such systems is desired it is hence recommended to attach your filter manually
(requires <varname>Delegate=</varname><constant>yes</constant>) instead of using this setting.</para>

View File

@ -1122,6 +1122,7 @@ libmount = dependency('mount',
version : fuzzer_build ? '>= 0' : '>= 2.30',
disabler : true,
required : get_option('libmount'))
libmount_cflags = libmount.partial_dependency(includes: true, compile_args: true)
libfdisk = dependency('fdisk',
version : '>= 2.32',

View File

@ -2446,8 +2446,6 @@ static int analyze_security_one(sd_bus *bus,
/* Refactoring SecurityInfo so that it can make use of existing struct variables instead of reading from dbus */
static int get_security_info(Unit *u, ExecContext *c, CGroupContext *g, SecurityInfo **ret_info) {
int r;
assert(ret_info);
_cleanup_(security_info_freep) SecurityInfo *info = security_info_new();
@ -2577,8 +2575,7 @@ static int get_security_info(Unit *u, ExecContext *c, CGroupContext *g, Security
info->_umask = c->umask;
#if HAVE_SECCOMP
r = dlopen_libseccomp();
if (r >= 0) {
if (dlopen_libseccomp() >= 0) {
SET_FOREACH(key, c->syscall_archs) {
const char *name;

View File

@ -15,3 +15,20 @@ const char* runtime_scope_to_string(RuntimeScope scope) _const_;
RuntimeScope runtime_scope_from_string(const char *s) _const_;
const char* runtime_scope_cmdline_option_to_string(RuntimeScope scope) _const_;
static inline mode_t runtime_scope_to_socket_mode(RuntimeScope scope) {
/* Returns the right socket mode to use for binding AF_UNIX sockets intended for the specified
* scope. If system mode is selected the whole system can connect to it, if user mode is selected
* only the user can connect to it. */
switch (scope) {
case RUNTIME_SCOPE_SYSTEM:
return 0666;
case RUNTIME_SCOPE_USER:
return 0600;
default:
return MODE_INVALID;
}
}

View File

@ -547,9 +547,9 @@ int bpf_firewall_compile(Unit *u) {
if (!cc)
return -EINVAL;
crt = unit_setup_cgroup_runtime(u);
crt = unit_get_cgroup_runtime(u);
if (!crt)
return -ENOMEM;
return -ESTALE;
if (bpf_program_supported() <= 0)
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),

View File

@ -2204,12 +2204,6 @@ int unit_attach_pids_to_cgroup(Unit *u, Set *pids, const char *suffix_path) {
if (set_isempty(pids))
return 0;
/* Load any custom firewall BPF programs here once to test if they are existing and actually loadable.
* Fail here early since later errors in the call chain unit_realize_cgroup to cgroup_context_apply are ignored. */
r = bpf_firewall_load_custom(u);
if (r < 0)
return r;
r = unit_realize_cgroup(u);
if (r < 0)
return r;
@ -4283,6 +4277,15 @@ int cgroup_runtime_serialize(Unit *u, FILE *f, FDSet *fds) {
if (!crt)
return 0;
if (crt->cgroup_path)
(void) serialize_item(f, "cgroup", crt->cgroup_path);
if (crt->cgroup_id != 0)
(void) serialize_item_format(f, "cgroup-id", "%" PRIu64, crt->cgroup_id);
(void) serialize_cgroup_mask(f, "cgroup-realized-mask", crt->cgroup_realized_mask);
(void) serialize_cgroup_mask(f, "cgroup-enabled-mask", crt->cgroup_enabled_mask);
(void) serialize_cgroup_mask(f, "cgroup-invalidated-mask", crt->cgroup_invalidated_mask);
(void) serialize_item_format(f, "cpu-usage-base", "%" PRIu64, crt->cpu_usage_base);
if (crt->cpu_usage_last != NSEC_INFINITY)
(void) serialize_item_format(f, "cpu-usage-last", "%" PRIu64, crt->cpu_usage_last);
@ -4316,15 +4319,6 @@ int cgroup_runtime_serialize(Unit *u, FILE *f, FDSet *fds) {
(void) serialize_item_format(f, io_accounting_metric_field_last_to_string(im), "%" PRIu64, crt->io_accounting_last[im]);
}
if (crt->cgroup_path)
(void) serialize_item(f, "cgroup", crt->cgroup_path);
if (crt->cgroup_id != 0)
(void) serialize_item_format(f, "cgroup-id", "%" PRIu64, crt->cgroup_id);
(void) serialize_cgroup_mask(f, "cgroup-realized-mask", crt->cgroup_realized_mask);
(void) serialize_cgroup_mask(f, "cgroup-enabled-mask", crt->cgroup_enabled_mask);
(void) serialize_cgroup_mask(f, "cgroup-invalidated-mask", crt->cgroup_invalidated_mask);
(void) bpf_socket_bind_serialize(u, f, fds);
(void) bpf_program_serialize_attachment(f, fds, "ip-bpf-ingress-installed", crt->ip_bpf_ingress_installed);

View File

@ -134,7 +134,7 @@ libcore_static = static_library(
dependencies : [libaudit_cflags,
libdl,
libm,
libmount,
libmount_cflags,
librt,
libseccomp_cflags,
libselinux,
@ -222,7 +222,7 @@ executables += [
libcore,
libshared
],
'dependencies' : libmount,
'dependencies' : libmount_cflags,
},
fuzz_template + {
'sources' : files('fuzz-manager-serialize.c'),

View File

@ -1952,16 +1952,16 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
struct libmnt_fs *fs;
const char *device, *path, *options, *fstype;
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r == 1)
break;
if (r < 0)
return log_error_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m");
device = mnt_fs_get_source(fs);
path = mnt_fs_get_target(fs);
options = mnt_fs_get_options(fs);
fstype = mnt_fs_get_fstype(fs);
device = sym_mnt_fs_get_source(fs);
path = sym_mnt_fs_get_target(fs);
options = sym_mnt_fs_get_options(fs);
fstype = sym_mnt_fs_get_fstype(fs);
if (!device || !path)
continue;
@ -1983,8 +1983,10 @@ static void mount_shutdown(Manager *m) {
m->mount_event_source = sd_event_source_disable_unref(m->mount_event_source);
mnt_unref_monitor(m->mount_monitor);
if (m->mount_monitor) {
sym_mnt_unref_monitor(m->mount_monitor);
m->mount_monitor = NULL;
}
}
static void mount_handoff_timestamp(
@ -2075,33 +2077,39 @@ static void mount_enumerate(Manager *m) {
assert(m);
mnt_init_debug(0);
r = dlopen_libmount();
if (r < 0) {
log_error_errno(r, "Cannot enumerate mounts, as libmount is not available: %m");
goto fail;
}
sym_mnt_init_debug(0);
if (!m->mount_monitor) {
usec_t mount_rate_limit_interval = 1 * USEC_PER_SEC;
unsigned mount_rate_limit_burst = 5;
int fd;
m->mount_monitor = mnt_new_monitor();
m->mount_monitor = sym_mnt_new_monitor();
if (!m->mount_monitor) {
log_oom();
goto fail;
}
r = mnt_monitor_enable_kernel(m->mount_monitor, 1);
r = sym_mnt_monitor_enable_kernel(m->mount_monitor, 1);
if (r < 0) {
log_error_errno(r, "Failed to enable watching of kernel mount events: %m");
goto fail;
}
r = mnt_monitor_enable_userspace(m->mount_monitor, 1, NULL);
r = sym_mnt_monitor_enable_userspace(m->mount_monitor, 1, NULL);
if (r < 0) {
log_error_errno(r, "Failed to enable watching of userspace mount events: %m");
goto fail;
}
/* mnt_unref_monitor() will close the fd */
fd = r = mnt_monitor_get_fd(m->mount_monitor);
fd = r = sym_mnt_monitor_get_fd(m->mount_monitor);
if (r < 0) {
log_error_errno(r, "Failed to acquire watch file descriptor: %m");
goto fail;
@ -2165,6 +2173,9 @@ static int drain_libmount(Manager *m) {
assert(m);
if (!m->mount_monitor)
return false;
/* Drain all events and verify that the event is valid.
*
* Note that libmount also monitors /run/mount mkdir if the directory does not exist yet. The mkdir
@ -2172,7 +2183,7 @@ static int drain_libmount(Manager *m) {
*
* error: r < 0; valid: r == 0, false positive: r == 1 */
do {
r = mnt_monitor_next_change(m->mount_monitor, NULL, NULL);
r = sym_mnt_monitor_next_change(m->mount_monitor, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to drain libmount events: %m");
if (r == 0)

View File

@ -664,8 +664,6 @@ static int timer_start(Unit *u) {
if (r < 0)
return r;
t->last_trigger = DUAL_TIMESTAMP_NULL;
/* Reenable all timers that depend on unit activation time */
LIST_FOREACH(value, v, t->values)
if (v->base == TIMER_ACTIVE)

View File

@ -5559,11 +5559,11 @@ int unit_fork_helper_process(Unit *u, const char *name, bool into_cgroup, PidRef
* with the child's PID. */
if (into_cgroup) {
(void) unit_realize_cgroup(u);
r = unit_realize_cgroup(u);
if (r < 0)
return r;
crt = unit_setup_cgroup_runtime(u);
if (!crt)
return -ENOMEM;
crt = unit_get_cgroup_runtime(u);
}
r = safe_fork(name, FORK_REOPEN_LOG|FORK_DEATHSIG_SIGTERM, &pid);
@ -6005,15 +6005,11 @@ int unit_prepare_exec(Unit *u) {
assert(u);
/* Load any custom firewall BPF programs here once to test if they are existing and actually loadable.
* Fail here early since later errors in the call chain unit_realize_cgroup to cgroup_context_apply are ignored. */
r = bpf_firewall_load_custom(u);
if (r < 0)
return r;
/* Prepares everything so that we can fork of a process for this unit */
(void) unit_realize_cgroup(u);
r = unit_realize_cgroup(u);
if (r < 0)
return r;
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (crt && crt->reset_accounting) {

View File

@ -191,19 +191,23 @@ static int is_tmpfs_with_noswap(dev_t devno) {
_cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
int r;
table = mnt_new_table();
if (!table)
return -ENOMEM;
r = mnt_table_parse_mtab(table, /* filename= */ NULL);
r = dlopen_libmount();
if (r < 0)
return r;
struct libmnt_fs *fs = mnt_table_find_devno(table, devno, MNT_ITER_FORWARD);
table = sym_mnt_new_table();
if (!table)
return -ENOMEM;
r = sym_mnt_table_parse_mtab(table, /* filename= */ NULL);
if (r < 0)
return r;
struct libmnt_fs *fs = sym_mnt_table_find_devno(table, devno, MNT_ITER_FORWARD);
if (!fs)
return -ENODEV;
r = mnt_fs_get_option(fs, "noswap", /* value= */ NULL, /* valuesz= */ NULL);
r = sym_mnt_fs_get_option(fs, "noswap", /* value= */ NULL, /* valuesz= */ NULL);
if (r < 0)
return r;

View File

@ -6,7 +6,7 @@ executables += [
'public' : true,
'sources' : files('creds.c'),
'dependencies' : [
libmount,
libmount_cflags,
libopenssl,
threads,
],

View File

@ -753,12 +753,12 @@ static char* disk_mount_point(const char *label) {
for (;;) {
struct libmnt_fs *fs;
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r != 0)
return NULL;
if (path_equal(mnt_fs_get_source(fs), device)) {
const char *target = mnt_fs_get_target(fs);
if (path_equal(sym_mnt_fs_get_source(fs), device)) {
const char *target = sym_mnt_fs_get_target(fs);
if (target)
return strdup(target);
}

View File

@ -19,7 +19,7 @@ executables += [
'sources' : systemd_cryptsetup_sources,
'dependencies' : [
libcryptsetup,
libmount,
libmount_cflags,
libopenssl,
libp11kit_cflags,
],

View File

@ -1048,15 +1048,19 @@ static int parse_fstab(bool prefix_sysroot) {
for (;;) {
struct libmnt_fs *fs;
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r < 0)
return log_error_errno(r, "Failed to get next entry from '%s': %m", fstab);
if (r > 0) /* EOF */
return ret;
r = parse_fstab_one(fstab,
mnt_fs_get_source(fs), mnt_fs_get_target(fs),
mnt_fs_get_fstype(fs), mnt_fs_get_options(fs), mnt_fs_get_passno(fs),
r = parse_fstab_one(
fstab,
sym_mnt_fs_get_source(fs),
sym_mnt_fs_get_target(fs),
sym_mnt_fs_get_fstype(fs),
sym_mnt_fs_get_options(fs),
sym_mnt_fs_get_passno(fs),
prefix_sysroot,
/* accept_root = */ false,
/* use_swap_enabled = */ true);
@ -1445,15 +1449,19 @@ static int add_mounts_from_creds(bool prefix_sysroot) {
for (;;) {
struct libmnt_fs *fs;
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r < 0)
return log_error_errno(r, "Failed to get next fstab entry from credential '%s': %m", cred);
if (r > 0) /* EOF */
return ret;
RET_GATHER(ret, parse_fstab_one("/run/credentials",
mnt_fs_get_source(fs), mnt_fs_get_target(fs),
mnt_fs_get_fstype(fs), mnt_fs_get_options(fs), mnt_fs_get_passno(fs),
RET_GATHER(ret, parse_fstab_one(
"/run/credentials",
sym_mnt_fs_get_source(fs),
sym_mnt_fs_get_target(fs),
sym_mnt_fs_get_fstype(fs),
sym_mnt_fs_get_options(fs),
sym_mnt_fs_get_passno(fs),
prefix_sysroot,
/* accept_root = */ true,
/* use_swap_enabled = */ true));

View File

@ -4,7 +4,7 @@ executables += [
generator_template + {
'name' : 'systemd-fstab-generator',
'sources' : files('fstab-generator.c'),
'dependencies' : libmount,
'dependencies' : libmount_cflags,
},
]

View File

@ -25,6 +25,7 @@ static int run(int argc, char *argv[]) {
"A service to create, remove, change or inspect home areas.",
BUS_IMPLEMENTATIONS(&manager_object,
&log_control_object),
/* runtime_scope= */ NULL,
argc, argv);
if (r <= 0)
return r;

View File

@ -1988,6 +1988,7 @@ static int run(int argc, char *argv[]) {
"Manage the system hostname and related metadata.",
BUS_IMPLEMENTATIONS(&manager_object,
&log_control_object),
/* runtime_scope= */ NULL,
argc, argv);
if (r <= 0)
return r;

View File

@ -2019,6 +2019,7 @@ static int run(int argc, char *argv[]) {
"VM and container image import and export service.",
BUS_IMPLEMENTATIONS(&manager_object,
&log_control_object),
/* runtime_scope= */ NULL,
argc, argv);
if (r <= 0)
return r;

View File

@ -33,32 +33,54 @@ int user_search_dirs(const char *suffix, char ***ret_config_dirs, char ***ret_da
return 0;
}
int runtime_directory(RuntimeScope scope, const char *suffix, char **ret) {
int runtime_directory_generic(RuntimeScope scope, const char *suffix, char **ret) {
int r;
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER));
assert(suffix);
assert(ret);
/* Accept $RUNTIME_DIRECTORY as authoritative
/* This does not bother with $RUNTIME_DIRECTORY, and hence can be applied to get other service's
* runtime dir */
switch (scope) {
case RUNTIME_SCOPE_USER:
r = xdg_user_runtime_dir(suffix, ret);
if (r < 0)
return r;
break;
case RUNTIME_SCOPE_SYSTEM: {
char *d = path_join("/run", suffix);
if (!d)
return -ENOMEM;
*ret = d;
break;
}
default:
return -EINVAL;
}
return 0;
}
int runtime_directory(RuntimeScope scope, const char *fallback_suffix, char **ret) {
int r;
assert(ret);
/* Accept $RUNTIME_DIRECTORY as authoritative, i.e. only works for our service's own runtime dir.
*
* If it's missing, apply the suffix to /run/, or $XDG_RUNTIME_DIR if we are in a user runtime scope.
*
* Return value indicates whether the suffix was applied or not */
* Return value indicates whether the suffix was applied or not. */
const char *e = secure_getenv("RUNTIME_DIRECTORY");
if (e)
return strdup_to(ret, e);
if (scope == RUNTIME_SCOPE_USER) {
r = xdg_user_runtime_dir(suffix, ret);
r = runtime_directory_generic(scope, fallback_suffix, ret);
if (r < 0)
return r;
} else {
char *d = path_join("/run", suffix);
if (!d)
return -ENOMEM;
*ret = d;
}
return 1;
}

View File

@ -57,7 +57,8 @@ int lookup_paths_init_or_warn(LookupPaths *lp, RuntimeScope scope, LookupPathsFl
void lookup_paths_log(LookupPaths *p);
void lookup_paths_done(LookupPaths *p);
int runtime_directory(RuntimeScope scope, const char *suffix, char **ret);
int runtime_directory_generic(RuntimeScope scope, const char *suffix, char **ret);
int runtime_directory(RuntimeScope scope, const char *fallback_suffix, char **ret);
/* We don't treat /etc/xdg/systemd/ in these functions as the xdg base dir spec suggests because we assume
* that is a link to /etc/systemd/ anyway. */

View File

@ -635,6 +635,7 @@ static int run(int argc, char *argv[]) {
"Manage system locale settings and key mappings.",
BUS_IMPLEMENTATIONS(&manager_object,
&log_control_object),
/* runtime_scope= */ NULL,
argc, argv);
if (r <= 0)
return r;

View File

@ -1340,6 +1340,7 @@ static int run(int argc, char *argv[]) {
"Manager for user logins and devices and privileged operations.",
BUS_IMPLEMENTATIONS(&manager_object,
&log_control_object),
/* runtime_scope= */ NULL,
argc, argv);
if (r <= 0)
return r;

View File

@ -40,6 +40,7 @@ int bus_image_method_remove(
if (m->n_operations >= OPERATIONS_MAX)
return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
if (m->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"image", image->name,
"verb", "remove",
@ -56,6 +57,7 @@ int bus_image_method_remove(
return r;
if (r == 0)
return 1; /* Will call us back */
}
if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
@ -65,7 +67,7 @@ int bus_image_method_remove(
return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
if (r == 0) {
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
r = image_remove(image);
r = image_remove(image, m->runtime_scope);
report_errno_and_exit(errno_pipe_fd[1], r);
}
@ -101,6 +103,7 @@ int bus_image_method_rename(
if (!image_name_is_valid(new_name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
if (m->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"image", image->name,
"verb", "rename",
@ -118,6 +121,7 @@ int bus_image_method_rename(
return r;
if (r == 0)
return 1; /* Will call us back */
}
r = rename_image_and_update_cache(m, image, new_name);
if (r < 0)
@ -150,6 +154,7 @@ int bus_image_method_clone(
if (!image_name_is_valid(new_name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
if (m->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"image", image->name,
"verb", "clone",
@ -167,6 +172,7 @@ int bus_image_method_clone(
return r;
if (r == 0)
return 1; /* Will call us back */
}
if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
@ -199,7 +205,7 @@ int bus_image_method_mark_read_only(
sd_bus_error *error) {
Image *image = userdata;
Manager *m = image->userdata;
Manager *m = ASSERT_PTR(image->userdata);
int read_only, r;
assert(message);
@ -208,6 +214,7 @@ int bus_image_method_mark_read_only(
if (r < 0)
return r;
if (m->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"image", image->name,
"verb", "mark_read_only",
@ -225,8 +232,9 @@ int bus_image_method_mark_read_only(
return r;
if (r == 0)
return 1; /* Will call us back */
}
r = image_read_only(image, read_only);
r = image_read_only(image, read_only, m->runtime_scope);
if (r < 0)
return r;
@ -251,6 +259,7 @@ int bus_image_method_set_limit(
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
if (m->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"machine", image->name,
"verb", "set_limit",
@ -267,6 +276,7 @@ int bus_image_method_set_limit(
return r;
if (r == 0)
return 1; /* Will call us back */
}
r = image_set_limit(image, limit);
if (r < 0)
@ -280,11 +290,12 @@ int bus_image_method_get_hostname(
void *userdata,
sd_bus_error *error) {
Image *image = userdata;
Image *image = ASSERT_PTR(userdata);
Manager *m = ASSERT_PTR(image->userdata);
int r;
if (!image->metadata_valid) {
r = image_read_metadata(image, &image_policy_container);
r = image_read_metadata(image, &image_policy_container, m->runtime_scope);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
}
@ -298,11 +309,12 @@ int bus_image_method_get_machine_id(
sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Image *image = userdata;
Image *image = ASSERT_PTR(userdata);
Manager *m = ASSERT_PTR(image->userdata);
int r;
if (!image->metadata_valid) {
r = image_read_metadata(image, &image_policy_container);
r = image_read_metadata(image, &image_policy_container, m->runtime_scope);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
}
@ -326,11 +338,12 @@ int bus_image_method_get_machine_info(
void *userdata,
sd_bus_error *error) {
Image *image = userdata;
Image *image = ASSERT_PTR(userdata);
Manager *m = ASSERT_PTR(image->userdata);
int r;
if (!image->metadata_valid) {
r = image_read_metadata(image, &image_policy_container);
r = image_read_metadata(image, &image_policy_container, m->runtime_scope);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
}
@ -343,11 +356,12 @@ int bus_image_method_get_os_release(
void *userdata,
sd_bus_error *error) {
Image *image = userdata;
Image *image = ASSERT_PTR(userdata);
Manager *m = ASSERT_PTR(image->userdata);
int r;
if (!image->metadata_valid) {
r = image_read_metadata(image, &image_policy_container);
r = image_read_metadata(image, &image_policy_container, m->runtime_scope);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
}

View File

@ -66,15 +66,17 @@ int vl_method_update_image(sd_varlink *link, sd_json_variant *parameters, sd_var
if (r < 0)
return r;
if (manager->runtime_scope != RUNTIME_SCOPE_USER) {
r = varlink_verify_polkit_async(
link,
manager->bus,
manager->system_bus,
"org.freedesktop.machine1.manage-images",
(const char**) STRV_MAKE("image", image->name,
"verb", "update"),
&manager->polkit_registry);
if (r <= 0)
return r;
}
if (p.new_name) {
r = rename_image_and_update_cache(manager, image, p.new_name);
@ -83,7 +85,7 @@ int vl_method_update_image(sd_varlink *link, sd_json_variant *parameters, sd_var
}
if (p.read_only >= 0) {
r = image_read_only(image, p.read_only);
r = image_read_only(image, p.read_only, manager->runtime_scope);
if (r < 0)
RET_GATHER(ret, log_debug_errno(r, "Failed to toggle image read only, ignoring: %m"));
}
@ -139,9 +141,10 @@ int vl_method_clone_image(sd_varlink *link, sd_json_variant *parameters, sd_varl
if (r < 0)
return r;
if (manager->runtime_scope != RUNTIME_SCOPE_USER) {
r = varlink_verify_polkit_async(
link,
manager->bus,
manager->system_bus,
"org.freedesktop.machine1.manage-images",
(const char**) STRV_MAKE("image", image->name,
"verb", "clone",
@ -149,6 +152,7 @@ int vl_method_clone_image(sd_varlink *link, sd_json_variant *parameters, sd_varl
&manager->polkit_registry);
if (r <= 0)
return r;
}
if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
return log_debug_errno(errno, "Failed to open pipe: %m");
@ -207,15 +211,17 @@ int vl_method_remove_image(sd_varlink *link, sd_json_variant *parameters, sd_var
if (r < 0)
return r;
if (manager->runtime_scope != RUNTIME_SCOPE_USER) {
r = varlink_verify_polkit_async(
link,
manager->bus,
manager->system_bus,
"org.freedesktop.machine1.manage-images",
(const char**) STRV_MAKE("image", image->name,
"verb", "remove"),
&manager->polkit_registry);
if (r <= 0)
return r;
}
if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
return log_debug_errno(errno, "Failed to open pipe: %m");
@ -225,7 +231,7 @@ int vl_method_remove_image(sd_varlink *link, sd_json_variant *parameters, sd_var
return log_debug_errno(r, "Failed to fork: %m");
if (r == 0) {
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
r = image_remove(image);
r = image_remove(image, manager->runtime_scope);
report_errno_and_exit(errno_pipe_fd[1], r);
}
@ -262,21 +268,23 @@ int vl_method_set_pool_limit(sd_varlink *link, sd_json_variant *parameters, sd_v
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
return sd_varlink_error_invalid_parameter_name(link, "limit");
if (manager->runtime_scope != RUNTIME_SCOPE_USER) {
r = varlink_verify_polkit_async(
link,
manager->bus,
manager->system_bus,
"org.freedesktop.machine1.manage-images",
(const char**) STRV_MAKE("verb", "set_pool_limit"),
&manager->polkit_registry);
if (r <= 0)
return r;
}
/* Set up the machine directory if necessary */
r = setup_machine_directory(/* error = */ NULL, /* use_btrfs_subvol= */ true, /* use_btrfs_quota= */ true);
if (r < 0)
return r;
r = image_set_pool_limit(IMAGE_MACHINE, limit);
r = image_set_pool_limit(manager->runtime_scope, IMAGE_MACHINE, limit);
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return sd_varlink_error(link, VARLINK_ERROR_MACHINE_IMAGE_NOT_SUPPORTED, NULL);
if (r < 0)
@ -394,15 +402,17 @@ int vl_method_clean_pool(sd_varlink *link, sd_json_variant *parameters, sd_varli
if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
if (manager->runtime_scope != RUNTIME_SCOPE_USER) {
r = varlink_verify_polkit_async(
link,
manager->bus,
manager->system_bus,
"org.freedesktop.machine1.manage-images",
(const char**) STRV_MAKE("mode", image_clean_pool_mode_to_string(mode),
"verb", "clean_pool"),
&manager->polkit_registry);
if (r <= 0)
return r;
}
Operation *op;
r = image_clean_pool_operation(manager, mode, &op);

View File

@ -143,7 +143,7 @@ int image_clean_pool_operation(Manager *manager, ImageCleanPoolMode mode, Operat
if (mode == IMAGE_CLEAN_POOL_REMOVE_HIDDEN && !image_is_hidden(image))
continue;
r = image_remove(image);
r = image_remove(image, manager->runtime_scope);
if (r == -EBUSY) {
log_debug("Keeping image '%s' because it's currently used.", image->name);
continue;

View File

@ -55,6 +55,7 @@ int bus_machine_method_unregister(sd_bus_message *message, void *userdata, sd_bu
assert(message);
if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"machine", m->name,
"verb", "unregister",
@ -73,6 +74,7 @@ int bus_machine_method_unregister(sd_bus_message *message, void *userdata, sd_bu
return r;
if (r == 0)
return 1; /* Will call us back */
}
r = machine_finalize(m);
if (r < 0)
@ -87,6 +89,7 @@ int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus
assert(message);
if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"machine", m->name,
"verb", "terminate",
@ -105,6 +108,7 @@ int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus
return r;
if (r == 0)
return 1; /* Will call us back */
}
r = machine_stop(m);
if (r < 0)
@ -137,6 +141,7 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
if (!SIGNAL_VALID(signo))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"machine", m->name,
"verb", "kill",
@ -155,6 +160,7 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
return r;
if (r == 0)
return 1; /* Will call us back */
}
r = machine_kill(m, whom, signo);
if (r < 0)
@ -260,6 +266,7 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_
assert(message);
if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"machine", m->name,
NULL
@ -277,6 +284,7 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_
return r;
if (r == 0)
return 1; /* Will call us back */
}
master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name);
if (master < 0)
@ -302,6 +310,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
assert(message);
if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"machine", m->name,
"verb", "login",
@ -320,6 +329,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
return r;
if (r == 0)
return 1; /* Will call us back */
}
master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name);
if (master < 0)
@ -345,7 +355,6 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
_cleanup_free_ char *pty_name = NULL;
_cleanup_close_ int master = -EBADF;
_cleanup_strv_free_ char **env = NULL, **args_wire = NULL, **args = NULL;
_cleanup_free_ char *command_line = NULL;
Machine *m = ASSERT_PTR(userdata);
const char *user, *path;
int r;
@ -402,9 +411,11 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
if (!strv_env_is_valid(env))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
command_line = strv_join(args, " ");
if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) {
_cleanup_free_ char *command_line = strv_join(args, " ");
if (!command_line)
return -ENOMEM;
const char *details[] = {
"machine", m->name,
"user", user,
@ -425,6 +436,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
return r;
if (r == 0)
return 1; /* Will call us back */
}
master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name);
if (master < 0)
@ -470,6 +482,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
else if (!path_is_absolute(dest) || !path_is_normalized(dest))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized.");
if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"machine", m->name,
"verb", "bind",
@ -489,6 +502,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
return r;
if (r == 0)
return 1; /* Will call us back */
}
r = machine_get_uid_shift(m, &uid);
if (r < 0)
@ -556,6 +570,7 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
else if (!path_is_absolute(dest))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute.");
if (manager->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"machine", m->name,
"verb", "copy",
@ -575,6 +590,7 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
return r;
if (r == 0)
return 1; /* Will call us back */
}
copy_from = strstr(sd_bus_message_get_member(message), "CopyFrom");
@ -602,6 +618,7 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
assert(message);
if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"machine", m->name,
"verb", "open_root_directory",
@ -619,6 +636,7 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
return r;
if (r == 0)
return 1; /* Will call us back */
}
fd = machine_open_root_directory(m);
if (ERRNO_IS_NEG_NOT_SUPPORTED(fd))
@ -862,7 +880,7 @@ int machine_send_signal(Machine *m, bool new_machine) {
return -ENOMEM;
return sd_bus_emit_signal(
m->manager->bus,
m->manager->api_bus,
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
new_machine ? "MachineNew" : "MachineRemoved",

View File

@ -155,15 +155,17 @@ int vl_method_register(sd_varlink *link, sd_json_variant *parameters, sd_varlink
if (r != 0)
return r;
if (manager->runtime_scope != RUNTIME_SCOPE_USER) {
r = varlink_verify_polkit_async(
link,
manager->bus,
manager->system_bus,
machine->allocate_unit ? "org.freedesktop.machine1.create-machine" : "org.freedesktop.machine1.register-machine",
(const char**) STRV_MAKE("name", machine->name,
"class", machine_class_to_string(machine->class)),
&manager->polkit_registry);
if (r <= 0)
return r;
}
if (!pidref_is_set(&machine->leader)) {
r = varlink_get_peer_pidref(link, &machine->leader);
@ -306,9 +308,10 @@ int vl_method_unregister_internal(sd_varlink *link, sd_json_variant *parameters,
Manager *manager = ASSERT_PTR(machine->manager);
int r;
if (manager->runtime_scope != RUNTIME_SCOPE_USER) {
r = varlink_verify_polkit_async_full(
link,
manager->bus,
manager->system_bus,
"org.freedesktop.machine1.manage-machines",
(const char**) STRV_MAKE("name", machine->name,
"verb", "unregister"),
@ -317,6 +320,7 @@ int vl_method_unregister_internal(sd_varlink *link, sd_json_variant *parameters,
&manager->polkit_registry);
if (r <= 0)
return r;
}
r = machine_finalize(machine);
if (r < 0)
@ -330,9 +334,10 @@ int vl_method_terminate_internal(sd_varlink *link, sd_json_variant *parameters,
Manager *manager = ASSERT_PTR(machine->manager);
int r;
if (manager->runtime_scope != RUNTIME_SCOPE_USER) {
r = varlink_verify_polkit_async_full(
link,
manager->bus,
manager->system_bus,
"org.freedesktop.machine1.manage-machines",
(const char**) STRV_MAKE("name", machine->name,
"verb", "terminate"),
@ -341,6 +346,7 @@ int vl_method_terminate_internal(sd_varlink *link, sd_json_variant *parameters,
&manager->polkit_registry);
if (r <= 0)
return r;
}
r = machine_stop(machine);
if (r < 0)
@ -400,9 +406,10 @@ int vl_method_kill(sd_varlink *link, sd_json_variant *parameters, sd_varlink_met
return sd_varlink_error_invalid_parameter_name(link, "whom");
}
if (manager->runtime_scope != RUNTIME_SCOPE_USER) {
r = varlink_verify_polkit_async_full(
link,
manager->bus,
manager->system_bus,
"org.freedesktop.machine1.manage-machines",
(const char**) STRV_MAKE("name", machine->name,
"verb", "kill"),
@ -411,6 +418,7 @@ int vl_method_kill(sd_varlink *link, sd_json_variant *parameters, sd_varlink_met
&manager->polkit_registry);
if (r <= 0)
return r;
}
r = machine_kill(machine, whom, p.signo);
if (r < 0)
@ -509,8 +517,8 @@ int vl_method_open(sd_varlink *link, sd_json_variant *parameters, sd_varlink_met
};
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
_cleanup_free_ char *ptmx_name = NULL, *command_line = NULL;
_cleanup_strv_free_ char **polkit_details = NULL, **args = NULL;
const char *user = NULL, *path = NULL; /* gcc complains about uninitialized variables */
_cleanup_strv_free_ char **args = NULL;
Machine *machine;
int r, ptmx_fd_idx;
@ -543,10 +551,13 @@ int vl_method_open(sd_varlink *link, sd_json_variant *parameters, sd_varlink_met
if (r < 0)
return r;
if (manager->runtime_scope != RUNTIME_SCOPE_USER) {
_cleanup_strv_free_ char **polkit_details = NULL;
polkit_details = machine_open_polkit_details(p.mode, machine->name, user, path, command_line);
r = varlink_verify_polkit_async_full(
link,
manager->bus,
manager->system_bus,
machine_open_polkit_action(p.mode, machine->class),
(const char**) polkit_details,
machine->uid,
@ -554,6 +565,7 @@ int vl_method_open(sd_varlink *link, sd_json_variant *parameters, sd_varlink_met
&manager->polkit_registry);
if (r <= 0)
return r;
}
ptmx_fd = machine_openpt(machine, O_RDWR|O_NOCTTY|O_CLOEXEC, &ptmx_name);
if (ERRNO_IS_NEG_NOT_SUPPORTED(ptmx_fd))
@ -824,10 +836,11 @@ int vl_method_bind_mount(sd_varlink *link, sd_json_variant *parameters, sd_varli
if (machine->class != MACHINE_CONTAINER)
return sd_varlink_error(link, VARLINK_ERROR_MACHINE_NOT_SUPPORTED, NULL);
if (manager->runtime_scope != RUNTIME_SCOPE_USER) {
/* NB: For now not opened up to owner of machine without auth */
r = varlink_verify_polkit_async(
link,
manager->bus,
manager->system_bus,
"org.freedesktop.machine1.manage-machines",
(const char**) STRV_MAKE("name", machine->name,
"verb", "bind",
@ -836,6 +849,7 @@ int vl_method_bind_mount(sd_varlink *link, sd_json_variant *parameters, sd_varli
&manager->polkit_registry);
if (r <= 0)
return r;
}
r = machine_get_uid_shift(machine, &uid_shift);
if (r < 0)
@ -936,10 +950,11 @@ int vl_method_copy_internal(sd_varlink *link, sd_json_variant *parameters, sd_va
if (machine->class != MACHINE_CONTAINER)
return sd_varlink_error(link, VARLINK_ERROR_MACHINE_NOT_SUPPORTED, NULL);
if (manager->runtime_scope != RUNTIME_SCOPE_USER) {
/* NB: For now not opened up to owner of machine without auth */
r = varlink_verify_polkit_async(
link,
manager->bus,
manager->system_bus,
"org.freedesktop.machine1.manage-machines",
(const char**) STRV_MAKE("name", machine->name,
"verb", "copy",
@ -948,6 +963,7 @@ int vl_method_copy_internal(sd_varlink *link, sd_json_variant *parameters, sd_va
&manager->polkit_registry);
if (r <= 0)
return r;
}
Operation *op;
r = machine_copy_from_to_operation(manager, machine, host_path, container_path, copy_from, copy_flags, &op);
@ -967,15 +983,17 @@ int vl_method_open_root_directory_internal(sd_varlink *link, sd_json_variant *pa
int r;
/* NB: For now not opened up to owner of machine without auth */
if (manager->runtime_scope != RUNTIME_SCOPE_USER) {
r = varlink_verify_polkit_async(
link,
manager->bus,
manager->system_bus,
"org.freedesktop.machine1.manage-machines",
(const char**) STRV_MAKE("name", machine->name,
"verb", "open_root_directory"),
&manager->polkit_registry);
if (r <= 0)
return r;
}
fd = machine_open_root_directory(machine);
if (ERRNO_IS_NEG_NOT_SUPPORTED(fd))

View File

@ -88,7 +88,7 @@ int machine_link(Manager *manager, Machine *machine) {
return -EINVAL;
if (machine->class != MACHINE_HOST) {
char *temp = path_join("/run/systemd/machines", machine->name);
char *temp = path_join(manager->state_dir, machine->name);
if (!temp)
return -ENOMEM;
@ -170,14 +170,14 @@ int machine_save(Machine *m) {
_cleanup_(unlink_and_freep) char *sl = NULL; /* auto-unlink! */
if (m->unit && !m->subgroup) {
sl = strjoin("/run/systemd/machines/unit:", m->unit);
sl = strjoin(m->manager->state_dir, "/unit:", m->unit);
if (!sl)
return log_oom();
}
r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0, MKDIR_WARN_MODE);
r = mkdir_safe_label(m->manager->state_dir, 0755, 0, 0, MKDIR_WARN_MODE);
if (r < 0)
return log_error_errno(r, "Failed to create /run/systemd/machines/: %m");
return log_error_errno(r, "Failed to create '%s': %m", m->manager->state_dir);
_cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
@ -267,7 +267,7 @@ static void machine_unlink(Machine *m) {
assert(m);
if (m->unit && !m->subgroup) {
const char *sl = strjoina("/run/systemd/machines/unit:", m->unit);
const char *sl = strjoina(m->manager->state_dir, "/unit:", m->unit);
(void) unlink(sl);
}
@ -438,7 +438,7 @@ static int machine_start_scope(
return log_oom();
r = bus_message_new_method_call(
machine->manager->bus,
machine->manager->api_bus,
&m,
bus_systemd_mgr,
"StartTransientUnit");
@ -920,7 +920,7 @@ int machine_start_getty(Machine *m, const char *ptmx_name, sd_bus_error *error)
if (r < 0)
return log_debug_errno(r, "Failed to create DBus to machine: %m");
container_bus = allocated_bus ?: m->manager->bus;
container_bus = allocated_bus ?: m->manager->system_bus;
getty = strjoina("container-getty@", p, ".service");
r = bus_call_method(container_bus, bus_systemd_mgr, "StartUnit", error, /* ret_reply = */ NULL, "ss", getty, "replace");
@ -966,7 +966,7 @@ int machine_start_shell(
if (r < 0)
return log_debug_errno(r, "Failed to create DBus to machine: %m");
container_bus = allocated_bus ?: m->manager->bus;
container_bus = allocated_bus ?: m->manager->system_bus;
r = bus_message_new_method_call(container_bus, &tm, bus_systemd_mgr, "StartTransientUnit");
if (r < 0)
return r;

View File

@ -104,6 +104,7 @@ static const char* arg_format = NULL;
static const char *arg_uid = NULL;
static char **arg_setenv = NULL;
static unsigned arg_max_addresses = 1;
static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_setenv, strv_freep);
@ -2099,6 +2100,8 @@ static int help(int argc, char *argv[], void *userdata) {
" --no-ask-password Do not ask for system passwords\n"
" -H --host=[USER@]HOST Operate on remote host\n"
" -M --machine=CONTAINER Operate on local container\n"
" --system Connect to system machine manager\n"
" --user Connect to user machine manager\n"
" -p --property=NAME Show only properties by this name\n"
" --value When showing properties, only print the value\n"
" -P NAME Equivalent to --value --property=NAME\n"
@ -2152,6 +2155,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_FORMAT,
ARG_UID,
ARG_MAX_ADDRESSES,
ARG_SYSTEM,
ARG_USER,
};
static const struct option options[] = {
@ -2181,6 +2186,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "uid", required_argument, NULL, ARG_UID },
{ "setenv", required_argument, NULL, 'E' },
{ "max-addresses", required_argument, NULL, ARG_MAX_ADDRESSES },
{ "user", no_argument, NULL, ARG_USER },
{ "system", no_argument, NULL, ARG_SYSTEM },
{}
};
@ -2404,6 +2411,14 @@ static int parse_argv(int argc, char *argv[]) {
"Invalid number of addresses: %s", optarg);
break;
case ARG_USER:
arg_runtime_scope = RUNTIME_SCOPE_USER;
break;
case ARG_SYSTEM:
arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
break;
case '?':
return -EINVAL;
@ -2488,9 +2503,9 @@ static int run(int argc, char *argv[]) {
"list-transfers", "cancel-transfer"))
return chainload_importctl(argc, argv);
r = bus_connect_transport(arg_transport, arg_host, RUNTIME_SCOPE_SYSTEM, &bus);
r = bus_connect_transport(arg_transport, arg_host, arg_runtime_scope, &bus);
if (r < 0)
return bus_log_connect_error(r, arg_transport, RUNTIME_SCOPE_SYSTEM);
return bus_log_connect_error(r, arg_transport, arg_runtime_scope);
(void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);

View File

@ -278,6 +278,7 @@ static int machine_add_from_params(
return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Only root may register machines for other users");
}
if (manager->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"name", name,
"class", machine_class_to_string(c),
@ -294,6 +295,7 @@ static int machine_add_from_params(
return r;
if (r == 0)
return 0; /* Will call us back */
}
r = manager_add_machine(manager, name, &m);
if (r < 0)
@ -697,7 +699,18 @@ static int method_register_machine(sd_bus_message *message, void *userdata, sd_b
if (r == 0)
return 1; /* Will call us back */
switch (manager->runtime_scope) {
case RUNTIME_SCOPE_USER:
r = cg_pidref_get_user_unit_full(&m->leader, &m->unit, &m->subgroup);
break;
case RUNTIME_SCOPE_SYSTEM:
r = cg_pidref_get_unit_full(&m->leader, &m->unit, &m->subgroup);
break;
default:
assert_not_reached();
}
if (r < 0) {
r = sd_bus_error_set_errnof(error, r,
"Failed to determine unit of process "PID_FMT" : %m",
@ -983,6 +996,7 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err
else
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mode '%s'.", mm);
if (m->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"verb", "clean_pool",
"mode", mm,
@ -999,6 +1013,7 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err
return r;
if (r == 0)
return 1; /* Will call us back */
}
r = image_clean_pool_operation(m, mode, &operation);
if (r < 0)
@ -1022,6 +1037,7 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
if (m->runtime_scope != RUNTIME_SCOPE_USER) {
const char *details[] = {
"verb", "set_pool_limit",
NULL
@ -1037,13 +1053,14 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
return r;
if (r == 0)
return 1; /* Will call us back */
}
/* Set up the machine directory if necessary */
r = setup_machine_directory(error, /* use_btrfs_subvol= */ true, /* use_btrfs_quota= */ true);
if (r < 0)
return r;
r = image_set_pool_limit(IMAGE_MACHINE, limit);
r = image_set_pool_limit(m->runtime_scope, IMAGE_MACHINE, limit);
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
if (r < 0)
@ -1534,7 +1551,7 @@ int manager_unref_unit(
assert(m);
assert(unit);
return bus_call_method(m->bus, bus_systemd_mgr, "UnrefUnit", error, NULL, "s", unit);
return bus_call_method(m->api_bus, bus_systemd_mgr, "UnrefUnit", error, NULL, "s", unit);
}
int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
@ -1544,7 +1561,7 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c
assert(manager);
assert(unit);
r = bus_call_method(manager->bus, bus_systemd_mgr, "StopUnit", error, &reply, "ss", unit, "fail");
r = bus_call_method(manager->api_bus, bus_systemd_mgr, "StopUnit", error, &reply, "ss", unit, "fail");
if (r < 0) {
if (sd_bus_error_has_names(error, BUS_ERROR_NO_SUCH_UNIT,
BUS_ERROR_LOAD_FAILED)) {
@ -1582,9 +1599,9 @@ int manager_kill_unit(Manager *manager, const char *unit, const char *subgroup,
assert(unit);
if (empty_or_root(subgroup))
return bus_call_method(manager->bus, bus_systemd_mgr, "KillUnit", reterr_error, NULL, "ssi", unit, "all", signo);
return bus_call_method(manager->api_bus, bus_systemd_mgr, "KillUnit", reterr_error, NULL, "ssi", unit, "all", signo);
return bus_call_method(manager->bus, bus_systemd_mgr, "KillUnitSubgroup", reterr_error, NULL, "sssi", unit, "cgroup", subgroup, signo);
return bus_call_method(manager->api_bus, bus_systemd_mgr, "KillUnitSubgroup", reterr_error, NULL, "sssi", unit, "cgroup", subgroup, signo);
}
int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *reterr_error) {
@ -1602,7 +1619,7 @@ int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *ret
return -ENOMEM;
r = sd_bus_get_property(
manager->bus,
manager->api_bus,
"org.freedesktop.systemd1",
path,
"org.freedesktop.systemd1.Unit",
@ -1638,7 +1655,7 @@ int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *rete
assert(path);
r = sd_bus_get_property(
manager->bus,
manager->api_bus,
"org.freedesktop.systemd1",
path,
"org.freedesktop.systemd1.Job",

View File

@ -18,6 +18,7 @@
#include "machine-varlink.h"
#include "machined.h"
#include "machined-varlink.h"
#include "path-lookup.h"
#include "string-util.h"
#include "strv.h"
#include "user-util.h"
@ -614,14 +615,15 @@ static int vl_method_open_root_directory(sd_varlink *link, sd_json_variant *para
return lookup_machine_and_call_method(link, parameters, flags, userdata, vl_method_open_root_directory_internal);
}
static int list_image_one_and_maybe_read_metadata(sd_varlink *link, Image *image, bool more, AcquireMetadata am) {
static int list_image_one_and_maybe_read_metadata(Manager *m, sd_varlink *link, Image *image, bool more, AcquireMetadata am) {
int r;
assert(m);
assert(link);
assert(image);
if (should_acquire_metadata(am) && !image->metadata_valid) {
r = image_read_metadata(image, &image_policy_container);
r = image_read_metadata(image, &image_policy_container, m->runtime_scope);
if (r < 0 && am != ACQUIRE_METADATA_GRACEFUL)
return log_debug_errno(r, "Failed to read image metadata: %m");
if (r < 0)
@ -697,7 +699,7 @@ static int vl_method_list_images(sd_varlink *link, sd_json_variant *parameters,
if (r < 0)
return log_debug_errno(r, "Failed to find image: %m");
return list_image_one_and_maybe_read_metadata(link, found, /* more = */ false, p.acquire_metadata);
return list_image_one_and_maybe_read_metadata(m, link, found, /* more = */ false, p.acquire_metadata);
}
if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
@ -711,7 +713,7 @@ static int vl_method_list_images(sd_varlink *link, sd_json_variant *parameters,
Image *image, *previous = NULL;
HASHMAP_FOREACH(image, images) {
if (previous) {
r = list_image_one_and_maybe_read_metadata(link, previous, /* more = */ true, p.acquire_metadata);
r = list_image_one_and_maybe_read_metadata(m, link, previous, /* more = */ true, p.acquire_metadata);
if (r < 0)
return r;
}
@ -720,7 +722,7 @@ static int vl_method_list_images(sd_varlink *link, sd_json_variant *parameters,
}
if (previous)
return list_image_one_and_maybe_read_metadata(link, previous, /* more = */ false, p.acquire_metadata);
return list_image_one_and_maybe_read_metadata(m, link, previous, /* more = */ false, p.acquire_metadata);
return sd_varlink_error(link, VARLINK_ERROR_MACHINE_IMAGE_NO_SUCH_IMAGE, NULL);
}
@ -733,6 +735,8 @@ static int manager_varlink_init_userdb(Manager *m) {
if (m->varlink_userdb_server)
return 0;
if (m->runtime_scope != RUNTIME_SCOPE_SYSTEM) /* no userdb in per-user mode! */
return 0;
r = varlink_server_new(&s, SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA, m);
if (r < 0)
@ -817,11 +821,20 @@ static int manager_varlink_init_machine(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to bind to passed Varlink sockets: %m");
if (r == 0) {
r = sd_varlink_server_listen_address(s, "/run/systemd/machine/io.systemd.Machine", 0666 | SD_VARLINK_SERVER_MODE_MKDIR_0755);
_cleanup_free_ char *socket_path = NULL;
r = runtime_directory_generic(m->runtime_scope, "systemd/machine/io.systemd.Machine", &socket_path);
if (r < 0)
return log_error_errno(r, "Failed to determine socket path: %m");
r = sd_varlink_server_listen_address(s, socket_path, runtime_scope_to_socket_mode(m->runtime_scope) | SD_VARLINK_SERVER_MODE_MKDIR_0755);
if (r < 0)
return log_error_errno(r, "Failed to bind to io.systemd.Machine varlink socket: %m");
r = sd_varlink_server_listen_address(s, "/run/systemd/machine/io.systemd.MachineImage", 0666);
socket_path = mfree(socket_path);
r = runtime_directory_generic(m->runtime_scope, "systemd/machine/io.systemd.MachineImage", &socket_path);
if (r < 0)
return log_error_errno(r, "Failed to determine socket path: %m");
r = sd_varlink_server_listen_address(s, socket_path, runtime_scope_to_socket_mode(m->runtime_scope));
if (r < 0)
return log_error_errno(r, "Failed to bind to io.systemd.MachineImage varlink socket: %m");
}

View File

@ -27,6 +27,7 @@
#include "main-func.h"
#include "mkdir-label.h"
#include "operation.h"
#include "path-lookup.h"
#include "service-util.h"
#include "signal-util.h"
#include "socket-util.h"
@ -38,7 +39,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(machine_hash_ops, char, string_hash_func, string_compare_func, Machine, machine_free);
static int manager_new(Manager **ret) {
static int manager_new(RuntimeScope scope, Manager **ret) {
_cleanup_(manager_unrefp) Manager *m = NULL;
int r;
@ -49,9 +50,13 @@ static int manager_new(Manager **ret) {
return -ENOMEM;
*m = (Manager) {
.runtime_scope = RUNTIME_SCOPE_SYSTEM,
.runtime_scope = scope,
};
r = runtime_directory_generic(scope, "systemd/machines", &m->state_dir);
if (r < 0)
return r;
m->machines = hashmap_new(&machine_hash_ops);
if (!m->machines)
return -ENOMEM;
@ -105,9 +110,12 @@ static Manager* manager_unref(Manager *m) {
manager_varlink_done(m);
sd_bus_flush_close_unref(m->bus);
sd_bus_flush_close_unref(m->api_bus);
sd_bus_flush_close_unref(m->system_bus);
sd_event_unref(m->event);
free(m->state_dir);
return mfree(m);
}
@ -118,6 +126,8 @@ static int manager_add_host_machine(Manager *m) {
Machine *t;
int r;
if (m->runtime_scope != RUNTIME_SCOPE_SYSTEM)
return 0;
if (m->host_machine)
return 0;
@ -174,12 +184,12 @@ static int manager_enumerate_machines(Manager *m) {
return r;
/* Read in machine data stored on disk */
d = opendir("/run/systemd/machines");
d = opendir(m->state_dir);
if (!d) {
if (errno == ENOENT)
return 0;
return log_error_errno(errno, "Failed to open %s: %m", "/run/systemd/machines");
return log_error_errno(errno, "Failed to open '%s': %m", m->state_dir);
}
FOREACH_DIRENT(de, d, return -errno) {
@ -214,26 +224,45 @@ static int manager_connect_bus(Manager *m) {
int r;
assert(m);
assert(!m->bus);
assert(!m->system_bus);
assert(!m->api_bus);
r = sd_bus_default_system(&m->bus);
r = sd_bus_default_system(&m->system_bus);
if (r < 0)
return log_error_errno(r, "Failed to connect to system bus: %m");
r = bus_add_implementation(m->bus, &manager_object, m);
r = sd_bus_attach_event(m->system_bus, m->event, 0);
if (r < 0)
return log_error_errno(r, "Failed to attach system bus to event loop: %m");
if (m->runtime_scope == RUNTIME_SCOPE_SYSTEM)
m->api_bus = sd_bus_ref(m->system_bus);
else {
assert(m->runtime_scope == RUNTIME_SCOPE_USER);
r = sd_bus_default_user(&m->api_bus);
if (r < 0)
return log_error_errno(r, "Failed to connect to user bus: %m");
r = sd_bus_attach_event(m->api_bus, m->event, 0);
if (r < 0)
return log_error_errno(r, "Failed to attach user bus to event loop: %m");
}
r = bus_add_implementation(m->api_bus, &manager_object, m);
if (r < 0)
return r;
r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "JobRemoved", match_job_removed, NULL, m);
r = bus_match_signal_async(m->api_bus, NULL, bus_systemd_mgr, "JobRemoved", match_job_removed, NULL, m);
if (r < 0)
return log_error_errno(r, "Failed to add match for JobRemoved: %m");
r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "UnitRemoved", match_unit_removed, NULL, m);
r = bus_match_signal_async(m->api_bus, NULL, bus_systemd_mgr, "UnitRemoved", match_unit_removed, NULL, m);
if (r < 0)
return log_error_errno(r, "Failed to request match for UnitRemoved: %m");
r = sd_bus_match_signal_async(
m->bus,
m->api_bus,
NULL,
"org.freedesktop.systemd1",
NULL,
@ -243,26 +272,22 @@ static int manager_connect_bus(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to request match for PropertiesChanged: %m");
r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "Reloading", match_reloading, NULL, m);
r = bus_match_signal_async(m->api_bus, NULL, bus_systemd_mgr, "Reloading", match_reloading, NULL, m);
if (r < 0)
return log_error_errno(r, "Failed to request match for Reloading: %m");
r = bus_call_method_async(m->bus, NULL, bus_systemd_mgr, "Subscribe", NULL, NULL, NULL);
r = bus_call_method_async(m->api_bus, NULL, bus_systemd_mgr, "Subscribe", NULL, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to enable subscription: %m");
r = bus_log_control_api_register(m->bus);
r = bus_log_control_api_register(m->api_bus);
if (r < 0)
return r;
r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.machine1", 0, NULL, NULL);
r = sd_bus_request_name_async(m->api_bus, NULL, "org.freedesktop.machine1", 0, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to request name: %m");
r = sd_bus_attach_event(m->bus, m->event, 0);
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
return 0;
}
@ -315,6 +340,7 @@ static bool check_idle(void *userdata) {
static int run(int argc, char *argv[]) {
_cleanup_(manager_unrefp) Manager *m = NULL;
RuntimeScope scope = RUNTIME_SCOPE_SYSTEM;
int r;
log_set_facility(LOG_AUTH);
@ -324,6 +350,7 @@ static int run(int argc, char *argv[]) {
"Manage registrations of local VMs and containers.",
BUS_IMPLEMENTATIONS(&manager_object,
&log_control_object),
&scope,
argc, argv);
if (r <= 0)
return r;
@ -333,11 +360,12 @@ static int run(int argc, char *argv[]) {
/* Always create the directories people can create inotify watches in. Note that some applications might check
* for the existence of /run/systemd/machines/ to determine whether machined is available, so please always
* make sure this check stays in. */
if (scope == RUNTIME_SCOPE_SYSTEM)
(void) mkdir_label("/run/systemd/machines", 0755);
assert_se(sigprocmask_many(SIG_BLOCK, /* ret_old_mask= */ NULL, SIGCHLD) >= 0);
r = manager_new(&m);
r = manager_new(scope, &m);
if (r < 0)
return log_error_errno(r, "Failed to allocate manager object: %m");
@ -351,7 +379,7 @@ static int run(int argc, char *argv[]) {
r = bus_event_loop_with_idle(
m->event,
m->bus,
m->api_bus,
"org.freedesktop.machine1",
DEFAULT_EXIT_USEC,
check_idle, m);

View File

@ -7,7 +7,8 @@
typedef struct Manager {
sd_event *event;
sd_bus *bus;
sd_bus *api_bus; /* this is where we offer our services */
sd_bus *system_bus; /* this is where we talk to system services on, for example PK or so */
Hashmap *machines;
Hashmap *machines_by_unit; /* This hashmap only tracks machines where a system-level encapsulates
@ -32,7 +33,8 @@ typedef struct Manager {
sd_varlink_server *varlink_userdb_server;
sd_varlink_server *varlink_machine_server;
RuntimeScope runtime_scope; /* for now: always RUNTIME_SCOPE_SYSTEM */
RuntimeScope runtime_scope;
char *state_dir;
} Manager;
int manager_add_machine(Manager *m, const char *name, Machine **ret);

View File

@ -49,5 +49,8 @@ install_data('org.freedesktop.machine1.conf',
install_dir : dbuspolicydir)
install_data('org.freedesktop.machine1.service',
install_dir : dbussystemservicedir)
install_data('org.freedesktop.machine1.service-for-session',
install_dir : dbussessionservicedir,
rename : 'org.freedesktop.machine1.service')
install_data('org.freedesktop.machine1.policy',
install_dir : polkitpolicydir)

View File

@ -0,0 +1,13 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[D-BUS Service]
Name=org.freedesktop.machine1
Exec=/bin/false
SystemdService=dbus-org.freedesktop.machine1.service

View File

@ -5,7 +5,7 @@ executables += [
'name' : 'systemd-mount',
'public' : true,
'sources' : files('mount-tool.c'),
'dependencies' : libmount,
'dependencies' : libmount_cflags,
},
]

View File

@ -860,14 +860,14 @@ static int find_mount_points_by_source(const char *what, char ***ret) {
struct libmnt_fs *fs;
const char *source, *target;
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r == 1)
break;
if (r < 0)
return log_error_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m");
source = mnt_fs_get_source(fs);
target = mnt_fs_get_target(fs);
source = sym_mnt_fs_get_source(fs);
target = sym_mnt_fs_get_target(fs);
if (!source || !target)
continue;

View File

@ -29,6 +29,7 @@ static int run(int argc, char *argv[]) {
r = service_parse_argv("systemd-networkd.service",
"Manage and configure network devices, create virtual network devices",
BUS_IMPLEMENTATIONS(&manager_object, &log_control_object),
/* runtime_scope= */ NULL,
argc, argv);
if (r <= 0)
return r;

View File

@ -294,6 +294,7 @@ int register_machine(
return 0;
if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD))
return log_error_errno(r, "Failed to register machine: %s", bus_error_message(&error, r));
sd_bus_error_free(&error);
if (FLAGS_SET(flags, REGISTER_MACHINE_KEEP_UNIT)) {
r = bus_call_method(
bus,

View File

@ -6055,6 +6055,7 @@ static int run(int argc, char *argv[]) {
/* We take an exclusive lock on this image, since it's our private, ephemeral copy
* only owned by us and no one else. */
r = image_path_lock(
arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER,
np,
LOCK_EX|LOCK_NB,
arg_privileged ? &tree_global_lock : NULL,
@ -6091,6 +6092,7 @@ static int run(int argc, char *argv[]) {
goto finish;
r = image_path_lock(
arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER,
arg_directory,
(arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB,
arg_privileged ? &tree_global_lock : NULL,
@ -6218,6 +6220,7 @@ static int run(int argc, char *argv[]) {
/* Always take an exclusive lock on our own ephemeral copy. */
r = image_path_lock(
arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER,
np,
LOCK_EX|LOCK_NB,
arg_privileged ? &tree_global_lock : NULL,
@ -6245,6 +6248,7 @@ static int run(int argc, char *argv[]) {
remove_image = true;
} else {
r = image_path_lock(
arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER,
arg_image,
(arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB,
arg_privileged ? &tree_global_lock : NULL,

View File

@ -61,7 +61,7 @@ int bus_image_common_get_os_release(
return 1;
if (!image->metadata_valid) {
r = image_read_metadata(image, &image_policy_service);
r = image_read_metadata(image, &image_policy_service, m->runtime_scope);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
}
@ -539,7 +539,7 @@ int bus_image_common_remove(
if (r == 0) {
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
r = image_remove(image);
r = image_remove(image, m->runtime_scope);
if (r < 0) {
(void) write(errno_pipe_fd[1], &r, sizeof(r));
_exit(EXIT_FAILURE);
@ -801,7 +801,7 @@ int bus_image_common_mark_read_only(
if (r == 0)
return 1; /* Will call us back */
r = image_read_only(image, read_only);
r = image_read_only(image, read_only, m->runtime_scope);
if (r < 0)
return r;

View File

@ -133,6 +133,7 @@ static int run(int argc, char *argv[]) {
"Manage registrations of portable images.",
BUS_IMPLEMENTATIONS(&manager_object,
&log_control_object),
/* runtime_scope= */ NULL,
argc, argv);
if (r <= 0)
return r;

View File

@ -4,6 +4,6 @@ executables += [
libexec_template + {
'name' : 'systemd-remount-fs',
'sources' : files('remount-fs.c'),
'dependencies' : libmount,
'dependencies' : libmount_cflags,
},
]

View File

@ -90,13 +90,13 @@ static int remount_by_fstab(Hashmap **ret_pids) {
for (;;) {
struct libmnt_fs *fs;
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r < 0)
return log_error_errno(r, "Failed to get next entry from fstab: %m");
if (r > 0) /* EOF */
break;
const char *target = mnt_fs_get_target(fs);
const char *target = sym_mnt_fs_get_target(fs);
if (!target)
continue;

View File

@ -30,6 +30,7 @@ static int run(int argc, char *argv[]) {
"Provide name resolution with caching using DNS, mDNS, LLMNR.",
BUS_IMPLEMENTATIONS(&manager_object,
&log_control_object),
/* runtime_scope= */ NULL,
argc, argv);
if (r <= 0)
return r;

View File

@ -19,6 +19,7 @@
#include "chase.h"
#include "chattr-util.h"
#include "copy.h"
#include "devnum-util.h"
#include "dirent-util.h"
#include "discover-image.h"
#include "dissect-image.h"
@ -37,6 +38,7 @@
#include "mkdir.h"
#include "nulstr-util.h"
#include "os-util.h"
#include "path-lookup.h"
#include "path-util.h"
#include "rm-rf.h"
#include "runtime-scope.h"
@ -119,6 +121,15 @@ static const char *const image_root_runtime_table[_IMAGE_CLASS_MAX] = {
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(image_root_runtime, ImageClass);
static const char *const image_dirname_table[_IMAGE_CLASS_MAX] = {
[IMAGE_MACHINE] = "machines",
[IMAGE_PORTABLE] = "portables",
[IMAGE_SYSEXT] = "extensions",
[IMAGE_CONFEXT] = "confexts",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(image_dirname, ImageClass);
static Image* image_free(Image *i) {
assert(i);
@ -138,7 +149,7 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(Image, image, image_free);
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(image_hash_ops, char, string_hash_func, string_compare_func,
Image, image_unref);
static char** image_settings_path(Image *image) {
static char** image_settings_path(Image *image, RuntimeScope scope) {
_cleanup_strv_free_ char **l = NULL;
_cleanup_free_ char *fn = NULL;
size_t i = 0;
@ -146,7 +157,7 @@ static char** image_settings_path(Image *image) {
assert(image);
l = new0(char*, 4);
l = new0(char*, 5);
if (!l)
return NULL;
@ -154,7 +165,41 @@ static char** image_settings_path(Image *image) {
if (!fn)
return NULL;
FOREACH_STRING(s, "/etc/systemd/nspawn", "/run/systemd/nspawn") {
static const uint64_t system_locations[] = {
SD_PATH_SYSTEM_CONFIGURATION,
SD_PATH_SYSTEM_RUNTIME,
SD_PATH_SYSTEM_LIBRARY_PRIVATE,
UINT64_MAX
};
static const uint64_t user_locations[] = {
SD_PATH_USER_CONFIGURATION,
SD_PATH_USER_RUNTIME,
SD_PATH_USER_LIBRARY_PRIVATE,
UINT64_MAX
};
const uint64_t *locations;
switch (scope) {
case RUNTIME_SCOPE_SYSTEM:
locations = system_locations;
break;
case RUNTIME_SCOPE_USER:
locations = user_locations;
break;
default:
assert_not_reached();
}
for (size_t k = 0; locations[k] != UINT64_MAX; k++) {
_cleanup_free_ char *s = NULL;
r = sd_path_lookup(locations[k], "systemd/nspawn", &s);
if (r == -ENXIO)
continue;
if (r < 0)
return NULL;
l[i] = path_join(s, fn);
if (!l[i])
return NULL;
@ -1086,7 +1131,7 @@ int image_discover(
return 0;
}
int image_remove(Image *i) {
int image_remove(Image *i, RuntimeScope scope) {
_cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
_cleanup_strv_free_ char **settings = NULL;
_cleanup_free_ char *roothash = NULL;
@ -1097,7 +1142,7 @@ int image_remove(Image *i) {
if (image_is_vendor(i) || image_is_host(i))
return -EROFS;
settings = image_settings_path(i);
settings = image_settings_path(i, scope);
if (!settings)
return -ENOMEM;
@ -1106,7 +1151,7 @@ int image_remove(Image *i) {
return r;
/* Make sure we don't interfere with a running nspawn */
r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
r = image_path_lock(scope, i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
if (r < 0)
return r;
@ -1192,7 +1237,7 @@ int image_rename(Image *i, const char *new_name, RuntimeScope scope) {
if (image_is_vendor(i) || image_is_host(i))
return -EROFS;
settings = image_settings_path(i);
settings = image_settings_path(i, scope);
if (!settings)
return -ENOMEM;
@ -1201,14 +1246,14 @@ int image_rename(Image *i, const char *new_name, RuntimeScope scope) {
return r;
/* Make sure we don't interfere with a running nspawn */
r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
r = image_path_lock(scope, i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
if (r < 0)
return r;
/* Make sure nobody takes the new name, between the time we
* checked it is currently unused in all search paths, and the
* time we take possession of it */
r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock);
r = image_name_lock(scope, new_name, LOCK_EX|LOCK_NB, &name_lock);
if (r < 0)
return r;
@ -1299,11 +1344,64 @@ static int clone_auxiliary_file(const char *path, const char *new_name, const ch
return copy_file_atomic(path, rs, 0664, COPY_REFLINK);
}
static int get_pool_directory(
RuntimeScope scope,
ImageClass class,
const char *fname,
const char *suffix,
char **ret) {
int r;
assert(scope >= 0);
assert(scope < _RUNTIME_SCOPE_MAX);
assert(class >= 0);
assert(class < _IMAGE_CLASS_MAX);
assert(ret);
_cleanup_free_ char *root = NULL;
switch (scope) {
case RUNTIME_SCOPE_SYSTEM:
r = sd_path_lookup(SD_PATH_SYSTEM_STATE_PRIVATE, /* suffix= */ NULL, &root);
break;
case RUNTIME_SCOPE_USER:
r = sd_path_lookup(SD_PATH_USER_STATE_PRIVATE, /* suffix= */ NULL, &root);
break;
default:
return -EOPNOTSUPP;
}
if (r < 0)
return r;
const char *n = image_dirname_to_string(class);
if (!n)
return -EOPNOTSUPP;
_cleanup_free_ char *j = NULL;
const char *fn;
if (fname && suffix) {
j = strjoin(fname, suffix);
if (!j)
return -ENOMEM;
fn = j;
} else
fn = fname ?: suffix;
_cleanup_free_ char *p = path_join(root, n, fn);
if (!p)
return -ENOMEM;
*ret = TAKE_PTR(p);
return 0;
}
int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope scope) {
_cleanup_(release_lock_file) LockFile name_lock = LOCK_FILE_INIT;
_cleanup_strv_free_ char **settings = NULL;
_cleanup_free_ char *roothash = NULL;
const char *new_path;
int r;
assert(i);
@ -1311,7 +1409,7 @@ int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope sco
if (!image_name_is_valid(new_name))
return -EINVAL;
settings = image_settings_path(i);
settings = image_settings_path(i, scope);
if (!settings)
return -ENOMEM;
@ -1322,11 +1420,11 @@ int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope sco
/* Make sure nobody takes the new name, between the time we
* checked it is currently unused in all search paths, and the
* time we take possession of it */
r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock);
r = image_name_lock(scope, new_name, LOCK_EX|LOCK_NB, &name_lock);
if (r < 0)
return r;
r = image_find(scope, IMAGE_MACHINE, new_name, NULL, NULL);
r = image_find(scope, i->class, new_name, NULL, NULL);
if (r >= 0)
return -EEXIST;
if (r != -ENOENT)
@ -1335,11 +1433,14 @@ int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope sco
switch (i->type) {
case IMAGE_SUBVOLUME:
case IMAGE_DIRECTORY:
case IMAGE_DIRECTORY: {
/* If we can we'll always try to create a new btrfs subvolume here, even if the source is a plain
* directory. */
new_path = strjoina("/var/lib/machines/", new_name);
_cleanup_free_ char *new_path = NULL;
r = get_pool_directory(scope, i->class, new_name, /* suffix= */ NULL, &new_path);
if (r < 0)
return r;
r = btrfs_subvol_snapshot_at(AT_FDCWD, i->path, AT_FDCWD, new_path,
(read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
@ -1353,19 +1454,23 @@ int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope sco
(void) btrfs_subvol_auto_qgroup(new_path, 0, true);
break;
}
case IMAGE_RAW:
new_path = strjoina("/var/lib/machines/", new_name, ".raw");
case IMAGE_RAW: {
_cleanup_free_ char *new_path = NULL;
r = get_pool_directory(scope, i->class, new_name, ".raw", &new_path);
if (r < 0)
return r;
r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644,
COPY_REFLINK|COPY_CRTIME|COPY_NOCOW_AFTER);
break;
}
case IMAGE_BLOCK:
default:
return -EOPNOTSUPP;
}
if (r < 0)
return r;
@ -1382,7 +1487,7 @@ int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope sco
return 0;
}
int image_read_only(Image *i, bool b) {
int image_read_only(Image *i, bool b, RuntimeScope scope) {
_cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
int r;
@ -1392,7 +1497,7 @@ int image_read_only(Image *i, bool b) {
return -EROFS;
/* Make sure we don't interfere with a running nspawn */
r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
r = image_path_lock(scope, i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
if (r < 0)
return r;
@ -1469,12 +1574,33 @@ int image_read_only(Image *i, bool b) {
return 0;
}
static void make_lock_dir(void) {
(void) mkdir_p("/run/systemd/nspawn", 0755);
(void) mkdir("/run/systemd/nspawn/locks", 0700);
static int make_lock_dir(RuntimeScope scope) {
int r;
_cleanup_free_ char *p = NULL;
r = runtime_directory_generic(scope, "systemd", &p);
if (r < 0)
return r;
_cleanup_close_ int pfd = open_mkdir_at(AT_FDCWD, p, O_CLOEXEC, 0755);
if (pfd < 0)
return pfd;
_cleanup_close_ int nfd = open_mkdir_at(pfd, "nspawn", O_CLOEXEC, 0755);
if (nfd < 0)
return nfd;
r = RET_NERRNO(mkdirat(nfd, "locks", 0700));
if (r == -EEXIST)
return 0;
if (r < 0)
return r;
return 1;
}
int image_path_lock(
RuntimeScope scope,
const char *path,
int operation,
LockFile *ret_global,
@ -1490,7 +1616,7 @@ int image_path_lock(
assert(ret_local);
/* Locks an image path. This actually creates two locks: one "local" one, next to the image path
* itself, which might be shared via NFS. And another "global" one, in /run, that uses the
* itself, which might be shared via NFS. And another "global" one, in /run/, that uses the
* device/inode number. This has the benefit that we can even lock a tree that is a mount point,
* correctly. */
@ -1534,15 +1660,21 @@ int image_path_lock(
}
if (ret_global) {
if (stat(path, &st) >= 0) {
if (stat(path, &st) < 0)
log_debug_errno(errno, "Failed to stat() image '%s', not locking image: %m", path);
else {
r = runtime_directory_generic(scope, "systemd/nspawn/locks/", &p);
if (r < 0)
return r;
if (S_ISBLK(st.st_mode))
r = asprintf(&p, "/run/systemd/nspawn/locks/block-%u:%u", major(st.st_rdev), minor(st.st_rdev));
r = strextendf(&p, "block-" DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(st.st_rdev));
else if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode))
r = asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino);
r = strextendf(&p, "inode-%" PRIu64 ":%" PRIu64, (uint64_t) st.st_dev, (uint64_t) st.st_ino);
else
return -ENOTTY;
if (r < 0)
return -ENOMEM;
return r;
}
}
@ -1551,15 +1683,15 @@ int image_path_lock(
if (!path_startswith(path, "/dev/")) {
r = make_lock_file_for(path, operation, &t);
if (r < 0) {
if (!exclusive && r == -EROFS)
log_debug_errno(r, "Failed to create shared lock for '%s', ignoring: %m", path);
else
if (exclusive || r != -EROFS)
return r;
log_debug_errno(r, "Failed to create shared lock for '%s', ignoring: %m", path);
}
}
if (p) {
make_lock_dir();
(void) make_lock_dir(scope);
r = make_lock_file(p, operation, ret_global);
if (r < 0) {
@ -1599,36 +1731,39 @@ int image_set_limit(Image *i, uint64_t referenced_max) {
return 0;
}
int image_set_pool_limit(ImageClass class, uint64_t referenced_max) {
const char *dir;
int image_set_pool_limit(RuntimeScope scope, ImageClass class, uint64_t referenced_max) {
int r;
assert(scope >= 0 && scope < _RUNTIME_SCOPE_MAX);
assert(class >= 0 && class < _IMAGE_CLASS_MAX);
dir = image_root_to_string(class);
_cleanup_free_ char *pool = NULL;
r = get_pool_directory(scope, class, /* fname= */ NULL, /* suffix= */ NULL, &pool);
if (r < 0)
return r;
r = btrfs_qgroup_set_limit(dir, /* qgroupid = */ 0, referenced_max);
r = btrfs_qgroup_set_limit(pool, /* qgroupid = */ 0, referenced_max);
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return r;
if (r < 0)
log_debug_errno(r, "Failed to set limit on btrfs quota group for '%s', ignoring: %m", dir);
log_debug_errno(r, "Failed to set limit on btrfs quota group for '%s', ignoring: %m", pool);
r = btrfs_subvol_set_subtree_quota_limit(dir, /* subvol_id = */ 0, referenced_max);
r = btrfs_subvol_set_subtree_quota_limit(pool, /* subvol_id = */ 0, referenced_max);
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return r;
if (r < 0)
return log_debug_errno(r, "Failed to set subtree quota limit for '%s': %m", dir);
return log_debug_errno(r, "Failed to set subtree quota limit for '%s': %m", pool);
return 0;
}
int image_read_metadata(Image *i, const ImagePolicy *image_policy) {
int image_read_metadata(Image *i, const ImagePolicy *image_policy, RuntimeScope scope) {
_cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
int r;
assert(i);
r = image_path_lock(i->path, LOCK_SH|LOCK_NB, &global_lock, &local_lock);
r = image_path_lock(scope, i->path, LOCK_SH|LOCK_NB, &global_lock, &local_lock);
if (r < 0)
return r;
@ -1758,8 +1893,13 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy) {
return 0;
}
int image_name_lock(const char *name, int operation, LockFile *ret) {
const char *p;
int image_name_lock(
RuntimeScope scope,
const char *name,
int operation,
LockFile *ret) {
int r;
assert(name);
assert(ret);
@ -1777,9 +1917,16 @@ int image_name_lock(const char *name, int operation, LockFile *ret) {
return 0;
}
make_lock_dir();
(void) make_lock_dir(scope);
_cleanup_free_ char *p = NULL;
r = runtime_directory_generic(scope, "systemd/nspawn/locks/name-", &p);
if (r < 0)
return r;
if (!strextend(&p, name))
return -ENOMEM;
p = strjoina("/run/systemd/nspawn/locks/name-", name);
return make_lock_file(p, operation, ret);
}

View File

@ -55,21 +55,21 @@ int image_from_path(const char *path, Image **ret);
int image_find_harder(RuntimeScope scope, ImageClass class, const char *name_or_path, const char *root, Image **ret);
int image_discover(RuntimeScope scope, ImageClass class, const char *root, Hashmap **images);
int image_remove(Image *i);
int image_remove(Image *i, RuntimeScope scope);
int image_rename(Image *i, const char *new_name, RuntimeScope scope);
int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope scope);
int image_read_only(Image *i, bool b);
int image_read_only(Image *i, bool b, RuntimeScope scope);
const char* image_type_to_string(ImageType t) _const_;
ImageType image_type_from_string(const char *s) _pure_;
int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local);
int image_name_lock(const char *name, int operation, LockFile *ret);
int image_path_lock(RuntimeScope scope, const char *path, int operation, LockFile *global, LockFile *local);
int image_name_lock(RuntimeScope scope, const char *name, int operation, LockFile *ret);
int image_set_limit(Image *i, uint64_t referenced_max);
int image_set_pool_limit(ImageClass class, uint64_t referenced_max);
int image_set_pool_limit(RuntimeScope scope, ImageClass class, uint64_t referenced_max);
int image_read_metadata(Image *i, const ImagePolicy *image_policy);
int image_read_metadata(Image *i, const ImagePolicy *image_policy, RuntimeScope scope);
bool image_in_search_path(RuntimeScope scope, ImageClass class, const char *root, const char *image);

View File

@ -56,13 +56,13 @@ int fstab_has_fstype(const char *fstype) {
for (;;) {
struct libmnt_fs *fs;
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r < 0)
return r;
if (r > 0) /* EOF */
return false;
if (streq_ptr(mnt_fs_get_fstype(fs), fstype))
if (streq_ptr(sym_mnt_fs_get_fstype(fs), fstype))
return true;
}
}
@ -134,13 +134,13 @@ int fstab_has_mount_point_prefix_strv(char * const *prefixes) {
struct libmnt_fs *fs;
const char *path;
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r < 0)
return r;
if (r > 0) /* EOF */
return false;
path = mnt_fs_get_target(fs);
path = sym_mnt_fs_get_target(fs);
if (!path)
continue;
@ -168,19 +168,19 @@ int fstab_is_mount_point_full(const char *where, const char *path) {
for (;;) {
struct libmnt_fs *fs;
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r < 0)
return r;
if (r > 0) /* EOF */
return false;
if (where && !path_equal(mnt_fs_get_target(fs), where))
if (where && !path_equal(sym_mnt_fs_get_target(fs), where))
continue;
if (!path)
return true;
r = fstab_is_same_node(mnt_fs_get_source(fs), path);
r = fstab_is_same_node(sym_mnt_fs_get_source(fs), path);
if (r > 0 || (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r)))
return r;
}

View File

@ -6,6 +6,83 @@
#include "libmount-util.h"
#include "log.h"
static void *libmount_dl = NULL;
DLSYM_PROTOTYPE(mnt_free_iter) = NULL;
DLSYM_PROTOTYPE(mnt_free_table) = NULL;
DLSYM_PROTOTYPE(mnt_fs_get_fs_options) = NULL;
DLSYM_PROTOTYPE(mnt_fs_get_fstype) = NULL;
DLSYM_PROTOTYPE(mnt_fs_get_id) = NULL;
DLSYM_PROTOTYPE(mnt_fs_get_option) = NULL;
DLSYM_PROTOTYPE(mnt_fs_get_options) = NULL;
DLSYM_PROTOTYPE(mnt_fs_get_passno) = NULL;
DLSYM_PROTOTYPE(mnt_fs_get_propagation) = NULL;
DLSYM_PROTOTYPE(mnt_fs_get_source) = NULL;
DLSYM_PROTOTYPE(mnt_fs_get_target) = NULL;
DLSYM_PROTOTYPE(mnt_fs_get_vfs_options) = NULL;
DLSYM_PROTOTYPE(mnt_get_builtin_optmap) = NULL;
DLSYM_PROTOTYPE(mnt_init_debug) = NULL;
DLSYM_PROTOTYPE(mnt_monitor_enable_kernel) = NULL;
DLSYM_PROTOTYPE(mnt_monitor_enable_userspace) = NULL;
DLSYM_PROTOTYPE(mnt_monitor_get_fd) = NULL;
DLSYM_PROTOTYPE(mnt_monitor_next_change) = NULL;
DLSYM_PROTOTYPE(mnt_new_iter) = NULL;
DLSYM_PROTOTYPE(mnt_new_monitor) = NULL;
DLSYM_PROTOTYPE(mnt_new_table) = NULL;
DLSYM_PROTOTYPE(mnt_optstr_get_flags) = NULL;
DLSYM_PROTOTYPE(mnt_table_find_devno) = NULL;
DLSYM_PROTOTYPE(mnt_table_find_target) = NULL;
DLSYM_PROTOTYPE(mnt_table_next_child_fs) = NULL;
DLSYM_PROTOTYPE(mnt_table_next_fs) = NULL;
DLSYM_PROTOTYPE(mnt_table_parse_file) = NULL;
DLSYM_PROTOTYPE(mnt_table_parse_mtab) = NULL;
DLSYM_PROTOTYPE(mnt_table_parse_stream) = NULL;
DLSYM_PROTOTYPE(mnt_table_parse_swaps) = NULL;
DLSYM_PROTOTYPE(mnt_unref_monitor) = NULL;
int dlopen_libmount(void) {
ELF_NOTE_DLOPEN("mount",
"Support for mount enumeration",
ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED,
"libmount.so.1");
return dlopen_many_sym_or_warn(
&libmount_dl,
"libmount.so.1",
LOG_DEBUG,
DLSYM_ARG(mnt_free_iter),
DLSYM_ARG(mnt_free_table),
DLSYM_ARG(mnt_fs_get_fs_options),
DLSYM_ARG(mnt_fs_get_fstype),
DLSYM_ARG(mnt_fs_get_id),
DLSYM_ARG(mnt_fs_get_option),
DLSYM_ARG(mnt_fs_get_options),
DLSYM_ARG(mnt_fs_get_passno),
DLSYM_ARG(mnt_fs_get_propagation),
DLSYM_ARG(mnt_fs_get_source),
DLSYM_ARG(mnt_fs_get_target),
DLSYM_ARG(mnt_fs_get_vfs_options),
DLSYM_ARG(mnt_get_builtin_optmap),
DLSYM_ARG(mnt_init_debug),
DLSYM_ARG(mnt_monitor_enable_kernel),
DLSYM_ARG(mnt_monitor_enable_userspace),
DLSYM_ARG(mnt_monitor_get_fd),
DLSYM_ARG(mnt_monitor_next_change),
DLSYM_ARG(mnt_new_iter),
DLSYM_ARG(mnt_new_monitor),
DLSYM_ARG(mnt_new_table),
DLSYM_ARG(mnt_optstr_get_flags),
DLSYM_ARG(mnt_table_find_devno),
DLSYM_ARG(mnt_table_find_target),
DLSYM_ARG(mnt_table_next_child_fs),
DLSYM_ARG(mnt_table_next_fs),
DLSYM_ARG(mnt_table_parse_file),
DLSYM_ARG(mnt_table_parse_mtab),
DLSYM_ARG(mnt_table_parse_stream),
DLSYM_ARG(mnt_table_parse_swaps),
DLSYM_ARG(mnt_unref_monitor));
}
int libmount_parse_full(
const char *path,
FILE *source,
@ -19,8 +96,12 @@ int libmount_parse_full(
/* Older libmount seems to require this. */
assert(!source || path);
table = mnt_new_table();
iter = mnt_new_iter(MNT_ITER_FORWARD);
r = dlopen_libmount();
if (r < 0)
return r;
table = sym_mnt_new_table();
iter = sym_mnt_new_iter(MNT_ITER_FORWARD);
if (!table || !iter)
return -ENOMEM;
@ -28,11 +109,11 @@ int libmount_parse_full(
* Only if both are empty, we use mnt_table_parse_mtab(). */
if (source)
r = mnt_table_parse_stream(table, source, path);
r = sym_mnt_table_parse_stream(table, source, path);
else if (path)
r = mnt_table_parse_file(table, path);
r = sym_mnt_table_parse_file(table, path);
else
r = mnt_table_parse_mtab(table, NULL);
r = sym_mnt_table_parse_mtab(table, NULL);
if (r < 0)
return r;
@ -53,14 +134,16 @@ int libmount_is_leaf(
struct libmnt_fs *fs) {
int r;
assert(table);
_cleanup_(mnt_free_iterp) struct libmnt_iter *iter_children = NULL;
iter_children = mnt_new_iter(MNT_ITER_FORWARD);
iter_children = sym_mnt_new_iter(MNT_ITER_FORWARD);
if (!iter_children)
return log_oom();
/* We care only whether it exists, it is unused */
_unused_ struct libmnt_fs *child;
r = mnt_table_next_child_fs(table, iter_children, fs, &child);
r = sym_mnt_table_next_child_fs(table, iter_children, fs, &child);
if (r < 0)
return r;

View File

@ -4,10 +4,45 @@
/* This needs to be after sys/mount.h */
#include <libmount.h> /* IWYU pragma: export */
#include "dlfcn-util.h"
#include "forward.h"
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct libmnt_table*, mnt_free_table, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct libmnt_iter*, mnt_free_iter, NULL);
extern DLSYM_PROTOTYPE(mnt_free_iter);
extern DLSYM_PROTOTYPE(mnt_free_table);
extern DLSYM_PROTOTYPE(mnt_fs_get_fs_options);
extern DLSYM_PROTOTYPE(mnt_fs_get_fstype);
extern DLSYM_PROTOTYPE(mnt_fs_get_id);
extern DLSYM_PROTOTYPE(mnt_fs_get_option);
extern DLSYM_PROTOTYPE(mnt_fs_get_options);
extern DLSYM_PROTOTYPE(mnt_fs_get_passno);
extern DLSYM_PROTOTYPE(mnt_fs_get_propagation);
extern DLSYM_PROTOTYPE(mnt_fs_get_source);
extern DLSYM_PROTOTYPE(mnt_fs_get_target);
extern DLSYM_PROTOTYPE(mnt_fs_get_vfs_options);
extern DLSYM_PROTOTYPE(mnt_get_builtin_optmap);
extern DLSYM_PROTOTYPE(mnt_init_debug);
extern DLSYM_PROTOTYPE(mnt_monitor_enable_kernel);
extern DLSYM_PROTOTYPE(mnt_monitor_enable_userspace);
extern DLSYM_PROTOTYPE(mnt_monitor_get_fd);
extern DLSYM_PROTOTYPE(mnt_monitor_next_change);
extern DLSYM_PROTOTYPE(mnt_new_iter);
extern DLSYM_PROTOTYPE(mnt_new_monitor);
extern DLSYM_PROTOTYPE(mnt_new_table);
extern DLSYM_PROTOTYPE(mnt_optstr_get_flags);
extern DLSYM_PROTOTYPE(mnt_table_find_devno);
extern DLSYM_PROTOTYPE(mnt_table_find_target);
extern DLSYM_PROTOTYPE(mnt_table_next_child_fs);
extern DLSYM_PROTOTYPE(mnt_table_next_fs);
extern DLSYM_PROTOTYPE(mnt_table_parse_file);
extern DLSYM_PROTOTYPE(mnt_table_parse_mtab);
extern DLSYM_PROTOTYPE(mnt_table_parse_stream);
extern DLSYM_PROTOTYPE(mnt_table_parse_swaps);
extern DLSYM_PROTOTYPE(mnt_unref_monitor);
int dlopen_libmount(void);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(struct libmnt_table*, sym_mnt_free_table, mnt_free_tablep, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(struct libmnt_iter*, sym_mnt_free_iter, mnt_free_iterp, NULL);
int libmount_parse_full(
const char *path,

View File

@ -322,7 +322,7 @@ libshared_deps = [threads,
libgcrypt_cflags,
libkmod_cflags,
liblz4_cflags,
libmount,
libmount_cflags,
libopenssl,
libp11kit_cflags,
libpam_cflags,

View File

@ -60,13 +60,13 @@ int umount_recursive_full(const char *prefix, int flags, char **keep) {
struct libmnt_fs *fs;
const char *path;
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r == 1)
break;
if (r < 0)
return log_debug_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m");
path = mnt_fs_get_target(fs);
path = sym_mnt_fs_get_target(fs);
if (!path)
continue;
@ -247,20 +247,20 @@ int bind_remount_recursive_with_mountinfo(
unsigned long flags = 0;
struct libmnt_fs *fs;
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r == 1) /* EOF */
break;
if (r < 0)
return log_debug_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m");
path = mnt_fs_get_target(fs);
path = sym_mnt_fs_get_target(fs);
if (!path)
continue;
if (!path_startswith(path, prefix))
continue;
type = mnt_fs_get_fstype(fs);
type = sym_mnt_fs_get_fstype(fs);
if (!type)
continue;
@ -298,9 +298,9 @@ int bind_remount_recursive_with_mountinfo(
continue;
}
opts = mnt_fs_get_vfs_options(fs);
opts = sym_mnt_fs_get_vfs_options(fs);
if (opts) {
r = mnt_optstr_get_flags(opts, &flags, mnt_get_builtin_optmap(MNT_LINUX_MAP));
r = sym_mnt_optstr_get_flags(opts, &flags, sym_mnt_get_builtin_optmap(MNT_LINUX_MAP));
if (r < 0)
log_debug_errno(r, "Could not get flags for '%s', ignoring: %m", path);
}
@ -438,15 +438,19 @@ int bind_remount_one_with_mountinfo(
rewind(proc_self_mountinfo);
table = mnt_new_table();
if (!table)
return -ENOMEM;
r = mnt_table_parse_stream(table, proc_self_mountinfo, "/proc/self/mountinfo");
r = dlopen_libmount();
if (r < 0)
return r;
fs = mnt_table_find_target(table, path, MNT_ITER_FORWARD);
table = sym_mnt_new_table();
if (!table)
return -ENOMEM;
r = sym_mnt_table_parse_stream(table, proc_self_mountinfo, "/proc/self/mountinfo");
if (r < 0)
return r;
fs = sym_mnt_table_find_target(table, path, MNT_ITER_FORWARD);
if (!fs) {
r = access_nofollow(path, F_OK); /* Hmm, it's not in the mount table, but does it exist at all? */
if (r < 0)
@ -455,9 +459,9 @@ int bind_remount_one_with_mountinfo(
return -EINVAL; /* Not a mount point we recognize */
}
opts = mnt_fs_get_vfs_options(fs);
opts = sym_mnt_fs_get_vfs_options(fs);
if (opts) {
r = mnt_optstr_get_flags(opts, &flags, mnt_get_builtin_optmap(MNT_LINUX_MAP));
r = sym_mnt_optstr_get_flags(opts, &flags, sym_mnt_get_builtin_optmap(MNT_LINUX_MAP));
if (r < 0)
log_debug_errno(r, "Could not get flags for '%s', ignoring: %m", path);
}
@ -873,7 +877,11 @@ int mount_option_mangle(
assert(ret_mount_flags);
assert(ret_remaining_options);
map = mnt_get_builtin_optmap(MNT_LINUX_MAP);
r = dlopen_libmount();
if (r < 0)
return r;
map = sym_mnt_get_builtin_optmap(MNT_LINUX_MAP);
if (!map)
return -EINVAL;
@ -1637,20 +1645,20 @@ int get_sub_mounts(const char *prefix, SubMount **ret_mounts, size_t *ret_n_moun
const char *path;
int id1, id2;
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r == 1)
break; /* EOF */
if (r < 0)
return log_debug_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m");
path = mnt_fs_get_target(fs);
path = sym_mnt_fs_get_target(fs);
if (!path)
continue;
if (isempty(path_startswith(path, prefix)))
continue;
id1 = mnt_fs_get_id(fs);
id1 = sym_mnt_fs_get_id(fs);
r = path_get_mnt_id(path, &id2);
if (r < 0) {
log_debug_errno(r, "Failed to get mount ID of '%s', ignoring: %m", path);
@ -1982,31 +1990,31 @@ int path_get_mount_info_at(
for (;;) {
struct libmnt_fs *fs;
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r == 1)
break; /* EOF */
if (r < 0)
return log_debug_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m");
if (mnt_fs_get_id(fs) != mnt_id)
if (sym_mnt_fs_get_id(fs) != mnt_id)
continue;
_cleanup_free_ char *fstype = NULL, *options = NULL, *source = NULL;
if (ret_fstype) {
fstype = strdup(strempty(mnt_fs_get_fstype(fs)));
fstype = strdup(strempty(sym_mnt_fs_get_fstype(fs)));
if (!fstype)
return log_oom_debug();
}
if (ret_options) {
options = strdup(strempty(mnt_fs_get_options(fs)));
options = strdup(strempty(sym_mnt_fs_get_options(fs)));
if (!options)
return log_oom_debug();
}
if (ret_source) {
source = strdup(strempty(mnt_fs_get_source(fs)));
source = strdup(strempty(sym_mnt_fs_get_source(fs)));
if (!source)
return log_oom_debug();
}

View File

@ -8,9 +8,19 @@
#include "bus-object.h"
#include "log.h"
#include "pretty-print.h"
#include "runtime-scope.h"
#include "service-util.h"
static int help(const char *program_path, const char *service, const char *description, bool bus_introspect) {
typedef enum HelpFlags {
HELP_WITH_BUS_INTROSPECT = 1 << 0,
HELP_WITH_RUNTIME_SCOPE = 1 << 1,
} HelpFlags;
static int help(const char *program_path,
const char *service,
const char *description,
HelpFlags flags) {
_cleanup_free_ char *link = NULL;
int r;
@ -25,6 +35,7 @@ static int help(const char *program_path, const char *service, const char *descr
" -h --help Show this help\n"
" --version Show package version\n"
"%8$s"
"%9$s"
"\nSee the %2$s for details.\n",
program_path,
link,
@ -33,7 +44,9 @@ static int help(const char *program_path, const char *service, const char *descr
ansi_highlight(),
ansi_normal(),
description,
bus_introspect ? " --bus-introspect=PATH Write D-Bus XML introspection data\n" : "");
FLAGS_SET(flags, HELP_WITH_BUS_INTROSPECT) ? " --bus-introspect=PATH Write D-Bus XML introspection data\n" : "",
FLAGS_SET(flags, HELP_WITH_RUNTIME_SCOPE) ? " --system Start service in system mode\n"
" --user Start service in user mode\n" : "");
return 0; /* No further action */
}
@ -42,17 +55,22 @@ int service_parse_argv(
const char *service,
const char *description,
const BusObjectImplementation* const* bus_objects,
RuntimeScope *runtime_scope,
int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_BUS_INTROSPECT,
ARG_SYSTEM,
ARG_USER,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "bus-introspect", required_argument, NULL, ARG_BUS_INTROSPECT },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "user", no_argument, NULL, ARG_USER },
{}
};
@ -65,7 +83,11 @@ int service_parse_argv(
switch (c) {
case 'h':
return help(argv[0], service, description, bus_objects);
return help(argv[0],
service,
description,
(bus_objects ? HELP_WITH_BUS_INTROSPECT : 0) |
(runtime_scope ? HELP_WITH_RUNTIME_SCOPE : 0));
case ARG_VERSION:
return version();
@ -76,6 +98,14 @@ int service_parse_argv(
optarg,
bus_objects);
case ARG_SYSTEM:
case ARG_USER:
if (!runtime_scope)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This service cannot be run in --system or --user mode, refusing.");
*runtime_scope = c == ARG_SYSTEM ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
break;
case '?':
return -EINVAL;

View File

@ -7,4 +7,5 @@ int service_parse_argv(
const char *service,
const char *description,
const BusObjectImplementation* const* bus_objects,
RuntimeScope *runtime_scope,
int argc, char *argv[]);

View File

@ -36,12 +36,16 @@ int swap_list_get(const char *swaps, SwapDevice **head) {
assert(head);
t = mnt_new_table();
i = mnt_new_iter(MNT_ITER_FORWARD);
r = dlopen_libmount();
if (r < 0)
return log_error_errno(r, "Cannot enumerate swap partitions, no libmount support.");
t = sym_mnt_new_table();
i = sym_mnt_new_iter(MNT_ITER_FORWARD);
if (!t || !i)
return log_oom();
r = mnt_table_parse_swaps(t, swaps);
r = sym_mnt_table_parse_swaps(t, swaps);
if (r == -ENOENT) /* no /proc/swaps is fine */
return 0;
if (r < 0)
@ -52,13 +56,13 @@ int swap_list_get(const char *swaps, SwapDevice **head) {
_cleanup_free_ SwapDevice *swap = NULL;
const char *source;
r = mnt_table_next_fs(t, i, &fs);
r = sym_mnt_table_next_fs(t, i, &fs);
if (r == 1) /* EOF */
break;
if (r < 0)
return log_error_errno(r, "Failed to get next entry from %s: %m", swaps ?: "/proc/swaps");
source = mnt_fs_get_source(fs);
source = sym_mnt_fs_get_source(fs);
if (!source)
continue;

View File

@ -16,7 +16,7 @@ executables += [
'name' : 'systemd-shutdown',
'sources' : systemd_shutdown_sources + systemd_shutdown_extract_sources,
'extract' : systemd_shutdown_extract_sources,
'dependencies' : libmount,
'dependencies' : libmount_cflags,
},
libexec_template + {
'name' : 'systemd-shutdown.standalone',
@ -28,11 +28,11 @@ executables += [
libshared_static,
libsystemd_static,
],
'dependencies' : libmount,
'dependencies' : libmount_cflags,
},
test_template + {
'sources' : files('test-umount.c'),
'objects' : ['systemd-shutdown'],
'dependencies' : libmount,
'dependencies' : libmount_cflags,
},
]

View File

@ -69,17 +69,17 @@ int mount_points_list_get(FILE *f, MountPoint **head) {
bool try_remount_ro, is_api_vfs, is_network;
_cleanup_free_ MountPoint *m = NULL;
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r == 1) /* EOF */
break;
if (r < 0)
return log_error_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m");
path = mnt_fs_get_target(fs);
path = sym_mnt_fs_get_target(fs);
if (!path)
continue;
fstype = mnt_fs_get_fstype(fs);
fstype = sym_mnt_fs_get_fstype(fs);
/* Combine the generic VFS options with the FS-specific options. Duplicates are not a problem
* here, because the only options that should come up twice are typically ro/rw, which are
@ -87,9 +87,9 @@ int mount_points_list_get(FILE *f, MountPoint **head) {
*
* Even if there are duplicates later in mount_option_mangle() they shouldn't hurt anyways as
* they override each other. */
if (!strextend_with_separator(&options, ",", mnt_fs_get_vfs_options(fs)))
if (!strextend_with_separator(&options, ",", sym_mnt_fs_get_vfs_options(fs)))
return log_oom();
if (!strextend_with_separator(&options, ",", mnt_fs_get_fs_options(fs)))
if (!strextend_with_separator(&options, ",", sym_mnt_fs_get_fs_options(fs)))
return log_oom();
/* Ignore mount points we can't unmount because they are API or because we are keeping them
@ -122,7 +122,7 @@ int mount_points_list_get(FILE *f, MountPoint **head) {
* were when the filesystem was mounted, except for the desired changes. So we
* reconstruct both here and adjust them for the later remount call too. */
r = mnt_fs_get_propagation(fs, &remount_flags);
r = sym_mnt_fs_get_propagation(fs, &remount_flags);
if (r < 0) {
log_warning_errno(r, "mnt_fs_get_propagation() failed for %s, ignoring: %m", path);
continue;

View File

@ -2127,7 +2127,7 @@ static int image_discover_and_read_metadata(ImageClass image_class, Hashmap **re
return log_error_errno(r, "Failed to discover images: %m");
HASHMAP_FOREACH(img, images) {
r = image_read_metadata(img, image_class_info[image_class].default_image_policy);
r = image_read_metadata(img, image_class_info[image_class].default_image_policy, RUNTIME_SCOPE_SYSTEM);
if (r < 0)
return log_error_errno(r, "Failed to read metadata for image %s: %m", img->name);
}

View File

@ -2079,6 +2079,7 @@ static int run(int argc, char *argv[]) {
"System update management service.",
BUS_IMPLEMENTATIONS(&manager_object,
&log_control_object),
/* runtime_scope= */ NULL,
argc, argv);
if (r <= 0)
return r;

View File

@ -215,7 +215,7 @@ simple_tests += files(
############################################################
common_test_dependencies = [
libmount,
libmount_cflags,
librt,
libseccomp_cflags,
libselinux,
@ -289,6 +289,7 @@ executables += [
'dependencies' : [
libblkid_cflags,
libkmod_cflags,
libmount_cflags,
libp11kit_cflags,
libseccomp_cflags,
],
@ -341,7 +342,7 @@ executables += [
test_template + {
'sources' : files('test-libmount.c'),
'dependencies' : [
libmount,
libmount_cflags,
threads,
],
},
@ -359,7 +360,7 @@ executables += [
},
test_template + {
'sources' : files('test-mount-util.c'),
'dependencies' : libmount,
'dependencies' : libmount_cflags,
},
test_template + {
'sources' : files('test-netlink-manual.c'),

View File

@ -49,7 +49,8 @@ int main(int argc, char *argv[]) {
if (!can_memlock())
return log_tests_skipped("Can't use mlock()");
r = enter_cgroup_subroot(NULL);
_cleanup_free_ char *cgroup_path = NULL;
r = enter_cgroup_subroot(&cgroup_path);
if (r == -ENOMEDIUM)
return log_tests_skipped("cgroupfs not available");
@ -128,6 +129,8 @@ int main(int argc, char *argv[]) {
SERVICE(u)->type = SERVICE_ONESHOT;
u->load_state = UNIT_LOADED;
CGroupRuntime *crt = ASSERT_PTR(unit_setup_cgroup_runtime(u));
unit_dump(u, stdout, NULL);
r = bpf_firewall_compile(u);
@ -135,7 +138,6 @@ int main(int argc, char *argv[]) {
return log_tests_skipped("Kernel doesn't support the necessary bpf bits (masked out via seccomp?)");
ASSERT_OK(r);
CGroupRuntime *crt = ASSERT_PTR(unit_get_cgroup_runtime(u));
ASSERT_NOT_NULL(crt->ip_bpf_ingress);
ASSERT_NOT_NULL(crt->ip_bpf_egress);

View File

@ -12,6 +12,7 @@
#include "libarchive-util.h"
#include "libaudit-util.h"
#include "libfido2-util.h"
#include "libmount-util.h"
#include "main-func.h"
#include "module-util.h"
#include "pam-util.h"
@ -58,6 +59,7 @@ static int run(int argc, char **argv) {
ASSERT_DLOPEN(dlopen_libacl, HAVE_ACL);
ASSERT_DLOPEN(dlopen_libblkid, HAVE_BLKID);
ASSERT_DLOPEN(dlopen_libseccomp, HAVE_SECCOMP);
ASSERT_DLOPEN(dlopen_libmount, true);
return 0;
}

View File

@ -32,7 +32,7 @@ static void test_libmount_unescaping_one(
/* We allow this call and the checks below to fail in some cases. See the case definitions below. */
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r != 0 && may_fail) {
log_error_errno(r, "mnt_table_next_fs failed: %m");
return;
@ -41,8 +41,8 @@ static void test_libmount_unescaping_one(
assert_se(x = cescape(string));
assert_se(source = mnt_fs_get_source(fs));
assert_se(target = mnt_fs_get_target(fs));
assert_se(source = sym_mnt_fs_get_source(fs));
assert_se(target = sym_mnt_fs_get_target(fs));
assert_se(cs = cescape(source));
assert_se(ct = cescape(target));
@ -63,7 +63,7 @@ static void test_libmount_unescaping_one(
assert_se(may_fail || streq(source, expected_source));
assert_se(may_fail || streq(target, expected_target));
assert_se(mnt_table_next_fs(table, iter, &fs) == 1);
assert_se(sym_mnt_table_next_fs(table, iter, &fs) == 1);
}
TEST(libmount_unescaping) {

View File

@ -361,7 +361,7 @@ TEST(umount_recursive) {
for (;;) {
struct libmnt_fs *fs;
r = mnt_table_next_fs(table, iter, &fs);
r = sym_mnt_table_next_fs(table, iter, &fs);
if (r == 1)
break;
if (r < 0) {
@ -369,7 +369,7 @@ TEST(umount_recursive) {
_exit(EXIT_FAILURE);
}
log_debug("left after complete umount: %s", mnt_fs_get_target(fs));
log_debug("left after complete umount: %s", sym_mnt_fs_get_target(fs));
}
_exit(EXIT_SUCCESS);

View File

@ -1164,6 +1164,7 @@ static int run(int argc, char *argv[]) {
"Manage the system clock and timezone and NTP enablement.",
BUS_IMPLEMENTATIONS(&manager_object,
&log_control_object),
/* runtime_scope= */ NULL,
argc, argv);
if (r <= 0)
return r;

View File

@ -151,6 +151,7 @@ static int run(int argc, char *argv[]) {
r = service_parse_argv("systemd-timesyncd.service",
"Network time synchronization",
BUS_IMPLEMENTATIONS(&manager_object, &log_control_object),
/* runtime_scope= */ NULL,
argc, argv);
if (r <= 0)
return r;

View File

@ -70,7 +70,7 @@ foreach dirname : [
'TEST-46-HOMED',
'TEST-50-DISSECT',
'TEST-52-HONORFIRSTSHUTDOWN',
'TEST-53-ISSUE-16347',
'TEST-53-TIMER',
'TEST-54-CREDS',
'TEST-55-OOMD',
'TEST-58-REPART',

View File

@ -3,8 +3,6 @@
set -eux
set -o pipefail
systemd-analyze log-level debug
cat >/run/systemd/system/floodme@.service <<EOF
[Service]
ExecStart=true

View File

@ -4,8 +4,6 @@
set -eux
set -o pipefail
systemd-analyze log-level debug
test_quotas() {
local directory="$1"
@ -90,6 +88,4 @@ EOF
test_quotas "/var/lib/private" "StateDirectory=quotadir" "StateDirectoryQuota=1%"
systemd-analyze log-level info
touch /testok

View File

@ -20,8 +20,6 @@ at_exit() {
trap at_exit EXIT
systemctl service-log-level systemd-importd debug
# Mount tmpfs over /var/lib/confexts to not pollute the image
mkdir -p /var/lib/confexts
mount -t tmpfs tmpfs /var/lib/confexts -o mode=755

View File

@ -20,8 +20,6 @@ at_exit() {
trap at_exit EXIT
systemctl service-log-level systemd-machined debug
systemctl service-log-level systemd-importd debug
# per request in https://github.com/systemd/systemd/pull/35117
systemctl edit --runtime --stdin 'systemd-nspawn@.service' --drop-in=debug.conf <<EOF
[Service]

View File

@ -11,8 +11,6 @@ if [[ "$(get_cgroup_hierarchy)" != unified ]]; then
exit 0
fi
systemd-analyze log-level debug
# Multiple level process tree, parent process stays up
cat >/tmp/test19-exit-cgroup.sh <<EOF
#!/usr/bin/env bash
@ -98,6 +96,3 @@ systemd-run --wait \
--property="Type=notify" \
--property="ExitType=cgroup" \
/tmp/test19-exit-cgroup-parentless.sh 'systemctl kill --signal 9 six'
systemd-analyze log-level info

View File

@ -0,0 +1,46 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
set -o pipefail
# Test that the service is not invoked if the cgroup cannot be created.
# It seems openSUSE kernel (at least kernel-default-6.16.8-1.1.x86_64.rpm) has a
# bag in kernel oom killer or clone3 syscall, and spawning executor on a cgroup
# with too small MemoryMax= triggers infinite loop of OOM kill, and posix_spawn()
# will never return, and the service manager will stuck.
####
# [ 119.776797] systemd invoked oom-killer: gfp_mask=0xcc0(GFP_KERNEL), order=0, oom_score_adj=0
# [ 119.776859] CPU: 1 UID: 0 PID: 1472 Comm: systemd Not tainted 6.16.8-1-default #1 PREEMPT(voluntary) openSUSE Tumbleweed 6c85865973e4ae641870ed68afe8933a6986c974
# [ 119.776865] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.17.0-5.fc42 04/01/2014
# [ 119.776867] Call Trace:
# (snip)
# [ 119.778126] Out of memory and no killable processes...
####
# On other distributions, the oom killer is triggered, but clone3 immediately
# fails with ENOMEM, and such problematic loop does not happen.
. /etc/os-release
if [[ "$ID" =~ opensuse ]]; then
echo "Skipping cgroup test with too small MemoryMax= setting on openSUSE."
exit 0
fi
cat >/run/systemd/system/testslice.slice <<EOF
[Slice]
MemoryMax=1
EOF
cat >/run/systemd/system/testservice.service <<EOF
[Service]
Type=oneshot
ExecStart=cat /proc/self/cgroup
Slice=testslice.slice
EOF
systemctl daemon-reload
(! systemctl start testservice.service)
rm /run/systemd/system/testslice.slice
rm /run/systemd/system/testservice.service
exit 0

View File

@ -5,8 +5,6 @@ set -o pipefail
# Test ExecReload= (PR #13098)
systemd-analyze log-level debug
export SYSTEMD_PAGER=
SERVICE_PATH="$(mktemp /etc/systemd/system/execreloadXXX.service)"
SERVICE_NAME="${SERVICE_PATH##*/}"
@ -57,5 +55,3 @@ systemctl status "$SERVICE_NAME"
systemctl reload "$SERVICE_NAME"
systemctl status "$SERVICE_NAME"
systemctl stop "$SERVICE_NAME"
systemd-analyze log-level info

View File

@ -4,8 +4,6 @@ set -eux
# Test that ExecStopPost= is always run
systemd-analyze log-level debug
systemd-run --unit=simple1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple \
-p ExecStopPost='touch /run/simple1' true
test -f /run/simple1
@ -101,5 +99,3 @@ test -f /run/idle1
(! systemd-run --unit=idle2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle \
-p ExecStopPost='touch /run/idle2' false)
test -f /run/idle2
systemd-analyze log-level info

View File

@ -18,8 +18,6 @@ mkdir /tmp/test-extra-fd
echo "Hello" > /tmp/test-extra-fd/1.txt
echo "Extra" > /tmp/test-extra-fd/2.txt
systemd-analyze log-level debug
# Open files and assign FD to variables
exec {TEST_FD1}</tmp/test-extra-fd/1.txt
exec {TEST_FD2}</tmp/test-extra-fd/2.txt
@ -61,5 +59,3 @@ assert_eq "$(systemctl show -P Result "$TEST_UNIT")" "success"
assert_eq "$(systemctl show -P ExecMainStatus "$TEST_UNIT")" "0"
systemctl stop "$TEST_UNIT"
systemctl log-level info

View File

@ -6,9 +6,6 @@ set -o pipefail
# Test JoinsNamespaceOf= with PrivateTmp=yes
systemd-analyze log-level debug
systemd-analyze log-target journal
# simple case
systemctl start TEST-23-UNIT-FILE-joins-namespace-of-1.service
systemctl start TEST-23-UNIT-FILE-joins-namespace-of-2.service
@ -27,5 +24,3 @@ systemctl start TEST-23-UNIT-FILE-joins-namespace-of-8.service
systemctl start TEST-23-UNIT-FILE-joins-namespace-of-9.service
systemctl stop TEST-23-UNIT-FILE-joins-namespace-of-6.service
systemctl stop TEST-23-UNIT-FILE-joins-namespace-of-8.service
systemd-analyze log-level info

View File

@ -5,8 +5,6 @@ set -o pipefail
# Test StandardOutput=file:
systemd-analyze log-level debug
systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-one \
-p StandardOutput=file:/tmp/stdout \
-p StandardError=file:/tmp/stderr \
@ -56,5 +54,3 @@ EOF
cmp /tmp/stderr <<EOF
b
EOF
systemd-analyze log-level info

View File

@ -6,9 +6,6 @@ set -o pipefail
# Test OnSuccess= + Uphold= + PropagatesStopTo= + BindsTo=
systemd-analyze log-level debug
systemd-analyze log-target journal
# Idea is this:
# 1. we start TEST-23-UNIT-FILE-success.service
# 2. which through OnSuccess= starts TEST-23-UNIT-FILE-fail.service,
@ -102,5 +99,3 @@ for _ in {1..120}; do
fi
done
[[ "$sigrtmin1" == 1 ]]
systemd-analyze log-level info

View File

@ -5,8 +5,6 @@ set -o pipefail
# Test ExecXYZEx= service unit dbus hookups
systemd-analyze log-level debug
declare -A property
property[1_one]=ExecCondition
@ -31,5 +29,3 @@ for c in "${!property[@]}"; do
systemctl show -p "${property[$c]}" "$c" | grep -F "path=echo ; argv[]=echo \${$c} ; ignore_errors=no"
systemctl show -p "${property[$c]}Ex" "$c" | grep -F "path=echo ; argv[]=echo \${$c} ; flags=no-env-expand"
done
systemd-analyze log-level info

View File

@ -11,8 +11,6 @@ set -o pipefail
# wait this many secs for each test service to succeed in what is being tested
MAX_SECS=60
systemctl log-level debug
# test one: Restart=on-failure should restart the service
(! systemd-run --unit=oneshot-restart-one -p Type=oneshot -p Restart=on-failure bash -c "exit 1")
@ -97,5 +95,3 @@ EOF
systemctl disable "$UNIT_NAME"
rm "$TMP_FILE" /run/systemd/system/{"$UNIT_NAME","$ONSUCCESS_UNIT_NAME"} "$FIFO_FILE"
systemctl log-level info

View File

@ -14,8 +14,6 @@ at_exit() {
trap at_exit EXIT
systemctl log-level debug
# Existing files
mkdir /tmp/test-open-file
@ -51,5 +49,3 @@ systemctl stop TEST-23-UNIT-FILE-openfile-server.socket
assert_rc 202 systemd-run -p OpenFile=/run/missing/foo:missing-file:read-only --wait true
assert_rc 0 systemd-run -p OpenFile=/run/missing/foo:missing-file:read-only,graceful --wait true
systemctl log-level info

View File

@ -5,8 +5,6 @@ set -o pipefail
# Test OnSuccess=/OnFailure= in combination
systemd-analyze log-level debug
# Start-up should fail, but the automatic restart should fix it
(! systemctl start success-failure-test )
@ -45,5 +43,3 @@ done
test "$(systemctl is-active success-failure-test-failure)" = "inactive"
systemctl stop success-failure-test success-failure-test-success
systemd-analyze log-level info

View File

@ -5,8 +5,6 @@ set -o pipefail
# Test Type=exec
systemd-analyze log-level debug
# Create a binary for which execve() will fail
touch /tmp/brokenbinary
chmod +x /tmp/brokenbinary
@ -59,5 +57,3 @@ busctl call \
sleep 0 true \
sleep 2 sleep 1 true \
0)
systemd-analyze log-level info

View File

@ -3,8 +3,6 @@
set -eux
set -o pipefail
systemd-analyze log-level debug
systemctl disable --now systemd-timesyncd.service
timedatectl set-timezone Europe/Berlin
@ -61,6 +59,4 @@ while test ! -f /tmp/timezone-changed-alternate-path-2 ; do sleep .5 ; done
rm /run/systemd/system.conf /run/systemd/system/systemd-timedated.service.d/override.conf
systemctl daemon-reload
systemd-analyze log-level info
touch /testok

View File

@ -10,8 +10,6 @@ set -o pipefail
# kernels where the concept was still new.
if test -f /sys/fs/cgroup/system.slice/TEST-32-OOMPOLICY.service/memory.oom.group; then
systemd-analyze log-level debug
# Run a service that is guaranteed to be the first candidate for OOM killing
systemd-run --unit=oomtest.service \
-p Type=exec -p OOMScoreAdjust=1000 -p OOMPolicy=stop -p MemoryAccounting=yes \
@ -29,8 +27,6 @@ if test -f /sys/fs/cgroup/system.slice/TEST-32-OOMPOLICY.service/memory.oom.grou
RESULT="$(systemctl show -P Result oomtest.service)"
test "$RESULT" = "oom-kill"
systemd-analyze log-level info
fi
touch /testok

View File

@ -3,8 +3,6 @@
set -eux
set -o pipefail
systemd-analyze log-level debug
test_directory() {
local directory="$1"
local path="$2"
@ -239,6 +237,4 @@ if systemd-analyze compare-versions "$(uname -r)" ge 5.12; then
test_check_idmapped_mounts_root
fi
systemd-analyze log-level info
touch /testok

View File

@ -21,9 +21,6 @@ at_exit() {
trap at_exit EXIT
systemd-analyze log-level debug
systemd-analyze log-target journal
# Log files
straceLog='strace.log'
journalLog='journal.log'
@ -352,6 +349,4 @@ fi
rm -rf "$confDir"
systemctl daemon-reload
systemd-analyze log-level info
touch /testok

View File

@ -12,8 +12,6 @@ if [[ -n "${COVERAGE_BUILD_DIR:-}" ]]; then
exit 77
fi
systemd-analyze log-level debug
unit=TEST-38-FREEZER-sleep.service
start_test_service() {

View File

@ -13,13 +13,9 @@ fi
install_extension_images
systemd-analyze log-level debug
runas testuser systemd-run --wait --user --unit=test-private-users \
-p PrivateUsers=yes -P echo hello
runas testuser systemctl --user log-level debug
runas testuser systemd-run --wait --user --unit=test-private-tmp-innerfile \
-p PrivateTmp=yes \
-P touch /tmp/innerfile.txt
@ -140,6 +136,4 @@ if unshare --mount --user --map-root-user mount -t overlay overlay /tmp/c -o low
grep PORTABLE_PREFIXES=app1 /usr/lib/extension-release.d/extension-release.app2
fi
systemd-analyze log-level info
touch /testok

View File

@ -2,8 +2,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
systemd-analyze log-level debug
journalctl --list-namespaces -o json | jq .
systemd-run --wait -p LogNamespace=foobar echo "hello world"
@ -25,6 +23,4 @@ journalctl --list-namespaces -o json | jq .
grep "^hello world$" /tmp/hello-world
(! grep "^hello world$" /tmp/no-hello-world)
systemd-analyze log-level info
touch /testok

View File

@ -40,9 +40,6 @@ FSTYPE="$(stat --file-system --format "%T" /)"
systemctl start systemd-homed.service systemd-userdbd.socket
systemd-analyze log-level debug
systemctl service-log-level systemd-homed debug
# Create a tmpfs to use as backing store for the home dir. That way we can enforce a size limit nicely.
mkdir -p /home
mount -t tmpfs tmpfs /home -o size=290M
@ -848,6 +845,4 @@ homectl inspect matchtest
homectl inspect matchtest | grep "Area: quux3"
homectl remove matchtest
systemd-analyze log-level info
touch /testok

View File

@ -3,8 +3,6 @@
set -ex
set -o pipefail
systemd-analyze log-level debug
systemctl enable test-honor-first-shutdown.service
systemctl start test-honor-first-shutdown.service

View File

@ -0,0 +1,97 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# When deserializing a serialized timer unit with RandomizedDelaySec= set, systemd should use the last
# inactive exit timestamp instead of current realtime to calculate the new next elapse, so the timer unit
# actually runs in the given calendar window.
#
# Provides coverage for:
# - https://github.com/systemd/systemd/issues/18678
# - https://github.com/systemd/systemd/pull/27752
set -eux
set -o pipefail
# shellcheck source=test/units/test-control.sh
. "$(dirname "$0")"/util.sh
UNIT_NAME="timer-RandomizedDelaySec-$RANDOM"
TARGET_TS="$(date --date="tomorrow 00:10")"
TARGET_TS_S="$(date --date="$TARGET_TS" "+%s")"
# Maximum possible next elapse timestamp: $TARGET_TS (OnCalendar=) + 22 hours (RandomizedDelaySec=)
MAX_NEXT_ELAPSE_REALTIME_S="$((TARGET_TS_S + 22 * 60 * 60))"
MAX_NEXT_ELAPSE_REALTIME="$(date --date="@$MAX_NEXT_ELAPSE_REALTIME_S")"
# Let's make sure to return the date & time back to the original state once we're done with our time
# shenigans. One way to do this would be to use hwclock, but the RTC in VMs can be unreliable or slow to
# respond, causing unexpected test fails/timeouts.
#
# Instead, let's save the realtime timestamp before we start with the test together with a current monotonic
# timestamp, after the test ends take the difference between the current monotonic timestamp and the "start"
# one, add it to the originally saved realtime timestamp, and finally use that timestamp to set the system
# time. This should advance the system time by the amount of time the test actually ran, and hence restore it
# to some sane state after the time jumps performed by the test. It won't be perfect, but it should be close
# enough for our needs.
START_REALTIME="$(date "+%s")"
START_MONOTONIC="$(cut -d . -f 1 /proc/uptime)"
at_exit() {
: "Restore the system date to a sane state"
END_MONOTONIC="$(cut -d . -f 1 /proc/uptime)"
date --set="@$((START_REALTIME + END_MONOTONIC - START_MONOTONIC))"
}
trap at_exit EXIT
# Set some predictable time so we can schedule the first timer elapse in a deterministic-ish way
date --set="23:00"
# Setup
cat >"/run/systemd/system/$UNIT_NAME.timer" <<EOF
[Timer]
# Run this timer daily, ten minutes after midnight
OnCalendar=*-*-* 00:10:00
RandomizedDelaySec=22h
AccuracySec=1ms
EOF
cat >"/run/systemd/system/$UNIT_NAME.service" <<EOF
[Service]
ExecStart=echo "Hello world"
EOF
systemctl daemon-reload
check_elapse_timestamp() {
systemctl status "$UNIT_NAME.timer"
systemctl show -p InactiveExitTimestamp "$UNIT_NAME.timer"
NEXT_ELAPSE_REALTIME="$(systemctl show -P NextElapseUSecRealtime "$UNIT_NAME.timer")"
NEXT_ELAPSE_REALTIME_S="$(date --date="$NEXT_ELAPSE_REALTIME" "+%s")"
: "Next elapse timestamp should be $TARGET_TS <= $NEXT_ELAPSE_REALTIME <= $MAX_NEXT_ELAPSE_REALTIME"
assert_ge "$NEXT_ELAPSE_REALTIME_S" "$TARGET_TS_S"
assert_le "$NEXT_ELAPSE_REALTIME_S" "$MAX_NEXT_ELAPSE_REALTIME_S"
}
# Restart the timer unit and check the initial next elapse timestamp
: "Initial next elapse timestamp"
systemctl restart "$UNIT_NAME.timer"
check_elapse_timestamp
# Bump the system date to 1 minute after the original calendar timer would've expired (without any random
# delay!) - systemd should recalculate the next elapse timestamp with a new randomized delay, but it should
# use the original inactive exit timestamp as a "base", so the final timestamp should not end up beyond the
# original calendar timestamp + randomized delay range.
#
# Similarly, do the same check after doing daemon-reload, as that also forces systemd to recalculate the next
# elapse timestamp (this goes through a slightly different codepath that actually contained the original
# issue).
: "Next elapse timestamp after time jump"
date -s "tomorrow 00:11"
check_elapse_timestamp
: "Next elapse timestamp after daemon-reload"
systemctl daemon-reload
check_elapse_timestamp
# Cleanup
systemctl stop "$UNIT_NAME".{timer,service}
rm -f "/run/systemd/system/$UNIT_NAME".{timer,service}
systemctl daemon-reload

View File

@ -3,10 +3,9 @@
set -eux
set -o pipefail
: >/failed
# Reset host date to current time, 3 days in the past.
date -s "-3 days"
trap 'date -s "+3 days"' EXIT
# Run a timer for every 15 minutes.
systemd-run --unit test-timer --on-calendar "*:0/15:0" true
@ -17,15 +16,12 @@ now=$(date +%s)
time_delta=$((next_elapsed - now))
# Check that the timer will elapse in less than 20 minutes.
((0 < time_delta && time_delta < 1200)) || {
if [[ "$time_delta" -lt 0 || "$time_delta" -gt 1200 ]]; then
echo 'Timer elapse outside of the expected 20 minute window.'
echo " next_elapsed=${next_elapsed}"
echo " now=${now}"
echo " time_delta=${time_delta}"
echo ''
} >>/failed
echo
if test ! -s /failed ; then
rm -f /failed
touch /testok
exit 1
fi

View File

@ -0,0 +1,77 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# Restarting an already elapsed timer shouldn't immediately trigger the corresponding service unit.
#
# Provides coverage for:
# - https://github.com/systemd/systemd/issues/31231
# - https://github.com/systemd/systemd/issues/35805
set -eux
set -o pipefail
# shellcheck source=test/units/test-control.sh
. "$(dirname "$0")"/util.sh
UNIT_NAME="timer-restart-$RANDOM"
TEST_MESSAGE="Hello from timer $RANDOM"
# Setup
cat >"/run/systemd/system/$UNIT_NAME.timer" <<EOF
[Timer]
OnCalendar=$(date --date="+1 hour" "+%Y-%m-%d %H:%M:%S")
AccuracySec=1s
EOF
cat >"/run/systemd/system/$UNIT_NAME.service" <<EOF
[Service]
ExecStart=echo "$TEST_MESSAGE"
EOF
systemctl daemon-reload
JOURNAL_TS="$(date "+%s")"
# Paranoia check that the test message is not already in the logs
(! journalctl -p info --since="@$JOURNAL_TS" --unit="$UNIT_NAME" --grep="$TEST_MESSAGE")
# Restart time timer and move time forward by 2 hours to trigger the timer
systemctl restart "$UNIT_NAME.timer"
systemctl status "$UNIT_NAME.timer"
date -s '+2 hours'
trap 'date -s "-2 hours"' EXIT
sleep 1
systemctl status "$UNIT_NAME.timer"
assert_eq "$(journalctl -q -p info --since="@$JOURNAL_TS" --unit="$UNIT_NAME" --grep="$TEST_MESSAGE" | wc -l)" "1"
# Restarting the timer unit shouldn't trigger neither the timer nor the service, so these
# fields should remain constant through the following tests
SERVICE_INV_ID="$(systemctl show --property=InvocationID "$UNIT_NAME.service")"
TIMER_LAST_TRIGGER="$(systemctl show --property=LastTriggerUSec "$UNIT_NAME.timer")"
# Now restart the timer and check if the timer and the service weren't triggered again
systemctl restart "$UNIT_NAME.timer"
sleep 5
assert_eq "$(journalctl -q -p info --since="@$JOURNAL_TS" --unit="$UNIT_NAME" --grep="$TEST_MESSAGE" | wc -l)" "1"
assert_eq "$SERVICE_INV_ID" "$(systemctl show --property=InvocationID "$UNIT_NAME.service")"
assert_eq "$TIMER_LAST_TRIGGER" "$(systemctl show --property=LastTriggerUSec "$UNIT_NAME.timer")"
# Set the timer into the past, restart it, and again check if it wasn't triggered
TIMER_TS="$(date --date="-1 day" "+%Y-%m-%d %H:%M:%S")"
mkdir "/run/systemd/system/$UNIT_NAME.timer.d/"
cat >"/run/systemd/system/$UNIT_NAME.timer.d/99-override.conf" <<EOF
[Timer]
OnCalendar=$TIMER_TS
EOF
systemctl daemon-reload
systemctl status "$UNIT_NAME.timer"
assert_in "OnCalendar=$TIMER_TS" "$(systemctl show -P TimersCalendar "$UNIT_NAME".timer)"
systemctl restart "$UNIT_NAME.timer"
sleep 5
assert_eq "$(journalctl -q -p info --since="@$JOURNAL_TS" --unit="$UNIT_NAME" --grep="$TEST_MESSAGE" | wc -l)" "1"
assert_eq "$SERVICE_INV_ID" "$(systemctl show --property=InvocationID "$UNIT_NAME.service")"
assert_eq "$TIMER_LAST_TRIGGER" "$(systemctl show --property=LastTriggerUSec "$UNIT_NAME.timer")"
# Cleanup
systemctl stop "$UNIT_NAME".{timer,service}
rm -f "/run/systemd/system/$UNIT_NAME".{timer,service}
systemctl daemon-reload

11
test/units/TEST-53-TIMER.sh Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
# shellcheck source=test/units/test-control.sh
. "$(dirname "$0")"/test-control.sh
run_subtests
touch /testok

Some files were not shown because too many files have changed in this diff Show More