Merge pull request #28836 from msekletar/aux-scope

core/manager: add dbus API to create auxiliary scope from running service
This commit is contained in:
Yu Watanabe
2024-01-04 04:52:39 +09:00
committed by GitHub
7 changed files with 714 additions and 0 deletions

View File

@@ -277,6 +277,11 @@ node /org/freedesktop/systemd1 {
GetDynamicUsers(out a(us) users);
DumpUnitFileDescriptorStore(in s name,
out a(suuutuusu) entries);
StartAuxiliaryScope(in s name,
in ah pidfds,
in t flags,
in a(sv) properties,
out o job);
signals:
UnitNew(s id,
o unit);
@@ -990,6 +995,8 @@ node /org/freedesktop/systemd1 {
<variablelist class="dbus-method" generated="True" extra-ref="DumpUnitFileDescriptorStore()"/>
<variablelist class="dbus-method" generated="True" extra-ref="StartAuxiliaryScope()"/>
<variablelist class="dbus-signal" generated="True" extra-ref="UnitNew"/>
<variablelist class="dbus-signal" generated="True" extra-ref="UnitRemoved"/>
@@ -1567,6 +1574,13 @@ node /org/freedesktop/systemd1 {
file descriptors currently in the file descriptor store of the specified unit. This call is equivalent
to <function>DumpFileDescriptorStore()</function> on the
<interfacename>org.freedesktop.systemd1.Service</interfacename>. For further details, see below.</para>
<para><function>StartAuxiliaryScope()</function> creates a new scope unit from a service where calling
process resides. Set of processes that will be migrated to newly created scope is passed in as an array
of pidfds. This is useful for creating auxiliary scopes that should contain worker processes and their lifecycle
shouldn't be bound to a lifecycle of the service, e.g. they should continue running after the restart
of the service. Note that the main PID of the service can not be migrated to an auxiliary scope.
Also, <varname>flags</varname> argument must be 0 and is reserved for future extensions.</para>
</refsect2>
<refsect2>
@@ -11826,6 +11840,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<function>QueueSignalUnit()</function>,
<function>SoftReboot()</function>, and
<function>DumpUnitFileDescriptorStore()</function> were added in version 254.</para>
<para><function>StartAuxiliaryScope()</function> was added in version 256.</para>
</refsect2>
<refsect2>
<title>Unit Objects</title>

View File

@@ -33,6 +33,7 @@
#include "process-util.h"
#include "procfs-util.h"
#include "restrict-ifaces.h"
#include "set.h"
#include "special.h"
#include "stdio-util.h"
#include "string-table.h"
@@ -189,6 +190,313 @@ void cgroup_context_init(CGroupContext *c) {
};
}
int cgroup_context_add_io_device_weight_dup(CGroupContext *c, CGroupIODeviceWeight *w) {
_cleanup_free_ CGroupIODeviceWeight *n = NULL;
assert(c);
assert(w);
n = new0(CGroupIODeviceWeight, 1);
if (!n)
return -ENOMEM;
n->path = strdup(w->path);
if (!n->path)
return -ENOMEM;
n->weight = w->weight;
LIST_PREPEND(device_weights, c->io_device_weights, TAKE_PTR(n));
return 0;
}
int cgroup_context_add_io_device_limit_dup(CGroupContext *c, CGroupIODeviceLimit *l) {
_cleanup_free_ CGroupIODeviceLimit *n = NULL;
assert(c);
assert(l);
n = new0(CGroupIODeviceLimit, 1);
if (!l)
return -ENOMEM;
n->path = strdup(l->path);
if (!n->path)
return -ENOMEM;
for (CGroupIOLimitType type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++)
n->limits[type] = l->limits[type];
LIST_PREPEND(device_limits, c->io_device_limits, TAKE_PTR(n));
return 0;
}
int cgroup_context_add_io_device_latency_dup(CGroupContext *c, CGroupIODeviceLatency *l) {
_cleanup_free_ CGroupIODeviceLatency *n = NULL;
assert(c);
assert(l);
n = new0(CGroupIODeviceLatency, 1);
if (!n)
return -ENOMEM;
n->path = strdup(l->path);
if (!n->path)
return -ENOMEM;
n->target_usec = l->target_usec;
LIST_PREPEND(device_latencies, c->io_device_latencies, TAKE_PTR(n));
return 0;
}
int cgroup_context_add_block_io_device_weight_dup(CGroupContext *c, CGroupBlockIODeviceWeight *w) {
_cleanup_free_ CGroupBlockIODeviceWeight *n = NULL;
assert(c);
assert(w);
n = new0(CGroupBlockIODeviceWeight, 1);
if (!n)
return -ENOMEM;
n->path = strdup(w->path);
if (!n->path)
return -ENOMEM;
n->weight = w->weight;
LIST_PREPEND(device_weights, c->blockio_device_weights, TAKE_PTR(n));
return 0;
}
int cgroup_context_add_block_io_device_bandwidth_dup(CGroupContext *c, CGroupBlockIODeviceBandwidth *b) {
_cleanup_free_ CGroupBlockIODeviceBandwidth *n = NULL;
assert(c);
assert(b);
n = new0(CGroupBlockIODeviceBandwidth, 1);
if (!n)
return -ENOMEM;
*n = (CGroupBlockIODeviceBandwidth) {
.rbps = b->rbps,
.wbps = b->wbps,
};
LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, TAKE_PTR(n));
return 0;
}
int cgroup_context_add_device_allow_dup(CGroupContext *c, CGroupDeviceAllow *a) {
_cleanup_free_ CGroupDeviceAllow *n = NULL;
assert(c);
assert(a);
n = new0(CGroupDeviceAllow, 1);
if (!n)
return -ENOMEM;
n->path = strdup(a->path);
if (!n->path)
return -ENOMEM;
n->permissions = a->permissions;
LIST_PREPEND(device_allow, c->device_allow, TAKE_PTR(n));
return 0;
}
static int cgroup_context_add_socket_bind_item_dup(CGroupContext *c, CGroupSocketBindItem *i, CGroupSocketBindItem *h) {
_cleanup_free_ CGroupSocketBindItem *n = NULL;
assert(c);
assert(i);
n = new0(CGroupSocketBindItem, 1);
if (!n)
return -ENOMEM;
*n = (CGroupSocketBindItem) {
.address_family = i->address_family,
.ip_protocol = i->ip_protocol,
.nr_ports = i->nr_ports,
.port_min = i->port_min,
};
LIST_PREPEND(socket_bind_items, h, TAKE_PTR(n));
return 0;
}
int cgroup_context_add_socket_bind_item_allow_dup(CGroupContext *c, CGroupSocketBindItem *i) {
return cgroup_context_add_socket_bind_item_dup(c, i, c->socket_bind_allow);
}
int cgroup_context_add_socket_bind_item_deny_dup(CGroupContext *c, CGroupSocketBindItem *i) {
return cgroup_context_add_socket_bind_item_dup(c, i, c->socket_bind_deny);
}
int cgroup_context_copy(CGroupContext *dst, const CGroupContext *src) {
struct in_addr_prefix *i;
char *iface;
int r;
assert(src);
assert(dst);
dst->cpu_accounting = src->cpu_accounting;
dst->io_accounting = src->io_accounting;
dst->blockio_accounting = src->blockio_accounting;
dst->memory_accounting = src->memory_accounting;
dst->tasks_accounting = src->tasks_accounting;
dst->ip_accounting = src->ip_accounting;
dst->memory_oom_group = dst->memory_oom_group;
dst->cpu_weight = src->cpu_weight;
dst->startup_cpu_weight = src->startup_cpu_weight;
dst->cpu_quota_per_sec_usec = src->cpu_quota_per_sec_usec;
dst->cpu_quota_period_usec = src->cpu_quota_period_usec;
dst->cpuset_cpus = src->cpuset_cpus;
dst->startup_cpuset_cpus = src->startup_cpuset_cpus;
dst->cpuset_mems = src->cpuset_mems;
dst->startup_cpuset_mems = src->startup_cpuset_mems;
dst->io_weight = src->io_weight;
dst->startup_io_weight = src->startup_io_weight;
LIST_FOREACH_BACKWARDS(device_weights, w, LIST_FIND_TAIL(device_weights, src->io_device_weights)) {
r = cgroup_context_add_io_device_weight_dup(dst, w);
if (r < 0)
return r;
}
LIST_FOREACH_BACKWARDS(device_limits, l, LIST_FIND_TAIL(device_limits, src->io_device_limits)) {
r = cgroup_context_add_io_device_limit_dup(dst, l);
if (r < 0)
return r;
}
LIST_FOREACH_BACKWARDS(device_latencies, l, LIST_FIND_TAIL(device_latencies, src->io_device_latencies)) {
r = cgroup_context_add_io_device_latency_dup(dst, l);
if (r < 0)
return r;
}
dst->default_memory_min = src->default_memory_min;
dst->default_memory_low = src->default_memory_low;
dst->default_startup_memory_low = src->default_startup_memory_low;
dst->memory_min = src->memory_min;
dst->memory_low = src->memory_low;
dst->startup_memory_low = src->startup_memory_low;
dst->memory_high = src->memory_high;
dst->startup_memory_high = src->startup_memory_high;
dst->memory_max = src->memory_max;
dst->startup_memory_max = src->startup_memory_max;
dst->memory_swap_max = src->memory_swap_max;
dst->startup_memory_swap_max = src->startup_memory_swap_max;
dst->memory_zswap_max = src->memory_zswap_max;
dst->startup_memory_zswap_max = src->startup_memory_zswap_max;
dst->default_memory_min_set = src->default_memory_min_set;
dst->default_memory_low_set = src->default_memory_low_set;
dst->default_startup_memory_low_set = src->default_startup_memory_low_set;
dst->memory_min_set = src->memory_min_set;
dst->memory_low_set = src->memory_low_set;
dst->startup_memory_low_set = src->startup_memory_low_set;
dst->startup_memory_high_set = src->startup_memory_high_set;
dst->startup_memory_max_set = src->startup_memory_max_set;
dst->startup_memory_swap_max_set = src->startup_memory_swap_max_set;
dst->startup_memory_zswap_max_set = src->startup_memory_zswap_max_set;
SET_FOREACH(i, src->ip_address_allow) {
r = in_addr_prefix_add(&dst->ip_address_allow, i);
if (r < 0)
return r;
}
SET_FOREACH(i, src->ip_address_deny) {
r = in_addr_prefix_add(&dst->ip_address_deny, i);
if (r < 0)
return r;
}
dst->ip_address_allow_reduced = src->ip_address_allow_reduced;
dst->ip_address_deny_reduced = src->ip_address_deny_reduced;
if (!strv_isempty(src->ip_filters_ingress)) {
dst->ip_filters_ingress = strv_copy(src->ip_filters_ingress);
if (!dst->ip_filters_ingress)
return -ENOMEM;
}
if (!strv_isempty(src->ip_filters_egress)) {
dst->ip_filters_egress = strv_copy(src->ip_filters_egress);
if (!dst->ip_filters_egress)
return -ENOMEM;
}
LIST_FOREACH_BACKWARDS(programs, l, LIST_FIND_TAIL(programs, src->bpf_foreign_programs)) {
r = cgroup_context_add_bpf_foreign_program_dup(dst, l);
if (r < 0)
return r;
}
SET_FOREACH(iface, src->restrict_network_interfaces) {
r = set_put_strdup(&dst->restrict_network_interfaces, iface);
if (r < 0)
return r;
}
dst->restrict_network_interfaces_is_allow_list = src->restrict_network_interfaces_is_allow_list;
dst->cpu_shares = src->cpu_shares;
dst->startup_cpu_shares = src->startup_cpu_shares;
dst->blockio_weight = src->blockio_weight;
dst->startup_blockio_weight = src->startup_blockio_weight;
LIST_FOREACH_BACKWARDS(device_weights, l, LIST_FIND_TAIL(device_weights, src->blockio_device_weights)) {
r = cgroup_context_add_block_io_device_weight_dup(dst, l);
if (r < 0)
return r;
}
LIST_FOREACH_BACKWARDS(device_bandwidths, l, LIST_FIND_TAIL(device_bandwidths, src->blockio_device_bandwidths)) {
r = cgroup_context_add_block_io_device_bandwidth_dup(dst, l);
if (r < 0)
return r;
}
dst->memory_limit = src->memory_limit;
dst->device_policy = src->device_policy;
LIST_FOREACH_BACKWARDS(device_allow, l, LIST_FIND_TAIL(device_allow, src->device_allow)) {
r = cgroup_context_add_device_allow_dup(dst, l);
if (r < 0)
return r;
}
LIST_FOREACH_BACKWARDS(socket_bind_items, l, LIST_FIND_TAIL(socket_bind_items, src->socket_bind_allow)) {
r = cgroup_context_add_socket_bind_item_allow_dup(dst, l);
if (r < 0)
return r;
}
LIST_FOREACH_BACKWARDS(socket_bind_items, l, LIST_FIND_TAIL(socket_bind_items, src->socket_bind_deny)) {
r = cgroup_context_add_socket_bind_item_deny_dup(dst, l);
if (r < 0)
return r;
}
dst->tasks_max = src->tasks_max;
return 0;
}
void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) {
assert(c);
assert(a);

View File

@@ -129,6 +129,7 @@ typedef enum CGroupPressureWatch {
_CGROUP_PRESSURE_WATCH_INVALID = -EINVAL,
} CGroupPressureWatch;
/* When adding members make sure to update cgroup_context_copy() accordingly */
struct CGroupContext {
bool cpu_accounting;
bool io_accounting;
@@ -285,6 +286,7 @@ uint64_t cgroup_context_cpu_weight(CGroupContext *c, ManagerState state);
usec_t cgroup_cpu_adjust_period(usec_t period, usec_t quota, usec_t resolution, usec_t max_period);
void cgroup_context_init(CGroupContext *c);
int cgroup_context_copy(CGroupContext *dst, const CGroupContext *src);
void cgroup_context_done(CGroupContext *c);
void cgroup_context_dump(Unit *u, FILE* f, const char *prefix);
void cgroup_context_dump_socket_bind_item(const CGroupSocketBindItem *item, FILE *f);
@@ -309,6 +311,18 @@ static inline bool cgroup_context_want_memory_pressure(const CGroupContext *c) {
int cgroup_context_add_device_allow(CGroupContext *c, const char *dev, CGroupDevicePermissions p);
int cgroup_context_add_or_update_device_allow(CGroupContext *c, const char *dev, CGroupDevicePermissions p);
int cgroup_context_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const char *path);
int cgroup_context_add_io_device_limit_dup(CGroupContext *c, CGroupIODeviceLimit *l);
int cgroup_context_add_io_device_weight_dup(CGroupContext *c, CGroupIODeviceWeight *w);
int cgroup_context_add_io_device_latency_dup(CGroupContext *c, CGroupIODeviceLatency *l);
int cgroup_context_add_block_io_device_weight_dup(CGroupContext *c, CGroupBlockIODeviceWeight *w);
int cgroup_context_add_block_io_device_bandwidth_dup(CGroupContext *c, CGroupBlockIODeviceBandwidth *b);
int cgroup_context_add_device_allow_dup(CGroupContext *c, CGroupDeviceAllow *a);
int cgroup_context_add_socket_bind_item_allow_dup(CGroupContext *c, CGroupSocketBindItem *i);
int cgroup_context_add_socket_bind_item_deny_dup(CGroupContext *c, CGroupSocketBindItem *i);
static inline int cgroup_context_add_bpf_foreign_program_dup(CGroupContext *c, CGroupBPFForeignProgram *p) {
return cgroup_context_add_bpf_foreign_program(c, p->attach_type, p->bpffs_path);
}
void unit_modify_nft_set(Unit *u, bool add);

View File

@@ -2933,6 +2933,180 @@ static int method_dump_unit_descriptor_store(sd_bus_message *message, void *user
return method_generic_unit_operation(message, userdata, error, bus_service_method_dump_file_descriptor_store, 0);
}
static int aux_scope_from_message(Manager *m, sd_bus_message *message, Unit **ret_scope, sd_bus_error *error) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
_cleanup_free_ PidRef *pidrefs = NULL;
const char *name;
Unit *from, *scope;
PidRef *main_pid;
CGroupContext *cc;
size_t n_pids = 0;
uint64_t flags;
pid_t pid;
int r;
assert(ret_scope);
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
if (r < 0)
return r;
r = sd_bus_creds_get_pid(creds, &pid);
if (r < 0)
return r;
from = manager_get_unit_by_pid(m, pid);
if (!from)
return sd_bus_error_set(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit.");
if (!IN_SET(from->type, UNIT_SERVICE, UNIT_SCOPE))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Starting auxiliary scope is supported only for service and scope units, refusing.");
if (!unit_name_is_valid(from->id, UNIT_NAME_PLAIN))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Auxiliary scope can be started only for non-template service units and scope units, refusing.");
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid name \"%s\" for auxiliary scope.", name);
if (unit_name_to_type(name) != UNIT_SCOPE)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Name \"%s\" of auxiliary scope doesn't have .scope suffix.", name);
main_pid = unit_main_pid(from);
r = sd_bus_message_enter_container(message, 'a', "h");
if (r < 0)
return r;
for (;;) {
_cleanup_(pidref_done) PidRef p = PIDREF_NULL;
Unit *unit;
int fd;
r = sd_bus_message_read(message, "h", &fd);
if (r < 0)
return r;
if (r == 0)
break;
r = pidref_set_pidfd(&p, fd);
if (r < 0) {
log_unit_warning_errno(from, r, "Failed to create process reference from PIDFD, ignoring: %m");
continue;
}
unit = manager_get_unit_by_pidref(m, &p);
if (!unit) {
log_unit_warning_errno(from, SYNTHETIC_ERRNO(ENOENT), "Failed to get unit from PIDFD, ingoring: %m");
continue;
}
if (!streq(unit->id, from->id)) {
log_unit_warning(from, "PID " PID_FMT " is not running in the same service as the calling process, ignoring.", p.pid);
continue;
}
if (pidref_equal(main_pid, &p)) {
log_unit_warning(from, "Main PID cannot be migrated into auxiliary scope, ignoring.");
continue;
}
if (!GREEDY_REALLOC(pidrefs, n_pids+1))
return -ENOMEM;
pidrefs[n_pids++] = TAKE_PIDREF(p);
}
if (n_pids == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "No processes can be migrated to auxiliary scope.");
r = sd_bus_message_exit_container(message);
if (r < 0)
return r;
r = sd_bus_message_read(message, "t", &flags);
if (r < 0)
return r;
if (flags != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be zero.");
r = manager_load_unit(m, name, NULL, error, &scope);
if (r < 0)
return r;
if (!unit_is_pristine(scope))
return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS,
"Unit %s was already loaded or has a fragment file.", name);
r = unit_set_slice(scope, UNIT_GET_SLICE(from));
if (r < 0)
return r;
cc = unit_get_cgroup_context(scope);
r = cgroup_context_copy(cc, unit_get_cgroup_context(from));
if (r < 0)
return r;
r = unit_make_transient(scope);
if (r < 0)
return r;
r = bus_unit_set_properties(scope, message, UNIT_RUNTIME, true, error);
if (r < 0)
return r;
FOREACH_ARRAY(p, pidrefs, n_pids) {
r = unit_pid_attachable(scope, p, error);
if (r < 0)
return r;
r = unit_watch_pidref(scope, p, /* exclusive= */ false);
if (r < 0 && r != -EEXIST)
return r;
}
/* Now load the missing bits of the unit we just created */
unit_add_to_load_queue(scope);
manager_dispatch_load_queue(m);
*ret_scope = TAKE_PTR(scope);
return 1;
}
static int method_start_aux_scope(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = ASSERT_PTR(userdata);
Unit *u = NULL; /* avoid false maybe-uninitialized warning */
int r;
assert(message);
r = mac_selinux_access_check(message, "start", error);
if (r < 0)
return r;
r = bus_verify_manage_units_async(m, message, error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = aux_scope_from_message(m, message, &u, error);
if (r < 0)
return r;
return bus_unit_queue_job(message, u, JOB_START, JOB_REPLACE, 0, error);
}
const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -3491,6 +3665,11 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_RESULT("a(suuutuusu)", entries),
method_dump_unit_descriptor_store,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("StartAuxiliaryScope",
SD_BUS_ARGS("s", name, "ah", pidfds, "t", flags, "a(sv)", properties),
SD_BUS_RESULT("o", job),
method_start_aux_scope,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL_WITH_ARGS("UnitNew",
SD_BUS_ARGS("s", id, "o", unit),

View File

@@ -600,4 +600,8 @@ executables += [
libudev_basic,
],
},
test_template + {
'sources' : files('test-aux-scope.c'),
'type' : 'manual',
},
]

160
src/test/test-aux-scope.c Normal file
View File

@@ -0,0 +1,160 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include "sd-event.h"
#include "bus-error.h"
#include "bus-message.h"
#include "bus-wait-for-jobs.h"
#include "fd-util.h"
#include "log.h"
#include "missing_syscall.h"
#include "process-util.h"
#include "tests.h"
static int on_sigusr1(sd_event_source *s, const struct signalfd_siginfo *ssi, void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
PidRef *pids = (PidRef *) userdata;
const char *job;
int r;
assert(pids);
r = sd_bus_open_system(&bus);
if (r < 0)
return log_error_errno(r, "Failed to acquire bus: %m");
r = sd_bus_message_new_method_call(bus, &message,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartAuxiliaryScope");
if (r < 0)
return log_error_errno(r, "Failed to create bus message: %m");
r = sd_bus_message_append_basic(message, 's', "test-aux-scope.scope");
if (r < 0)
return log_error_errno(r, "Failed to attach scope name: %m");
r = sd_bus_message_open_container(message, 'a', "h");
if (r < 0)
return log_error_errno(r, "Failed to create array of FDs: %m");
for (size_t i = 0; i < 10; i++) {
r = sd_bus_message_append_basic(message, 'h', &pids[i].fd);
if (r < 0)
return log_error_errno(r, "Failed to append PIDFD to message: %m");
}
r = sd_bus_message_close_container(message);
if (r < 0)
return log_error_errno(r, "Failed to close container: %m");
r = sd_bus_message_append(message, "ta(sv)", UINT64_C(0), 1, "Description", "s", "Test auxiliary scope");
if (r < 0)
return log_error_errno(r, "Failed to append unit properties: %m");
r = sd_bus_call(bus, message, 0, &error, &reply);
if (r < 0)
return log_error_errno(r, "Failed to start auxiliary scope: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "o", &job);
if (r < 0)
return log_error_errno(r, "Failed to read reply: %m");
r = bus_wait_for_jobs_new(bus, &w);
if (r < 0)
return log_error_errno(r, "Could not watch jobs: %m");
r = bus_wait_for_jobs_one(w, job, false, NULL);
if (r < 0)
return r;
return 0;
}
static void destroy_pidrefs(PidRef *pids, size_t npids) {
assert(pids || npids == 0);
for (size_t i = 0; i < npids; i++)
pidref_done(&pids[i]);
free(pids);
}
int main(int argc, char *argv[]) {
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
PidRef *pids = NULL;
size_t npids = 0;
int r, fd;
CLEANUP_ARRAY(pids, npids, destroy_pidrefs);
test_setup_logging(LOG_INFO);
fd = pidfd_open(getpid_cached(), 0);
if (fd < 0 && (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)))
return log_tests_skipped("pidfds are not available");
else if (fd < 0) {
log_error_errno(errno, "pidfd_open() failed: %m");
return EXIT_FAILURE;
}
safe_close(fd);
r = sd_event_new(&event);
if (r < 0) {
log_error_errno(r, "Failed to allocate event loop: %m");
return EXIT_FAILURE;
}
npids = 10;
pids = new0(PidRef, npids);
assert(pids);
r = sd_event_add_signal(event, NULL, SIGUSR1|SD_EVENT_SIGNAL_PROCMASK, on_sigusr1, pids);
if (r < 0) {
log_error_errno(r, "Failed to setup SIGUSR1 signal handling: %m");
return EXIT_FAILURE;
}
for (size_t i = 0; i < npids; i++) {
PidRef pidref = PIDREF_NULL;
pid_t pid;
r = safe_fork("(worker)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_CLOSE_ALL_FDS, &pid);
if (r < 0) {
log_error_errno(r, "Failed to fork(): %m");
return EXIT_FAILURE;
}
if (r == 0) {
/* Worker */
sleep(3600);
_exit(EXIT_SUCCESS);
}
r = pidref_set_pid(&pidref, pid);
if (r < 0) {
log_error_errno(r, "Failed to initialize PID ref: %m");
return EXIT_FAILURE;
}
assert_se(pidref.pid == pid);
assert_se(pidref.fd != -EBADF);
pids[i] = TAKE_PIDREF(pidref);
}
r = sd_event_loop(event);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
return 0;
}

View File

@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
set -o pipefail
export SYSTEMD_PAGER=cat
if ! grep -q pidfd_open /proc/kallsyms; then
echo "pidfds not available, skipping the test..."
exit 0
fi
systemd-run --unit test-aux-scope.service \
-p Slice=aux.slice -p Type=exec -p TasksMax=99 -p CPUWeight=199 -p IPAccounting=yes \
/usr/lib/systemd/tests/unit-tests/manual/test-aux-scope
kill -s USR1 "$(systemctl show --value --property MainPID test-aux-scope.service)"
sleep 1
systemctl status test-aux-scope.service
# shellcheck disable=SC2009
test "$(ps -eo pid,unit | grep -c test-aux-scope.service)" = 1
systemctl status test-aux-scope.scope
# shellcheck disable=SC2009
test "$(ps -eo pid,unit | grep -c test-aux-scope.scope)" = 10
test "$(systemctl show -p Slice --value test-aux-scope.scope)" = aux.slice
test "$(systemctl show -p TasksMax --value test-aux-scope.scope)" = 99
test "$(systemctl show -p CPUWeight --value test-aux-scope.scope)" = 199
test "$(systemctl show -p IPAccounting --value test-aux-scope.scope)" = yes
systemctl stop test-aux-scope.scope
systemctl stop test-aux-scope.service