1
0
mirror of https://github.com/systemd/systemd synced 2026-03-08 06:04:45 +01:00

Compare commits

..

16 Commits

Author SHA1 Message Date
Zbigniew Jędrzejewski-Szmek
f9dbf6fd5a
core: add SetProperties varlink method (#40356)
Start with Markers as a property, more can be added in the future as
needed
2026-02-17 10:57:09 +01:00
Yu Watanabe
4fae85faea
Several fixlets for issues found by Coverity (#40707) 2026-02-17 18:26:46 +09:00
Chris Down
31620807e1 oomd: Fix unnecessary delays during OOM kills with pending kills present
Let's say a user has two services with ManagedOOMMemoryPressure=kill,
perhaps a web server under system.slice and a batch job under
user.slice. Both exceed their pressure limits. On the previous timer
tick, oomd has already queued the web server's candidate for killing,
but the prekill hook has not yet responded, so the kill is still
pending.

In the code, monitor_memory_pressure_contexts_handler() iterates over
all pressure targets that have exceeded their limits. When it reaches
the web server target and calls oomd_cgroup_kill_mark(), which returns 0
because that cgroup is already queued. The code treats this the same as
a successful new kill: it resets the 15 second delay timer and returns
from the function, exiting the loop.

This loop is handled by SET_FOREACH and the iteration order is
hash-dependent. As such, if the web server target happens coincidentally
to be visited first, oomd never evaluates the batch job target at all.

The effect is twofold:

1. oomd stalls for 15 seconds despite not having initiated any new kill.
   That can unnecessarily delay further action to stem increases in
   memory pressure. The delay exists to let stale pressure counters
   settle after a kill, but no kill has happened here.
2. It non-deterministically skips pressure targets that may have
   unqueued candidates, dangerously allowing memory pressure to persist
   for longer than it should.

Fix this by skipping cgroups that are already queued so the loop
proceeds to try other pressure targets. We should only delay when a new
kill mark is actually created.
2026-02-17 18:26:04 +09:00
Chris Down
4fb8cc53b3 oomd: Fix silent failure to find bad cgroups when another cgroup dies
Consider a workload slice with several sibling cgroups. Imagine that one
of those cgroups is removed between the moment oomd enumerates the
directory and the moment it reads memory.oom.group. This is actually
relatively plausible under the high memory pressure conditions where
oomd is most needed.

In this case, the failed read prompts us to `return 0`, which exits the
entire enumeration loop in recursively_get_cgroup_context(). As a
result, all remaining sibling cgroups are silently dropped from the
candidate list for that monitoring cycle.

The effect is that oomd can fail to identify and kill the actual
offending cgroup, allowing memory pressure to persist until a subsequent
cycle where the race doesn't occur.

Fix this by instead proceeding to evaluate further sibling cgroups.
2026-02-17 17:32:26 +09:00
Chris Down
e757e791b2 oomd: Fix silent, non-deterministic dropped OOM kills
Let's say a user has two services with ManagedOOMMemoryPressure=kill,
one for a web server under system.slice, and one for a batch job under
user.slice. The batch job is causing severe memory pressure, whereas the
web server's cgroup has no processes with significant pgscan activity.

In the code, monitor_memory_pressure_contexts_handler() iterates over
all pressure targets that have exceeded their limits. When
oomd_select_by_pgscan_rate() returns 0 (that is, no candidates) for a
target, we return from the entire SET_FOREACH loop instead of moving to
the next target. Since SET_FOREACH iteration order is hash-dependent, if
the web server target happens to be visited first, oomd will find no
kill candidates for it and exit the loop. The batch job target that is
actually slamming the machine will never even be evaluated, and can
continue to wreak havoc without any intervention.

The effect is that oomd non-deterministically and silently fails to kill
cgroups that it should actually kill, allowing memory pressure to
persist and dangerously build up on the machine.

The fix is simple, keep evaluating remaining targets when one does not
match.
2026-02-17 17:31:39 +09:00
Yu Watanabe
7a6b6c4d07 network/wwan: fix memleak in failure path
Fixes CID#1645018.
2026-02-17 17:02:10 +09:00
Yu Watanabe
1a416a47e7 network/wwan: add missing error check
Fixes CID#1645015.
2026-02-17 17:01:00 +09:00
Yu Watanabe
2e8cfb7cef sd-bus: use bus_error_message() at several more places
For CID#1645014, CID#1645016, and CID#1645017.
2026-02-17 17:00:42 +09:00
Yu Watanabe
ce6ea42b69 bus-polkit: add one more assertion
For CID#1645013.
2026-02-17 16:39:38 +09:00
Mike Yuan
fad273450a socket-util: remove unused {send,receive}_many_fds*()
These were introduced as part of the effort of sd-executor
worker pool (#29566), which never landed due to unsignificant
performance improvement. Let's just remove the unused
helpers. If that work ever gets resurrected they can be
restored from this commit pretty easily.
2026-02-17 14:56:00 +09:00
Yu Watanabe
d9e078fe68
oomd: Fix Killed signal reason being lost (#40689)
Emitting "oom" doesn't mesh with the org.freedesktop.oom1.Manager
Killed() API contract, which defines "memory-used" and "memory-pressure"
as possible reasons. Consumers that key off reason thus will either lose
policy attribution or may reject the unknown value completely.

Plumb the reason through so it is visible to consumers.
2026-02-17 14:53:46 +09:00
Chris Down
b10e5a60aa oomd: Add regression tests to check Killed notification gives a reason 2026-02-17 14:50:21 +09:00
Chris Down
936d864b0b oomd: Fix Killed signal reason being lost
Emitting "oom" doesn't mesh with the org.freedesktop.oom1.Manager
Killed() API contract, which defines "memory-used" and "memory-pressure"
as possible reasons. Consumers that key off reason thus will either lose
policy attribution or may reject the unknown value completely.

Plumb the reason through so it is visible to consumers.
2026-02-17 14:50:17 +09:00
Daan De Meyer
48c84fe730 nspawn-mount: Use setns() in wipe_fully_visible_api_fs()
namespace_enter() now does a is_our_namespace() check, which requires
/proc on older kernels, which is not available anymore after we call
do_wipe_fully_visible_api_fs() in wipe_fully_visible_api_fs().

Let's just call setns() instead as namespace_enter() is overkill to
enter a single namespace anyway.
2026-02-17 04:55:24 +01:00
Luca Boccassi
0e1c4de235 core: add SetProperties varlink method
Initial support for 'Markers' only
2026-02-13 19:16:13 +00:00
Luca Boccassi
cb7c0950b3 core: ensure varlink Unit.List() can query inactive units
manager_get_unit() only returns loaded units, if a unit is
stopped and not referenced by anything it will be GCed, so
this method will return nothing. Use manager_load_unit()
instead so that a stopped unit gets loaded and we can
return its state to the caller, after checking that it
actually exists.

Follow-up for d2a8dbabf5e2676f436515cc821040bd24eb6003
2026-02-13 19:16:13 +00:00
21 changed files with 429 additions and 293 deletions

View File

@ -970,53 +970,6 @@ int getpeerpidref(int fd, PidRef *ret) {
return pidref_set_pidfd_consume(ret, pidfd);
}
ssize_t send_many_fds_iov_sa(
int transport_fd,
int *fds_array, size_t n_fds_array,
const struct iovec *iov, size_t iovlen,
const struct sockaddr *sa, socklen_t len,
int flags) {
_cleanup_free_ struct cmsghdr *cmsg = NULL;
struct msghdr mh = {
.msg_name = (struct sockaddr*) sa,
.msg_namelen = len,
.msg_iov = (struct iovec *)iov,
.msg_iovlen = iovlen,
};
ssize_t k;
assert(transport_fd >= 0);
assert(fds_array || n_fds_array == 0);
/* The kernel will reject sending more than SCM_MAX_FD FDs at once */
if (n_fds_array > SCM_MAX_FD)
return -E2BIG;
/* We need either an FD array or data to send. If there's nothing, return an error. */
if (n_fds_array == 0 && !iov)
return -EINVAL;
if (n_fds_array > 0) {
mh.msg_controllen = CMSG_SPACE(sizeof(int) * n_fds_array);
mh.msg_control = cmsg = malloc(mh.msg_controllen);
if (!cmsg)
return -ENOMEM;
*cmsg = (struct cmsghdr) {
.cmsg_len = CMSG_LEN(sizeof(int) * n_fds_array),
.cmsg_level = SOL_SOCKET,
.cmsg_type = SCM_RIGHTS,
};
memcpy(CMSG_DATA(cmsg), fds_array, sizeof(int) * n_fds_array);
}
k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags);
if (k < 0)
return (ssize_t) -errno;
return k;
}
ssize_t send_one_fd_iov_sa(
int transport_fd,
int fd,
@ -1072,74 +1025,6 @@ int send_one_fd_sa(
return (int) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, sa, len, flags);
}
ssize_t receive_many_fds_iov(
int transport_fd,
struct iovec *iov, size_t iovlen,
int **ret_fds_array, size_t *ret_n_fds_array,
int flags) {
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int) * SCM_MAX_FD)) control;
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
.msg_iov = iov,
.msg_iovlen = iovlen,
};
_cleanup_free_ int *fds_array = NULL;
size_t n_fds_array = 0;
struct cmsghdr *cmsg;
ssize_t k;
assert(transport_fd >= 0);
assert(ret_fds_array);
assert(ret_n_fds_array);
/*
* Receive many FDs via @transport_fd. We don't care for the transport-type. We retrieve all the FDs
* at once. This is best used in combination with send_many_fds().
*/
k = recvmsg_safe(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags);
if (k < 0)
return k;
CMSG_FOREACH(cmsg, &mh)
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
size_t n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
if (!GREEDY_REALLOC_APPEND(fds_array, n_fds_array, CMSG_TYPED_DATA(cmsg, int), n)) {
cmsg_close_all(&mh);
return -ENOMEM;
}
}
if (n_fds_array == 0) {
cmsg_close_all(&mh);
/* If didn't receive an FD or any data, return an error. */
if (k == 0)
return -EIO;
}
*ret_fds_array = TAKE_PTR(fds_array);
*ret_n_fds_array = n_fds_array;
return k;
}
int receive_many_fds(int transport_fd, int **ret_fds_array, size_t *ret_n_fds_array, int flags) {
ssize_t k;
k = receive_many_fds_iov(transport_fd, NULL, 0, ret_fds_array, ret_n_fds_array, flags);
if (k == 0)
return 0;
/* k must be negative, since receive_many_fds_iov() only returns a positive value if data was received
* through the iov. */
assert(k < 0);
return (int) k;
}
ssize_t receive_one_fd_iov(
int transport_fd,
struct iovec *iov, size_t iovlen,

View File

@ -120,28 +120,6 @@ int getpeergroups(int fd, gid_t **ret);
int getpeerpidfd(int fd);
int getpeerpidref(int fd, PidRef *ret);
ssize_t send_many_fds_iov_sa(
int transport_fd,
int *fds_array, size_t n_fds_array,
const struct iovec *iov, size_t iovlen,
const struct sockaddr *sa, socklen_t len,
int flags);
static inline ssize_t send_many_fds_iov(
int transport_fd,
int *fds_array, size_t n_fds_array,
const struct iovec *iov, size_t iovlen,
int flags) {
return send_many_fds_iov_sa(transport_fd, fds_array, n_fds_array, iov, iovlen, NULL, 0, flags);
}
static inline int send_many_fds(
int transport_fd,
int *fds_array,
size_t n_fds_array,
int flags) {
return send_many_fds_iov_sa(transport_fd, fds_array, n_fds_array, NULL, 0, NULL, 0, flags);
}
ssize_t send_one_fd_iov_sa(
int transport_fd,
int fd,
@ -156,8 +134,6 @@ int send_one_fd_sa(int transport_fd,
#define send_one_fd(transport_fd, fd, flags) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, NULL, 0, flags)
ssize_t receive_one_fd_iov(int transport_fd, struct iovec *iov, size_t iovlen, int flags, int *ret_fd);
int receive_one_fd(int transport_fd, int flags);
ssize_t receive_many_fds_iov(int transport_fd, struct iovec *iov, size_t iovlen, int **ret_fds_array, size_t *ret_n_fds_array, int flags);
int receive_many_fds(int transport_fd, int **ret_fds_array, size_t *ret_n_fds_array, int flags);
ssize_t next_datagram_size_fd(int fd);
@ -203,9 +179,7 @@ void* cmsg_find_and_copy_data(struct msghdr *mh, int level, int type, void *buf,
}
size_t sockaddr_ll_len(const struct sockaddr_ll *sa);
size_t sockaddr_un_len(const struct sockaddr_un *sa);
size_t sockaddr_len(const union sockaddr_union *sa);
int socket_ioctl_fd(void);

View File

@ -2121,7 +2121,6 @@ static int bus_unit_set_live_property(
for (;;) {
const char *word;
bool b;
r = sd_bus_message_read(message, "s", &word);
if (r < 0)
@ -2129,22 +2128,14 @@ static int bus_unit_set_live_property(
if (r == 0)
break;
if (IN_SET(word[0], '+', '-')) {
b = word[0] == '+';
word++;
some_plus_minus = true;
} else {
b = true;
some_absolute = true;
}
UnitMarker m = unit_marker_from_string(word);
if (m < 0)
r = parse_unit_marker(word, &settings, &mask);
if (r < 0)
return sd_bus_error_setf(reterr_error, BUS_ERROR_BAD_UNIT_SETTING,
"Unknown marker \"%s\".", word);
SET_FLAG(settings, 1u << m, b);
SET_FLAG(mask, 1u << m, true);
if (r > 0)
some_plus_minus = true;
else
some_absolute = true;
}
r = sd_bus_message_exit_container(message);

View File

@ -7058,3 +7058,26 @@ int unit_queue_job_check_and_mangle_type(
return 0;
}
int parse_unit_marker(const char *marker, unsigned *settings, unsigned *mask) {
bool some_plus_minus = false, b = true;
assert(marker);
assert(settings);
assert(mask);
if (IN_SET(marker[0], '+', '-')) {
b = marker[0] == '+';
marker++;
some_plus_minus = true;
}
UnitMarker m = unit_marker_from_string(marker);
if (m < 0)
return -EINVAL;
SET_FLAG(*settings, 1u << m, b);
SET_FLAG(*mask, 1u << m, true);
return some_plus_minus;
}

View File

@ -1094,6 +1094,8 @@ DECLARE_STRING_TABLE_LOOKUP(oom_policy, OOMPolicy);
int unit_queue_job_check_and_mangle_type(Unit *u, JobType *type, bool reload_if_possible, sd_bus_error *reterr_error);
int parse_unit_marker(const char *marker, unsigned *settings, unsigned *mask);
/* Macros which append UNIT= or USER_UNIT= to the message */
#define log_unit_full_errno_zerook(unit, level, error, ...) \

View File

@ -361,6 +361,40 @@ static int lookup_unit_by_pidref(sd_varlink *link, Manager *manager, PidRef *pid
return 0;
}
static int load_unit_and_check(sd_varlink *link, Manager *manager, const char *name, Unit **ret_unit) {
Unit *unit;
int r;
assert(link);
assert(manager);
assert(name);
assert(ret_unit);
r = manager_load_unit(manager, name, /* path= */ NULL, /* e= */ NULL, &unit);
if (r < 0)
return r;
/* manager_load_unit() will create an object regardless of whether the unit actually exists, so
* check the state and refuse if it's not in a good state. */
if (IN_SET(unit->load_state, UNIT_NOT_FOUND, UNIT_STUB, UNIT_MERGED))
return sd_varlink_error(link, "io.systemd.Unit.NoSuchUnit", NULL);
if (unit->load_state == UNIT_BAD_SETTING)
return sd_varlink_error(link, "io.systemd.Unit.UnitError", NULL);
if (unit->load_state == UNIT_ERROR)
return sd_varlink_errorbo(
link,
SD_VARLINK_ERROR_SYSTEM,
SD_JSON_BUILD_PAIR_STRING("origin", "linux"),
SD_JSON_BUILD_PAIR_INTEGER("errno", unit->load_error),
JSON_BUILD_PAIR_STRING_NON_EMPTY("errnoName", "io.systemd.Unit.UnitError"));
if (unit->load_state == UNIT_MASKED)
return sd_varlink_error(link, "io.systemd.Unit.UnitMasked", NULL);
assert(UNIT_IS_LOAD_COMPLETE(unit->load_state));
*ret_unit = unit;
return 0;
}
typedef struct UnitLookupParameters {
const char *name, *cgroup;
PidRef pidref;
@ -400,9 +434,9 @@ static int lookup_unit_by_parameters(
assert(ret);
if (p->name) {
unit = manager_get_unit(manager, p->name);
if (!unit)
return varlink_error_no_such_unit(link, "name");
r = load_unit_and_check(link, manager, p->name, &unit);
if (r < 0)
return r;
}
if (pidref_is_set_or_automatic(&p->pidref)) {
@ -534,3 +568,107 @@ int varlink_error_no_such_unit(sd_varlink *v, const char *name) {
VARLINK_ERROR_UNIT_NO_SUCH_UNIT,
JSON_BUILD_PAIR_STRING_NON_EMPTY("parameter", name));
}
typedef struct UnitSetPropertiesParameters {
const char *unsupported_property; /* For error reporting */
const char *name;
bool runtime;
bool markers_found;
unsigned markers, markers_mask;
} UnitSetPropertiesParameters;
static int parse_unit_markers(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
UnitSetPropertiesParameters *p = ASSERT_PTR(userdata);
bool some_plus_minus = false, some_absolute = false;
unsigned settings = 0, mask = 0;
sd_json_variant *e;
int r;
assert(variant);
JSON_VARIANT_ARRAY_FOREACH(e, variant) {
if (!sd_json_variant_is_string(e))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Marker is not an array of strings.");
const char *word = sd_json_variant_string(e);
r = parse_unit_marker(word, &settings, &mask);
if (r < 0)
return json_log(variant, flags, r, "Failed to parse marker '%s'.", word);
if (r > 0)
some_plus_minus = true;
else
some_absolute = true;
}
if (some_plus_minus && some_absolute)
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Absolute and non-absolute markers in the same setting.");
if (some_absolute || sd_json_variant_elements(variant) == 0)
mask = UINT_MAX;
p->markers = settings;
p->markers_mask = mask;
p->markers_found = true;
return 0;
}
static int unit_dispatch_properties(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
static const sd_json_dispatch_field dispatch_table[] = {
{ "Markers", SD_JSON_VARIANT_ARRAY, parse_unit_markers, 0, 0 },
{}
};
UnitSetPropertiesParameters *p = ASSERT_PTR(userdata);
const char *bad_field = NULL;
int r;
r = sd_json_dispatch_full(variant, dispatch_table, /* bad= */ NULL, flags, userdata, &bad_field);
if (r == -EADDRNOTAVAIL && !isempty(bad_field))
/* When properties contains a valid field, but that we don't currently support, make sure to
* return the offending property, rather than generic invalid argument. */
p->unsupported_property = bad_field;
return r;
}
int vl_method_set_unit_properties(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
static const sd_json_dispatch_field dispatch_table[] = {
{ "name", SD_JSON_VARIANT_STRING, json_dispatch_const_unit_name, offsetof(UnitSetPropertiesParameters, name), SD_JSON_MANDATORY },
{ "runtime", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(UnitSetPropertiesParameters, runtime), SD_JSON_MANDATORY },
{ "properties", SD_JSON_VARIANT_OBJECT, unit_dispatch_properties, 0, SD_JSON_MANDATORY },
{}
};
UnitSetPropertiesParameters p = {};
Manager *manager = ASSERT_PTR(userdata);
const char *bad_field = NULL;
Unit *unit;
int r;
assert(link);
assert(parameters);
r = sd_json_dispatch_full(parameters, dispatch_table, /* bad= */ NULL, /* flags= */ 0, &p, &bad_field);
if (r < 0) {
/* When properties contains a valid field, but that we don't currently support, make sure to
* return a specific error, rather than generic invalid argument. */
if (streq_ptr(bad_field, "properties") && r == -EADDRNOTAVAIL)
return sd_varlink_errorbo(
link,
"io.systemd.Unit.PropertyNotSupported",
SD_JSON_BUILD_PAIR_CONDITION(!!p.unsupported_property, "property", SD_JSON_BUILD_STRING(p.unsupported_property)));
if (bad_field)
return sd_varlink_error_invalid_parameter_name(link, bad_field);
return r;
}
r = load_unit_and_check(link, manager, p.name, &unit);
if (r < 0)
return r;
if (p.markers_found)
unit->markers = p.markers | (unit->markers & ~p.markers_mask);
return sd_varlink_reply(link, NULL);
}

View File

@ -17,4 +17,6 @@ int varlink_unit_queue_job_one(
uint32_t *ret_job_id,
sd_bus_error *reterr_bus_error);
int vl_method_set_unit_properties(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
int varlink_error_no_such_unit(sd_varlink *v, const char *name);

View File

@ -391,6 +391,7 @@ int manager_setup_varlink_server(Manager *m) {
"io.systemd.Manager.Reload", vl_method_reload_manager,
"io.systemd.Manager.EnqueueMarkedJobs", vl_method_enqueue_marked_jobs_manager,
"io.systemd.Unit.List", vl_method_list_units,
"io.systemd.Unit.SetProperties", vl_method_set_unit_properties,
"io.systemd.service.Ping", varlink_method_ping,
"io.systemd.service.GetEnvironment", varlink_method_get_environment);
if (r < 0)

View File

@ -4,6 +4,7 @@
#include "alloc-util.h"
#include "bus-control.h"
#include "bus-error.h"
#include "bus-internal.h"
#include "bus-message.h"
#include "fd-util.h"
@ -136,11 +137,14 @@ static int default_request_name_handler(
assert(m);
if (sd_bus_message_is_method_error(m, NULL)) {
log_debug_errno(sd_bus_message_get_errno(m),
"Unable to request name, failing connection: %s",
sd_bus_message_get_error(m)->message);
const sd_bus_error *e = ASSERT_PTR(sd_bus_message_get_error(m));
r = sd_bus_error_get_errno(e);
bus_enter_closing(sd_bus_message_get_bus(m), -sd_bus_message_get_errno(m));
log_debug_errno(r,
"Unable to request name, failing connection: %s",
bus_error_message(e, r));
bus_enter_closing(sd_bus_message_get_bus(m), -r);
return 1;
}
@ -290,11 +294,12 @@ static int default_release_name_handler(
assert(m);
if (sd_bus_message_is_method_error(m, NULL)) {
log_debug_errno(sd_bus_message_get_errno(m),
"Unable to release name, failing connection: %s",
sd_bus_message_get_error(m)->message);
const sd_bus_error *e = ASSERT_PTR(sd_bus_message_get_error(m));
r = sd_bus_error_get_errno(e);
bus_enter_closing(sd_bus_message_get_bus(m), -sd_bus_message_get_errno(m));
log_debug_errno(r, "Unable to release name, failing connection: %s", bus_error_message(e, r));
bus_enter_closing(sd_bus_message_get_bus(m), -r);
return 1;
}

View File

@ -3483,10 +3483,13 @@ static int add_match_callback(
sd_bus_slot_ref(match_slot);
if (sd_bus_message_is_method_error(m, NULL)) {
r = log_debug_errno(sd_bus_message_get_errno(m),
const sd_bus_error *e = ASSERT_PTR(sd_bus_message_get_error(m));
r = sd_bus_error_get_errno(e);
r = log_debug_errno(r,
"Unable to add match %s, failing connection: %s",
match_slot->match_callback.match_string,
sd_bus_message_get_error(m)->message);
bus_error_message(e, r));
failed = true;
} else

View File

@ -109,16 +109,16 @@ static int map_dns(
void *userdata) {
Bearer *b = ASSERT_PTR(userdata);
struct in_addr_full *a;
const char *s;
int r;
assert(m);
const char *s;
r = sd_bus_message_read_basic(m, 's', &s);
if (r < 0)
return r;
_cleanup_free_ struct in_addr_full *a = NULL;
r = in_addr_full_new_from_string(s, &a);
if (r < 0)
return r;
@ -127,7 +127,6 @@ static int map_dns(
return -ENOMEM;
b->dns[b->n_dns++] = TAKE_PTR(a);
return 0;
}
@ -477,18 +476,16 @@ static int bearer_new_and_initialize(Modem *modem, const char *path) {
static int modem_connect_handler(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
Modem *modem = ASSERT_PTR(userdata);
const sd_bus_error *e;
const char *new_bearer;
int r;
assert(message);
modem->slot_connect = sd_bus_slot_unref(modem->slot_connect);
e = sd_bus_message_get_error(message);
const sd_bus_error *e = sd_bus_message_get_error(message);
if (e) {
r = sd_bus_error_get_errno(e);
log_full_errno(LOG_ERR, r,
log_error_errno(r,
"Could not connect modem %s %s: %s",
modem->manufacturer, modem->model,
bus_error_message(e, r));
@ -497,10 +494,16 @@ static int modem_connect_handler(sd_bus_message *message, void *userdata, sd_bus
return 0;
}
sd_bus_message_read(message, "o", &new_bearer);
const char *new_bearer;
r = sd_bus_message_read(message, "o", &new_bearer);
if (r < 0) {
log_debug_errno(r, "Received connection of %s %s from ModemManager, but bearer not found, ignoring: %m",
modem->manufacturer, modem->model);
return 0;
}
log_debug("ModemManager: %s %s connected, bearer is at %s",
modem->manufacturer, modem->model, new_bearer);
return 0;
}

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <linux/magic.h>
#include <sched.h>
#include <sys/mount.h>
#include <unistd.h>
@ -1491,7 +1492,7 @@ static int do_wipe_fully_visible_api_fs(void) {
int wipe_fully_visible_api_fs(int mntns_fd) {
_cleanup_close_ int orig_mntns_fd = -EBADF;
int r, rr;
int r;
log_debug("Wiping fully visible API FS");
@ -1499,23 +1500,13 @@ int wipe_fully_visible_api_fs(int mntns_fd) {
if (orig_mntns_fd < 0)
return log_error_errno(orig_mntns_fd, "Failed to pin originating mount namespace: %m");
r = namespace_enter(/* pidns_fd= */ -EBADF,
mntns_fd,
/* netns_fd= */ -EBADF,
/* userns_fd= */ -EBADF,
/* root_fd= */ -EBADF);
if (r < 0)
return log_error_errno(r, "Failed to enter mount namespace: %m");
if (setns(mntns_fd, CLONE_NEWNS) < 0)
return log_error_errno(errno, "Failed to enter mount namespace: %m");
rr = do_wipe_fully_visible_api_fs();
r = do_wipe_fully_visible_api_fs();
r = namespace_enter(/* pidns_fd= */ -EBADF,
orig_mntns_fd,
/* netns_fd= */ -EBADF,
/* userns_fd= */ -EBADF,
/* root_fd= */ -EBADF);
if (r < 0)
return log_error_errno(r, "Failed to enter original mount namespace: %m");
if (setns(orig_mntns_fd, CLONE_NEWNS) < 0)
return log_error_errno(errno, "Failed to enter original mount namespace: %m");
return rr;
return r;
}

View File

@ -241,7 +241,7 @@ static int recursively_get_cgroup_context(Hashmap *new_h, const char *path) {
return r;
if (r < 0) {
log_debug_errno(r, "Failed to read memory.oom.group from %s, ignoring: %m", cg_path);
return 0;
continue;
}
if (r > 0)
r = oomd_insert_cgroup_context(NULL, new_h, cg_path);
@ -416,7 +416,7 @@ static int monitor_swap_contexts_handler(sd_event_source *s, uint64_t usec, void
return 0;
}
r = oomd_cgroup_kill_mark(m, selected);
r = oomd_cgroup_kill_mark(m, selected, "memory-used");
if (r == -ENOMEM)
return log_oom();
if (r < 0)
@ -531,28 +531,28 @@ static int monitor_memory_pressure_contexts_handler(sd_event_source *s, uint64_t
return log_error_errno(r, "Failed to select any cgroups based on swap, ignoring: %m");
if (r == 0) {
log_debug("No cgroup candidates found for memory pressure-based OOM action for %s", t->path);
return 0;
continue;
}
r = oomd_cgroup_kill_mark(m, selected);
r = oomd_cgroup_kill_mark(m, selected, "memory-pressure");
if (r == -ENOMEM)
return log_oom();
if (r < 0)
log_error_errno(r, "Failed to select any cgroups under %s based on pressure, ignoring: %m", t->path);
else if (r == 0)
/* Already queued for kill by an earlier iteration, try next target without
* resetting the delay timer. */
continue;
else {
/* Don't act on all the high pressure cgroups at once; return as soon as we kill one.
* If r == 0 then the cgroup is already queued for kill by an earlier iteration.
* In either case, go through the event loop again and select a new candidate if
* pressure is still high. */
/* Don't act on all the high pressure cgroups at once; return as soon as we kill one. */
m->mem_pressure_post_action_delay_start = usec_now;
if (selected && r > 0) {
if (selected)
log_notice("Marked %s for killing due to memory pressure for %s being %lu.%02lu%% > %lu.%02lu%%"
" for > %s with reclaim activity",
selected->path, t->path,
LOADAVG_INT_SIDE(t->memory_pressure.avg10), LOADAVG_DECIMAL_SIDE(t->memory_pressure.avg10),
LOADAVG_INT_SIDE(t->mem_pressure_limit), LOADAVG_DECIMAL_SIDE(t->mem_pressure_limit),
FORMAT_TIMESPAN(t->mem_pressure_duration_usec, USEC_PER_SEC));
}
return 0;
}
}

View File

@ -27,6 +27,7 @@
typedef struct OomdKillState {
Manager *manager;
OomdCGroupContext *ctx;
const char *reason;
/* This holds sd_varlink references */
Set *links;
} OomdKillState;
@ -245,11 +246,12 @@ int oomd_sort_cgroup_contexts(Hashmap *h, oomd_compare_t compare_func, const cha
return (int) k;
}
int oomd_cgroup_kill(Manager *m, OomdCGroupContext *ctx, bool recurse) {
int oomd_cgroup_kill(Manager *m, OomdCGroupContext *ctx, bool recurse, const char *reason) {
_cleanup_set_free_ Set *pids_killed = NULL;
int r;
assert(ctx);
assert(!m || reason);
pids_killed = set_new(NULL);
if (!pids_killed)
@ -288,7 +290,7 @@ int oomd_cgroup_kill(Manager *m, OomdCGroupContext *ctx, bool recurse) {
"Killed",
"ss",
ctx->path,
"oom");
reason);
return !set_isempty(pids_killed);
}
@ -334,7 +336,7 @@ static void oomd_kill_state_remove(OomdKillState *ks) {
if (!set_isempty(ks->links))
return;
r = oomd_cgroup_kill(ks->manager, ks->ctx, /* recurse= */ true);
r = oomd_cgroup_kill(ks->manager, ks->ctx, /* recurse= */ true, ks->reason);
if (r < 0)
log_debug_errno(r, "Failed to kill cgroup '%s', ignoring: %m", ks->ctx->path);
oomd_kill_state_free(ks);
@ -465,11 +467,12 @@ static int oomd_prekill_hook(Manager *m, OomdKillState *ks) {
return 0;
}
int oomd_cgroup_kill_mark(Manager *m, OomdCGroupContext *ctx) {
int oomd_cgroup_kill_mark(Manager *m, OomdCGroupContext *ctx, const char *reason) {
int r;
assert(ctx);
assert(m);
assert(reason);
if (m->dry_run) {
_cleanup_free_ char *cg_path = NULL;
@ -489,6 +492,7 @@ int oomd_cgroup_kill_mark(Manager *m, OomdCGroupContext *ctx) {
*ks = (OomdKillState) {
.manager = m,
.ctx = oomd_cgroup_context_ref(ctx),
.reason = reason,
};
r = set_ensure_put(&m->kill_states, &oomd_kill_state_hash_ops, ks);

View File

@ -124,8 +124,8 @@ int oomd_sort_cgroup_contexts(Hashmap *h, oomd_compare_t compare_func, const cha
int oomd_fetch_cgroup_oom_preference(OomdCGroupContext *ctx, const char *prefix);
/* Returns a negative value on error, 0 if no processes were killed, or 1 if processes were killed. */
int oomd_cgroup_kill(Manager *m, OomdCGroupContext *ctx, bool recurse);
int oomd_cgroup_kill_mark(Manager *m, OomdCGroupContext *ctx);
int oomd_cgroup_kill(Manager *m, OomdCGroupContext *ctx, bool recurse, const char *reason);
int oomd_cgroup_kill_mark(Manager *m, OomdCGroupContext *ctx, const char *reason);
/* The following oomd_kill_by_* functions return 1 if processes were killed, or negative otherwise. */
/* If `prefix` is supplied, only cgroups whose paths start with `prefix` are eligible candidates. Otherwise,

View File

@ -1,20 +1,25 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include "sd-bus.h"
#include "alloc-util.h"
#include "cgroup-setup.h"
#include "cgroup-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "oomd-manager.h"
#include "oomd-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "pidref.h"
#include "process-util.h"
#include "set.h"
#include "string-util.h"
#include "tests.h"
#include "time-util.h"
#include "tmpfile-util.h"
@ -59,6 +64,103 @@ static int fork_and_sleep(unsigned sleep_min, PidRef *ret) {
return r;
}
static int setup_local_oomd_bus(sd_bus **ret_server, sd_bus **ret_client) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *server = NULL, *client = NULL;
_cleanup_close_pair_ int pair[2] = EBADF_PAIR;
int r;
assert(ret_server);
assert(ret_client);
r = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, pair);
if (r < 0)
return -errno;
r = sd_bus_new(&server);
if (r < 0)
return r;
r = sd_bus_set_fd(server, pair[0], pair[0]);
if (r < 0)
return r;
pair[0] = -EBADF;
r = sd_bus_set_server(server, true, SD_ID128_NULL);
if (r < 0)
return r;
r = sd_bus_set_anonymous(server, true);
if (r < 0)
return r;
r = sd_bus_start(server);
if (r < 0)
return r;
r = sd_bus_new(&client);
if (r < 0)
return r;
r = sd_bus_set_fd(client, pair[1], pair[1]);
if (r < 0)
return r;
pair[1] = -EBADF;
r = sd_bus_set_anonymous(client, true);
if (r < 0)
return r;
r = sd_bus_start(client);
if (r < 0)
return r;
*ret_server = TAKE_PTR(server);
*ret_client = TAKE_PTR(client);
return 0;
}
static int wait_for_killed_signal(sd_bus *server, sd_bus *client, const char *cgroup_path, const char *reason) {
int r;
assert(server);
assert(client);
assert(cgroup_path);
assert(reason);
for (size_t i = 0; i < 200; i++) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m_client = NULL;
r = sd_bus_process(server, NULL);
if (r < 0)
return r;
r = sd_bus_process(client, &m_client);
if (r < 0)
return r;
if (m_client && sd_bus_message_is_signal(m_client, "org.freedesktop.oom1.Manager", "Killed")) {
const char *got_cgroup, *got_reason;
r = sd_bus_message_read(m_client, "ss", &got_cgroup, &got_reason);
if (r < 0)
return r;
if (!streq(got_cgroup, cgroup_path))
return -ENOMSG;
if (!streq(got_reason, reason))
return -ENOMSG;
return 0;
}
r = sd_bus_wait(client, 50 * USEC_PER_MSEC);
if (r < 0)
return r;
}
return -ETIMEDOUT;
}
TEST(oomd_cgroup_kill) {
_cleanup_free_ char *subcgroup = NULL;
int r;
@ -88,7 +190,11 @@ TEST(oomd_cgroup_kill) {
ASSERT_OK(fork_and_sleep(5, &two));
ASSERT_OK(cg_attach(subcgroup, two.pid));
ASSERT_OK_POSITIVE(oomd_cgroup_kill(NULL /* manager */, &(OomdCGroupContext){ .path = subcgroup }, false /* recurse */));
ASSERT_OK_POSITIVE(oomd_cgroup_kill(
/* m= */ NULL,
&(OomdCGroupContext){ .path = subcgroup },
/* recurse= */ false,
/* reason= */ NULL));
ASSERT_OK(cg_get_xattr(subcgroup, "user.oomd_ooms", &v, /* ret_size= */ NULL));
ASSERT_STREQ(v, i == 0 ? "1" : "2");
@ -113,6 +219,54 @@ TEST(oomd_cgroup_kill) {
ASSERT_OK(cg_trim(subcgroup, /* delete_root */ true));
}
TEST(oomd_cgroup_kill_signal_reason) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *server = NULL, *client = NULL;
_cleanup_free_ char *subcgroup = NULL;
static const char *reasons[] = {
"memory-used",
"memory-pressure",
};
Manager m = {};
int r;
r = enter_cgroup_root_cached();
if (r < 0)
return (void) log_tests_skipped_errno(r, "Failed to enter cgroup root");
ASSERT_NOT_NULL(subcgroup = path_join(cgroup, "oomdkillsignaltest"));
ASSERT_OK(cg_trim(subcgroup, /* delete_root= */ true));
ASSERT_OK(cg_create(subcgroup));
ASSERT_OK(setup_local_oomd_bus(&server, &client));
m.bus = server;
for (size_t i = 0; i < ELEMENTSOF(reasons); i++) {
_cleanup_(pidref_done) PidRef one = PIDREF_NULL, two = PIDREF_NULL;
const char *reason = reasons[i];
ASSERT_OK(fork_and_sleep(5, &one));
ASSERT_OK(cg_attach(subcgroup, one.pid));
ASSERT_OK(fork_and_sleep(5, &two));
ASSERT_OK(cg_attach(subcgroup, two.pid));
ASSERT_OK_POSITIVE(oomd_cgroup_kill(&m, &(OomdCGroupContext) { .path = subcgroup }, /* recurse= */ false, reason));
ASSERT_OK(wait_for_killed_signal(server, client, subcgroup, reason));
/* Cleanup isn't instantaneous, so give it some grace */
bool empty = false;
for (size_t t = 0; t < 100; t++) {
usleep_safe(100 * USEC_PER_MSEC);
if (ASSERT_OK(cg_is_empty(subcgroup)) > 0) {
empty = true;
break;
}
}
ASSERT_TRUE(empty);
}
ASSERT_OK(cg_trim(subcgroup, /* delete_root= */ true));
}
TEST(oomd_cgroup_context_acquire_and_insert) {
_cleanup_hashmap_free_ Hashmap *h1 = NULL, *h2 = NULL;
_cleanup_(oomd_cgroup_context_unrefp) OomdCGroupContext *ctx = NULL;

View File

@ -286,9 +286,7 @@ static int async_polkit_read_reply(sd_bus_message *reply, AsyncPolkitQuery *q) {
a = ASSERT_PTR(TAKE_PTR(q->action));
if (sd_bus_message_is_method_error(reply, NULL)) {
const sd_bus_error *e;
e = sd_bus_message_get_error(reply);
const sd_bus_error *e = ASSERT_PTR(sd_bus_message_get_error(reply));
if (bus_error_is_unknown_service(e)) {
/* If PK is absent, then store this away, as it depends on the callers flags whether

View File

@ -951,13 +951,13 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_FIELD_COMMENT("If not empty, the field contains the name of another unit that this unit follows in state"),
SD_VARLINK_DEFINE_FIELD(Following, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Reflects whether the configuration file of this unit has been loaded"),
SD_VARLINK_DEFINE_FIELD(LoadState, SD_VARLINK_STRING, 0),
SD_VARLINK_DEFINE_FIELD(LoadState, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Reflects whether the unit is currently active or not"),
SD_VARLINK_DEFINE_FIELD(ActiveState, SD_VARLINK_STRING, 0),
SD_VARLINK_DEFINE_FIELD(ActiveState, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Reflects whether the unit is currently frozen or not"),
SD_VARLINK_DEFINE_FIELD(FreezerState, SD_VARLINK_STRING, 0),
SD_VARLINK_DEFINE_FIELD(FreezerState, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Reflect more fine-grained state that is unit-type-specific"),
SD_VARLINK_DEFINE_FIELD(SubState, SD_VARLINK_STRING, 0),
SD_VARLINK_DEFINE_FIELD(SubState, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Reflects the install state of the unit file"),
SD_VARLINK_DEFINE_FIELD(UnitFileState, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Timestamp when the firmware first began execution"),
@ -971,27 +971,27 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_FIELD_COMMENT("Timestamp when the unit exited inactive state"),
SD_VARLINK_DEFINE_FIELD_BY_TYPE(InactiveExitTimestamp, Timestamp, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Reflects whether the unit can be started or not"),
SD_VARLINK_DEFINE_FIELD(CanStart, SD_VARLINK_BOOL, 0),
SD_VARLINK_DEFINE_FIELD(CanStart, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Reflects whether the unit can be stopped or not"),
SD_VARLINK_DEFINE_FIELD(CanStop, SD_VARLINK_BOOL, 0),
SD_VARLINK_DEFINE_FIELD(CanStop, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Reflects whether the unit can be reloaded or not"),
SD_VARLINK_DEFINE_FIELD(CanReload, SD_VARLINK_BOOL, 0),
SD_VARLINK_DEFINE_FIELD(CanReload, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Reflects whether the unit may be started in isolation mode"),
SD_VARLINK_DEFINE_FIELD(CanIsolate, SD_VARLINK_BOOL, 0),
SD_VARLINK_DEFINE_FIELD(CanIsolate, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Returns which unit resources can be cleaned up"),
SD_VARLINK_DEFINE_FIELD(CanClean, SD_VARLINK_STRING, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Whether the unit supports the freeze operation"),
SD_VARLINK_DEFINE_FIELD(CanFreeze, SD_VARLINK_BOOL, 0),
SD_VARLINK_DEFINE_FIELD(CanFreeze, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Whether the unit supports live mounting"),
SD_VARLINK_DEFINE_FIELD(CanLiveMount, SD_VARLINK_BOOL, 0),
SD_VARLINK_DEFINE_FIELD(CanLiveMount, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The job ID of the job currently scheduled or being executed for this unit, if there is any."),
SD_VARLINK_DEFINE_FIELD(JobId, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Whether the configuration file this unit is loaded from (i.e. FragmentPath or SourcePath) has changed since the configuration was read and hence whether a configuration reload is recommended"),
SD_VARLINK_DEFINE_FIELD(NeedDaemonReload, SD_VARLINK_BOOL, 0),
SD_VARLINK_DEFINE_FIELD(NeedDaemonReload, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Condition result of the last time the configured conditions of this unit were checked"),
SD_VARLINK_DEFINE_FIELD(ConditionResult, SD_VARLINK_BOOL, 0),
SD_VARLINK_DEFINE_FIELD(ConditionResult, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Assert result of the last time the configured asserts of this unit were checked"),
SD_VARLINK_DEFINE_FIELD(AssertResult, SD_VARLINK_BOOL, 0),
SD_VARLINK_DEFINE_FIELD(AssertResult, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The last time the configured conditions of the unit have been checked"),
SD_VARLINK_DEFINE_FIELD_BY_TYPE(ConditionTimestamp, Timestamp, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The last time the configured asserts of the unit have been checked"),
@ -1027,12 +1027,28 @@ static SD_VARLINK_DEFINE_ERROR(
static SD_VARLINK_DEFINE_ERROR(OnlyByDependency);
static SD_VARLINK_DEFINE_ERROR(DBusShuttingDown);
static SD_VARLINK_DEFINE_ERROR(UnitMasked);
static SD_VARLINK_DEFINE_ERROR(UnitError);
static SD_VARLINK_DEFINE_ERROR(
PropertyNotSupported,
SD_VARLINK_DEFINE_FIELD(property, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
SetProperties,
SD_VARLINK_FIELD_COMMENT("The name of the unit to operate on."),
SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("Whether to apply the change ephemerally or persistently."),
SD_VARLINK_DEFINE_INPUT(runtime, SD_VARLINK_BOOL, 0),
SD_VARLINK_FIELD_COMMENT("The runtime properties to set."),
SD_VARLINK_DEFINE_INPUT_BY_TYPE(properties, UnitRuntime, 0));
SD_VARLINK_DEFINE_INTERFACE(
io_systemd_Unit,
"io.systemd.Unit",
SD_VARLINK_SYMBOL_COMMENT("List units"),
&vl_method_List,
SD_VARLINK_SYMBOL_COMMENT("Set unit properties"),
&vl_method_SetProperties,
&vl_type_RateLimit,
SD_VARLINK_SYMBOL_COMMENT("An object to represent a unit's conditions"),
&vl_type_Condition,
@ -1093,6 +1109,12 @@ SD_VARLINK_DEFINE_INTERFACE(
/* Errors */
SD_VARLINK_SYMBOL_COMMENT("No matching unit found"),
&vl_error_NoSuchUnit,
SD_VARLINK_SYMBOL_COMMENT("The unit is masked"),
&vl_error_UnitMasked,
SD_VARLINK_SYMBOL_COMMENT("Unit is in a fatal error state"),
&vl_error_UnitError,
SD_VARLINK_SYMBOL_COMMENT("Changing this property via SetProperties() is not supported"),
&vl_error_PropertyNotSupported,
SD_VARLINK_SYMBOL_COMMENT("Job for the unit may only be enqueued by dependencies"),
&vl_error_OnlyByDependency,
SD_VARLINK_SYMBOL_COMMENT("A unit that requires D-Bus cannot be started as D-Bus is shutting down"),

View File

@ -299,70 +299,6 @@ TEST(passfd_contents_read) {
ASSERT_STREQ(buf, file_contents);
}
TEST(pass_many_fds_contents_read) {
_cleanup_close_pair_ int pair[2] = EBADF_PAIR;
static const char file_contents[][STRLEN("test contents in the fileX") + 1] = {
"test contents in the file0",
"test contents in the file1",
"test contents in the file2"
};
static const char wire_contents[] = "test contents on the wire";
int r;
assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
r = ASSERT_OK(pidref_safe_fork("(passfd_contents_read)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT, NULL));
if (r == 0) {
/* Child */
struct iovec iov = IOVEC_MAKE_STRING(wire_contents);
char tmpfile[][STRLEN("/tmp/test-socket-util-passfd-contents-read-XXXXXX") + 1] = {
"/tmp/test-socket-util-passfd-contents-read-XXXXXX",
"/tmp/test-socket-util-passfd-contents-read-XXXXXX",
"/tmp/test-socket-util-passfd-contents-read-XXXXXX"
};
int tmpfds[3] = EBADF_TRIPLET;
pair[0] = safe_close(pair[0]);
for (size_t i = 0; i < 3; ++i) {
assert_se(write_tmpfile(tmpfile[i], file_contents[i]) == 0);
tmpfds[i] = open(tmpfile[i], O_RDONLY);
assert_se(tmpfds[i] >= 0);
assert_se(unlink(tmpfile[i]) == 0);
}
assert_se(send_many_fds_iov(pair[1], tmpfds, 3, &iov, 1, MSG_DONTWAIT) > 0);
close_many(tmpfds, 3);
_exit(EXIT_SUCCESS);
}
/* Parent */
char buf[64];
struct iovec iov = IOVEC_MAKE(buf, sizeof(buf)-1);
_cleanup_free_ int *fds = NULL;
size_t n_fds = 0;
ssize_t k;
pair[1] = safe_close(pair[1]);
k = receive_many_fds_iov(pair[0], &iov, 1, &fds, &n_fds, MSG_DONTWAIT);
assert_se(k > 0);
buf[k] = 0;
ASSERT_STREQ(buf, wire_contents);
assert_se(n_fds == 3);
for (size_t i = 0; i < 3; ++i) {
assert_se(fds[i] >= 0);
r = read(fds[i], buf, sizeof(buf)-1);
assert_se(r >= 0);
buf[r] = 0;
ASSERT_STREQ(buf, file_contents[i]);
safe_close(fds[i]);
}
}
TEST(receive_nopassfd) {
_cleanup_close_pair_ int pair[2] = EBADF_PAIR;
static const char wire_contents[] = "no fd passed here";

View File

@ -378,7 +378,7 @@ systemctl reload-or-restart --marked
# again, but with varlinkctl instead
systemctl restart "$UNIT_NAME"
systemctl set-property "$UNIT_NAME" Markers=needs-restart
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"needs-restart\"]}}"
systemctl show -P Markers "$UNIT_NAME" | grep needs-restart
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Manager.EnqueueMarkedJobs '{}'
timeout 30 bash -c "until systemctl list-jobs $UNIT_NAME | grep \"No jobs\" 2>/dev/null; do sleep 1; done"

View File

@ -209,6 +209,10 @@ varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"name": "
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"name": "non-existent.service"}')
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"pid": {"pid": -1}}' )
(! varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"name": "multi-user.target", "pid": {"pid": 1}}')
set +o pipefail
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties '{"runtime": true, "name": "non-existent.service", "properties": {"Markers": ["needs-restart"]}}' |& grep "io.systemd.Unit.NoSuchUnit"
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties '{"runtime": true, "name": "systemd-journald.service", "properties": {"LoadState": "foobar"}}' |& grep "io.systemd.Unit.PropertyNotSupported"
set -o pipefail
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List '{"cgroup": "/init.scope"}'
invocation_id="$(systemctl show -P InvocationID systemd-journald.service)"