1
0
mirror of https://github.com/systemd/systemd synced 2026-04-12 10:04:50 +02:00

Compare commits

...

19 Commits

Author SHA1 Message Date
Lennart Poettering
ff6b70fe7e Update TODO 2026-04-10 14:54:39 +02:00
Daan De Meyer
8355eb6e11 meson: Check if files returned by git ls-files actually exist
Otherwise you run into errors such as:

"""
../meson.build:2899:28: ERROR: File src/test/test-loop-util.c does not exist.
"""

when deleting a file in git without staging the deletion.
2026-04-10 14:34:00 +02:00
Frantisek Sumsal
92d87ac302 sd-bus: don't overallocate the message buffer
newa(t, n) already allocates sizeof(t) * n bytes, so previously we'd
actually allocate sizeof(t) * sizeof(t) * n bytes, which is ~16x more
(on x86_64) that we actually needed.

This is probably an oversight from a tree-wide change in
6e9417f5b4f29938fab1eee2b5edf596cc580452 that replaced alloca() with
newa().

Follow-up for 6e9417f5b4f29938fab1eee2b5edf596cc580452.
2026-04-10 12:20:16 +01:00
Lennart Poettering
5401a1270a
logind: add io.systemd.Shutdown varlink interface (#41229)
The shutdown interface is currently only exposed via dbus. This PR
adds a comparable varlink implementation. It is inspired by the existing
dbus methods and implements PowerOff, Reboot, Halt, Kexec, SoftReboot
as varlink methods on a new io.systemd.Shutdown interface.

It is (intentional) simpler than the dbus for now, i.e. no Can* yet,
mostly
because I want to get feedback first (happy to do that in a followup). 

The only real difficulty here is what to do about
verify_shutdown_creds()
as this is needed by both dbus and varlink and its dbus only. I went for
an ugly but (hopefully) pragmatic choice (see the commit message for
details). But I can totally understand if a refactor instead is
preferred.
2026-04-10 12:29:32 +02:00
Lennart Poettering
58b9e400a6
Help users with incorrect / permission bits (#41431)
This error causes the computer to pass the emergency.target and go to
graphical.target.
Then, your window manager will have problems because it cant access any
directories, your network manager wont startup the network. In my case,
the screen just goes black. Ideally, you'd get an error message
explaining this edge scenario that's occuring to you, and an emergency
shell that makes it easy to run the necessary chmod 0755 / to proceed
with booting. IDK not sure if this is the correct way to implement this,
sorry it's my first contribution.

I ran 
`meson test -C build`
and got 

Ok:                1806
Fail:              26  
Skipped:           25  
on my cloned systemd repo before any changes, and got the same result
after my commit ¯\_(ツ)_/¯
So I hope I did that right. 
Thanks
2026-04-10 12:25:07 +02:00
Lennart Poettering
554ba32d30
report: add cgroup metrics in a separate varlink service (#41489)
Add CpuUsage, MemoryUsage, IOReadBytes, IOReadOperations, and
TasksCurrent in a standalone socket-activated varlink service. These
metrics are gathered from the kernel via cgroup files and PID1's only
role is mapping unit names to cgroup paths — a separate process can
query PID1 once for that mapping and then read the cgroup files
directly, minimizing PID1 involvement.

The new systemd-report-cgroup-metrics service listens at
/run/systemd/report/io.systemd.CGroup and exposes:
  - io.systemd.CGroup.CpuUsage
  - io.systemd.CGroup.IOReadBytes
  - io.systemd.CGroup.IOReadOperations
  - io.systemd.CGroup.MemoryUsage (with type=current/available/peak)
  - io.systemd.CGroup.TasksCurrent

This is spun out of #41078 and based on top of it. Will rebase once
that's merged.
2026-04-10 12:10:06 +02:00
Milan Kyselica
dd80e5a348 resolved: replace assert() with error return in DNSSEC verify functions
dnssec_rsa_verify_raw() asserts that RSA_size(key) matches the RRSIG
signature size, and dnssec_ecdsa_verify_raw() asserts that
EC_KEY_check_key() succeeds. Both conditions depend on parsed DNS
record content. Replace with proper error returns.

The actual crypto verify calls (EVP_PKEY_verify / ECDSA_do_verify)
handle mismatches fine on their own, so the asserts were also redundant.

While at it, fix the misleading "EC_POINT_bn2point failed" log message
that actually refers to an EC_KEY_set_public_key() failure.

Fixes: https://github.com/systemd/systemd/issues/41569
2026-04-10 10:29:37 +01:00
Daan De Meyer
a65ebc3ff9 claude-review: improve review quality for large PRs
Several issues were identified from analyzing logs of a large (52-commit) PR
review:

- Claude was batching multiple commits into a single review agent instead of
  one per worktree. Strengthen the prompt to explicitly prohibit grouping.
- Claude was reading pr-context.json and commit messages before spawning
  agents despite instructions not to, wasting time. Tighten the pre-spawn
  rules to only allow listing worktrees/ and reading review-schema.json.
- Subagents were spawned with model "sonnet" instead of "opus". Add explicit
  instruction to use opus.
- After agents returned, Claude spent 9 minutes re-verifying findings with
  bash/grep/sed commands, duplicating the agents' work. Add instruction to
  trust subagent findings and only read pr-context.json in phase 2.
- Subagents returned markdown-wrapped JSON instead of raw JSON arrays. Add
  instruction requiring raw JSON output only.
- Each subagent was independently reading review-schema.json. Instead have
  the main agent read it once and paste it into each subagent prompt.
- The "drop low-confidence findings" instruction was being used to justify
  dropping findings that Claude itself acknowledged as valid ("solid cleanup
  suggestions", "reasonable consistency improvement"). Remove the instruction.
- Simplify the deduplication instructions
- Stop adding the severity to the body in the post processing job as claude is
  also adding it so they end up duplicated.
2026-04-10 10:57:35 +02:00
azureuser
fb0ae7436d resolved: skip cache flush on server switch/re-probe when StaleRetentionSec is set
manager_set_dns_server() and dns_server_flush_cache() call dns_cache_flush()
unconditionally, wiping the entire cache even when StaleRetentionSec is
configured. This defeats serve-stale by discarding cached records that should
remain available during server switches and feature-level re-probes.

The original serve-stale commit (5ed91481ab) added a stale_retention_usec
guard to link_set_dns_server(), and a later commit (7928c0e0a1) added the
same guard to dns_delegate_set_dns_server(), but these two call sites in
resolved-dns-server.c were missed.

This is particularly visible with DNSOverTLS, where TLS handshake failures
trigger frequent feature-level downgrades and re-probes via
dns_server_flush_cache(), flushing the cache each time.

Add the same stale_retention_usec guard to both call sites so that cache
entries are allowed to expire naturally via dns_cache_prune() when
serve-stale is enabled.

Fixes: #40781

This commit was prepared with assistance from an AI coding agent (GitHub
Copilot). All changes have been reviewed for correctness and adherence to the
systemd coding style.
2026-04-10 10:10:26 +02:00
Yaping Li
8c65fe4fa1 report: add cgroup metrics in a separate varlink service
Add CpuUsage, MemoryUsage, IOReadBytes, IOReadOperations, and
TasksCurrent in a standalone socket-activated varlink service.

The new systemd-report-cgroup service listens at
/run/systemd/report/io.systemd.CGroup and exposes:
  - io.systemd.CGroup.CpuUsage
  - io.systemd.CGroup.IOReadBytes
  - io.systemd.CGroup.IOReadOperations
  - io.systemd.CGroup.MemoryUsage (with type=current/available/peak)
  - io.systemd.CGroup.TasksCurrent
2026-04-09 14:45:05 -07:00
Yaping Li
6fe4a16f36 cgroup-util: add cg_get_keyed_attribute_uint64() helper
Multiple callers of cg_get_keyed_attribute() follow the same pattern of
reading a single keyed attribute and then parsing it as uint64_t with
safe_atou64(). Add a helper that combines both steps.

Convert all existing single-key + uint64 call sites in cgtop, cgroup.c,
and oomd-util.c to use the new helper.
2026-04-09 14:45:05 -07:00
ipv6
5168e9eada added root.conf to meson.build 2026-04-09 14:04:16 -05:00
ipv6
15384f6e1f Added NEWS 2026-04-08 11:06:41 -05:00
ipv6
7b2a10b594 Added tmpfiles.d/root.conf to set access permissions to root / dir 2026-04-08 10:32:45 -05:00
Michael Vogt
768b507adc logind: log peer ID when shutdown is called
The io.systemd.Manager.{PowerOff,SoftReboot,Halt,Kexec} manager
varlink and bus methods log the peer ID when calling shutdown.

The logind code is missing this, so this commit adds a similar
logging now. The code is quite similar to the one in existing in
src/core/manager.c but its hard to share code so this adds a bit
of duplication.
2026-04-07 21:01:21 +02:00
Michael Vogt
db426d147d logind: extend verify_shutdown_creds() to take sd_varlink *link
To properly support a shutdown interface with varlink we need
the functionality of verify_shutdown_creds(). This is currently
dbus only. There are some options:
1. refactor and abstract so that verify_shutdown_creds() is agnostic
2. provide a equivalent function with varlink
3. allow to call it with either a dbus or a varlink message.

The most elegant of course is (1) but it makes reviewing harder
and has a higher regression risk. It will also be more code.

Doing (2) has the risk of drift, i.e. we will need to keep
the two functions in sync and not forget about it ever.

So this commit opts for (3): allowing either dbus or varlink.
This is quite ugly, however the big advantage is that its very
simple to review as the dbus/varlink branches mirror each other.
And there is no risk of drift the dbus/varlink options are
close to each other. It unlikely we get a third bus, so it will
most likely stay this way. It is still ugly though so I can
understand if this is undesired and I can look into (1) if its
too ugly.

With this function avaialble logind-varlink.c is now updated to
use it.
2026-04-07 21:01:21 +02:00
Michael Vogt
c1b928c810 logind: move verify_shutdown_creds() to logind-shutdown.c
Move verify_shutdown_creds() and its helper have_multiple_sessions()
from logind-dbus.c to logind-shutdown.c so that they can be reused
by the varlink transport. No functional changes.

Also prefix both with `manager_` now that they are public.
2026-04-07 20:38:50 +02:00
Michael Vogt
d1d72563e0 logind: add io.systemd.Shutdown varlink Shutdown interface
The shutdown interface is currently only exposed via dbus. This commit
adds a comparable varlink implementation. It is inspired by the existing
dbus methods and implements PowerOff, Reboot, Halt, Kexec, SoftReboot.

It is (intentional) simpler than the dbus for now, i.e. strictly root
only. To match dbus we will need to the functionality
of verify_shutdown_creds() which is dbus-ish right now and would
need some refactor.

For the same reason it does not do the Can* methods - we will need
the verify_shutdown_creds() equivalent first.
2026-04-07 20:38:40 +02:00
Michael Vogt
c2283986f9 logind: move reset_scheduled_shutdown() to new logind-shutdown.c
This function operates on generic Manager state and will be needed
by the varlink shutdown interface too. Move it out of logind-dbus.c
into a new logind-shutdown.c, alongside the SHUTDOWN_SCHEDULE_FILE
define and use `manager_reset_scheduled_shutdown() as the new
name.
2026-04-07 20:06:53 +02:00
33 changed files with 1299 additions and 251 deletions

View File

@ -256,27 +256,34 @@ jobs:
## Phase 1: Review commits
List the directories in `worktrees/` — there is one per commit. Each
worktree at `worktrees/<sha>/` contains the full source tree checked out at
that commit, plus `commit.patch` (the diff) and `commit-message.txt`
(the commit message). Spawn one
review subagent per worktree, all in a single message so they run concurrently.
Do NOT pre-compute diffs or read any other files before spawning — the subagents
will do that themselves.
First, list the directories in `worktrees/` and read `review-schema.json`.
Then, spawn exactly one review subagent per worktree directory, all in a
single message so they run concurrently. Do NOT batch or group multiple
commits into a single agent. Do NOT read any other files before spawning —
the subagents will do that themselves.
Each worktree at `worktrees/<sha>/` contains the full source tree checked
out at that commit, plus `commit.patch` (the diff) and `commit-message.txt`
(the commit message).
Each reviewer reviews design, code quality, style, potential bugs, and
security implications.
Each subagent must be spawned with `model: "opus"`.
Each subagent prompt must include:
- Instructions to read `pr-context.json` in the repository root for additional
context.
- Instructions to read `review-schema.json` in the repository root and
return a JSON array matching the `comments` items schema from that file.
- The contents of `review-schema.json` (paste it into each prompt so the
agent doesn't have to read it separately).
- The worktree path.
- Instructions to read `commit-message.txt` and `commit.patch` in the
worktree for the commit message and diff.
- Instructions to verify every `line` and `start_line` value
against the hunk ranges in `commit.patch` before returning.
- Instructions to return ONLY a raw JSON array of findings. No markdown,
no explanation, no code fences — just the JSON array. If there are no
findings, return `[]`.
## Phase 2: Collect, deduplicate, and summarize
@ -290,26 +297,20 @@ jobs:
populate the `resolve` array.
- If `tracking_comment` is non-null, use it as the basis for your summary.
Trust the subagent findings — do NOT re-verify them by running your own
bash, grep, sed, or awk commands against the source code. Phase 2 should
only read `pr-context.json` and then produce the structured output.
Then:
1. Collect all issues. Merge duplicates (same file, lines within 3 of each other, same problem).
2. Drop low-confidence findings.
3. Check the existing inline review comments from `pr-context.json`. Do NOT
include a comment if one already exists on the same file about the same
problem, even if the exact line numbers differ (lines shift between
revisions). Also check for author replies that dismiss or reject a previous
comment — do NOT re-raise an issue the PR author has already responded to
disagreeing with.
Populate the `resolve` array with the REST API `id` (integer) of your own
review comment threads that should be resolved (user.login == "github-actions[bot]"
and body starts with "Claude: "). Do not resolve threads from human reviewers.
A thread should be resolved if:
- The issue it raised has been addressed in the current PR (i.e. your review
no longer flags it), or
- The PR author (or another reviewer) left a reply disagreeing with or
dismissing the comment.
Only include the `id` of the **first** comment in each thread (the one that
started the conversation). Do not resolve threads for issues that are still
present and unaddressed.
1. Collect all issues. Merge duplicates across agents (same file, same
problem, lines within 3 of each other).
2. Drop issues that already have a review comment on the same file about
the same problem, or where the PR author replied disagreeing.
3. Populate the `resolve` array with the `id` of your own review comment
threads (user.login == "github-actions[bot]", body starts with
"Claude: ") that should be resolved — either because the issue was
fixed or because the author dismissed it. Use the first comment `id`
in each thread. Do not resolve threads from human reviewers.
4. Write a `summary` field in markdown for a top-level tracking comment.
**If no existing tracking comment was found (first run):**
@ -486,7 +487,7 @@ jobs:
...(c.side != null && { side: c.side }),
...(c.start_line != null && { start_line: c.start_line }),
...(c.start_side != null && { start_side: c.start_side }),
body: `Claude: **${c.severity}**: ${c.body}`,
body: c.body,
});
posted++;
} catch (e) {

3
NEWS
View File

@ -22,6 +22,9 @@ CHANGES WITH 261 in spe:
New features:
* A new tmpfiles.d/root.conf has been added that sets permissions
on the root directory (/) to 0555
* Networking to cloud IMDS services may be locked down for recognized
clouds. This is recommended for secure installations, but typically
conflicts with traditional IMDS clients such as cloud-init, which

9
TODO
View File

@ -122,11 +122,12 @@ Features:
* sysext: make systemd-{sys,conf}ext-sysroot.service work in the split '/var'
configuration.
* sd-varlink: add fully async modes of the protocol upgrade stuff
* introduce a concept of /etc/machine-info "TAGS=" field that allows tagging
machines with zero, one or more roles, states or other forms of
categorization. Then, add a way of using this in sysupdate to automatically
enable certain transfers, one for each role.
* sd-varlink: optimize the read-byte-by-byte mode in case upgrade mode is
enabled, via recvmsg() with MSG_SEEK: first read non-destrictively, look for
NUL byte, and only then flush out
* sd-varlink: add fully async modes of the protocol upgrade stuff
* repart: maybe remove iso9660/eltorito superblock from disk when booting via
gpt, if there is one.

View File

@ -84,6 +84,15 @@
<citerefentry><refentrytitle>org.freedesktop.LogControl1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for information about the D-Bus APIs <filename>systemd-logind</filename> provides.</para>
<para>In addition to the D-Bus interface, <filename>systemd-logind</filename> also provides a Varlink
interface <constant>io.systemd.Shutdown</constant> for shutting down or rebooting the system. It
supports <function>PowerOff</function>, <function>Reboot</function>, <function>Halt</function>,
<function>KExec</function>, and <function>SoftReboot</function> methods. Each method accepts an
optional <varname>skipInhibitors</varname> boolean parameter to bypass active block inhibitors
(matching the <constant>SD_LOGIND_SKIP_INHIBITORS</constant> flag of the D-Bus interface). The
interface can be queried with
<command>varlinkctl introspect /run/systemd/io.systemd.Login io.systemd.Shutdown</command>.</para>
<para>For more information see
<ulink url="https://systemd.io/INHIBITOR_LOCKS">Inhibitor Locks</ulink>.</para>

View File

@ -2896,7 +2896,13 @@ if git.found()
'ls-files', ':/*.[ch]', ':/*.cc',
check : false)
if all_files.returncode() == 0
all_files = files(all_files.stdout().split())
existing_files = []
foreach f : all_files.stdout().split()
if fs.exists(f)
existing_files += f
endif
endforeach
all_files = files(existing_files)
custom_target(
output : 'tags',

View File

@ -1560,6 +1560,24 @@ fail:
return r;
}
int cg_get_keyed_attribute_uint64(const char *path, const char *attribute, const char *key, uint64_t *ret) {
_cleanup_free_ char *val = NULL;
int r;
assert(key);
assert(ret);
r = cg_get_keyed_attribute(path, attribute, STRV_MAKE(key), &val);
if (r < 0)
return r;
r = safe_atou64(val, ret);
if (r < 0)
return log_debug_errno(r, "Failed to parse value '%s' of key '%s' in cgroup attribute '%s': %m", val, key, attribute);
return 0;
}
int cg_mask_to_string(CGroupMask mask, char **ret) {
_cleanup_free_ char *s = NULL;
bool space = false;

View File

@ -165,6 +165,7 @@ int cg_get_attribute_as_uint64(const char *path, const char *attribute, uint64_t
int cg_get_attribute_as_bool(const char *path, const char *attribute);
int cg_get_keyed_attribute(const char *path, const char *attribute, char * const *keys, char **values);
int cg_get_keyed_attribute_uint64(const char *path, const char *attribute, const char *key, uint64_t *ret);
int cg_get_owner(const char *path, uid_t *ret_uid);

View File

@ -285,19 +285,14 @@ static int process_cpu(Group *g, unsigned iteration) {
if (r < 0)
return r;
} else {
_cleanup_free_ char *val = NULL;
uint64_t u;
r = cg_get_keyed_attribute(g->path, "cpu.stat", STRV_MAKE("usage_usec"), &val);
r = cg_get_keyed_attribute_uint64(g->path, "cpu.stat", "usage_usec", &u);
if (IN_SET(r, -ENOENT, -ENXIO))
return 0;
if (r < 0)
return r;
r = safe_atou64(val, &u);
if (r < 0)
return r;
new_usage = u * NSEC_PER_USEC;
}

View File

@ -2996,9 +2996,8 @@ int unit_check_oomd_kill(Unit *u) {
}
int unit_check_oom(Unit *u) {
_cleanup_free_ char *oom_kill = NULL;
bool increased;
uint64_t c;
uint64_t c = 0;
int r;
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
@ -3013,33 +3012,25 @@ int unit_check_oom(Unit *u) {
* back to reading oom_kill if we can't find the file or field. */
if (ctx->memory_oom_group) {
r = cg_get_keyed_attribute(
r = cg_get_keyed_attribute_uint64(
crt->cgroup_path,
"memory.events.local",
STRV_MAKE("oom_group_kill"),
&oom_kill);
"oom_group_kill",
&c);
if (r < 0 && !IN_SET(r, -ENOENT, -ENXIO))
return log_unit_debug_errno(u, r, "Failed to read oom_group_kill field of memory.events.local cgroup attribute, ignoring: %m");
}
if (isempty(oom_kill)) {
r = cg_get_keyed_attribute(
if (!ctx->memory_oom_group || r < 0) {
r = cg_get_keyed_attribute_uint64(
crt->cgroup_path,
"memory.events",
STRV_MAKE("oom_kill"),
&oom_kill);
"oom_kill",
&c);
if (r < 0 && !IN_SET(r, -ENOENT, -ENXIO))
return log_unit_debug_errno(u, r, "Failed to read oom_kill field of memory.events cgroup attribute: %m");
}
if (!oom_kill)
c = 0;
else {
r = safe_atou64(oom_kill, &c);
if (r < 0)
return log_unit_debug_errno(u, r, "Failed to parse memory.events cgroup oom field: %m");
}
increased = c > crt->oom_kill_last;
crt->oom_kill_last = c;
@ -3585,14 +3576,9 @@ static int unit_get_cpu_usage_raw(const Unit *u, const CGroupRuntime *crt, nsec_
if (unit_has_host_root_cgroup(u))
return procfs_cpu_get_usage(ret);
_cleanup_free_ char *val = NULL;
uint64_t us;
r = cg_get_keyed_attribute(crt->cgroup_path, "cpu.stat", STRV_MAKE("usage_usec"), &val);
if (r < 0)
return r;
r = safe_atou64(val, &us);
r = cg_get_keyed_attribute_uint64(crt->cgroup_path, "cpu.stat", "usage_usec", &us);
if (r < 0)
return r;

View File

@ -1238,7 +1238,6 @@ int bus_socket_take_fd(sd_bus *b) {
int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
struct iovec *iov;
ssize_t k;
size_t n;
unsigned j;
int r;
@ -1254,9 +1253,8 @@ int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
if (r < 0)
return r;
n = m->n_iovec * sizeof(struct iovec);
iov = newa(struct iovec, n);
memcpy_safe(iov, m->iovec, n);
iov = newa(struct iovec, m->n_iovec);
memcpy_safe(iov, m->iovec, sizeof(struct iovec) * m->n_iovec);
j = 0;
iovec_advance(iov, &j, *idx);

View File

@ -45,6 +45,7 @@
#include "logind-seat.h"
#include "logind-seat-dbus.h"
#include "logind-session-dbus.h"
#include "logind-shutdown.h"
#include "logind-user.h"
#include "logind-user-dbus.h"
#include "logind-utmp.h"
@ -76,10 +77,6 @@
*/
#define WALL_MESSAGE_MAX 4096U
#define SHUTDOWN_SCHEDULE_FILE "/run/systemd/shutdown/scheduled"
static void reset_scheduled_shutdown(Manager *m);
static int get_sender_session(
Manager *m,
sd_bus_message *message,
@ -1866,24 +1863,6 @@ static int method_flush_devices(sd_bus_message *message, void *userdata, sd_bus_
return sd_bus_reply_method_return(message, NULL);
}
static int have_multiple_sessions(
Manager *m,
uid_t uid) {
Session *session;
assert(m);
/* Check for other users' sessions. Greeter sessions do not
* count, and non-login sessions do not count either. */
HASHMAP_FOREACH(session, m->sessions)
if (SESSION_CLASS_IS_INHIBITOR_LIKE(session->class) &&
session->user->user_record->uid != uid)
return true;
return false;
}
static int bus_manager_log_shutdown(
Manager *m,
const HandleActionData *a) {
@ -2189,121 +2168,6 @@ int bus_manager_shutdown_or_sleep_now_or_later(
return r;
}
static int verify_shutdown_creds(
Manager *m,
sd_bus_message *message,
const HandleActionData *a,
uint64_t flags,
sd_bus_error *error) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
bool multiple_sessions, blocked, interactive;
_unused_ bool error_or_denial = false;
Inhibitor *offending = NULL;
uid_t uid;
int r;
assert(m);
assert(a);
assert(message);
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
if (r < 0)
return r;
r = sd_bus_creds_get_euid(creds, &uid);
if (r < 0)
return r;
r = have_multiple_sessions(m, uid);
if (r < 0)
return r;
multiple_sessions = r > 0;
blocked = manager_is_inhibited(m, a->inhibit_what, NULL, /* flags= */ 0, uid, &offending);
interactive = flags & SD_LOGIND_INTERACTIVE;
if (multiple_sessions) {
r = bus_verify_polkit_async_full(
message,
a->polkit_action_multiple_sessions,
/* details= */ NULL,
/* good_user= */ UID_INVALID,
interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
&m->polkit_registry,
error);
if (r < 0) {
/* If we get -EBUSY, it means a polkit decision was made, but not for
* this action in particular. Assuming we are blocked on inhibitors,
* ignore that error and allow the decision to be revealed below. */
if (blocked && r == -EBUSY)
error_or_denial = true;
else
return r;
}
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
}
if (blocked) {
PolkitFlags polkit_flags = 0;
/* With a strong inhibitor, if the skip flag is not set, reject outright.
* With a weak inhibitor, if root is asking and the root flag is set, reject outright.
* All else, check polkit first. */
if (!FLAGS_SET(flags, SD_LOGIND_SKIP_INHIBITORS) &&
(offending->mode != INHIBIT_BLOCK_WEAK ||
(uid == 0 && FLAGS_SET(flags, SD_LOGIND_ROOT_CHECK_INHIBITORS))))
return sd_bus_error_set(error, BUS_ERROR_BLOCKED_BY_INHIBITOR_LOCK,
"Operation denied due to active block inhibitor");
/* We want to always ask here, even for root, to only allow bypassing if explicitly allowed
* by polkit, unless a weak blocker is used, in which case it will be authorized. */
if (offending->mode != INHIBIT_BLOCK_WEAK)
polkit_flags |= POLKIT_ALWAYS_QUERY;
if (interactive)
polkit_flags |= POLKIT_ALLOW_INTERACTIVE;
r = bus_verify_polkit_async_full(
message,
a->polkit_action_ignore_inhibit,
/* details= */ NULL,
/* good_user= */ UID_INVALID,
polkit_flags,
&m->polkit_registry,
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 */
}
if (!multiple_sessions && !blocked) {
r = bus_verify_polkit_async_full(
message,
a->polkit_action,
/* details= */ NULL,
/* good_user= */ UID_INVALID,
interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
&m->polkit_registry,
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 */
}
/* If error_or_denial was set above, it means that a polkit denial or
* error was deferred for a future call to bus_verify_polkit_async_full()
* to catch. In any case, it also means that the payload guarded by
* these polkit calls should never be executed, and hence we should
* never reach this point. */
assert(!error_or_denial);
return 0;
}
static int setup_wall_message_timer(Manager *m, sd_bus_message* message) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
int r;
@ -2442,10 +2306,17 @@ static int method_do_shutdown_or_sleep(
} else if (!a)
assert_se(a = handle_action_lookup(action));
r = verify_shutdown_creds(m, message, a, flags, error);
r = manager_verify_shutdown_creds(m, message, /* link= */ NULL, a, flags, error);
if (r != 0)
return r;
{
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
(void) bus_query_sender_pidref(message, &pidref);
log_shutdown_caller(&pidref, handle_action_to_string(a->handle));
}
if (m->delayed_action)
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
"Action %s already in progress, refusing requested %s operation.",
@ -2454,7 +2325,7 @@ static int method_do_shutdown_or_sleep(
/* reset case we're shorting a scheduled shutdown */
m->unlink_nologin = false;
reset_scheduled_shutdown(m);
manager_reset_scheduled_shutdown(m);
m->scheduled_shutdown_timeout = 0;
m->scheduled_shutdown_action = action;
@ -2568,29 +2439,6 @@ static usec_t nologin_timeout_usec(usec_t elapse) {
return LESS_BY(elapse, 5 * USEC_PER_MINUTE);
}
static void reset_scheduled_shutdown(Manager *m) {
assert(m);
m->scheduled_shutdown_timeout_source = sd_event_source_disable_unref(m->scheduled_shutdown_timeout_source);
m->wall_message_timeout_source = sd_event_source_disable_unref(m->wall_message_timeout_source);
m->nologin_timeout_source = sd_event_source_disable_unref(m->nologin_timeout_source);
m->scheduled_shutdown_action = _HANDLE_ACTION_INVALID;
m->scheduled_shutdown_timeout = USEC_INFINITY;
m->scheduled_shutdown_uid = UID_INVALID;
m->scheduled_shutdown_tty = mfree(m->scheduled_shutdown_tty);
m->shutdown_dry_run = false;
if (m->unlink_nologin) {
(void) unlink_or_warn("/run/nologin");
m->unlink_nologin = false;
}
(void) unlink(SHUTDOWN_SCHEDULE_FILE);
manager_send_changed(m, "ScheduledShutdown");
}
static int update_schedule_file(Manager *m) {
_cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
@ -2669,7 +2517,7 @@ static int manager_scheduled_shutdown_handler(
bus_manager_log_shutdown(m, a);
log_info("Running in dry run, suppressing action.");
reset_scheduled_shutdown(m);
manager_reset_scheduled_shutdown(m);
return 0;
}
@ -2683,7 +2531,7 @@ static int manager_scheduled_shutdown_handler(
return 0;
error:
reset_scheduled_shutdown(m);
manager_reset_scheduled_shutdown(m);
return r;
}
@ -2738,7 +2586,7 @@ void manager_load_scheduled_shutdown(Manager *m) {
"TTY", &tty);
/* reset will delete the file */
reset_scheduled_shutdown(m);
manager_reset_scheduled_shutdown(m);
if (r == -ENOENT)
return;
@ -2784,7 +2632,7 @@ void manager_load_scheduled_shutdown(Manager *m) {
r = manager_setup_shutdown_timers(m);
if (r < 0)
return reset_scheduled_shutdown(m);
return manager_reset_scheduled_shutdown(m);
(void) manager_setup_wall_message_timer(m);
(void) update_schedule_file(m);
@ -2819,7 +2667,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
assert_se(a = handle_action_lookup(handle));
assert(a->polkit_action);
r = verify_shutdown_creds(m, message, a, 0, error);
r = manager_verify_shutdown_creds(m, message, /* link= */ NULL, a, 0, error);
if (r != 0)
return r;
@ -2853,7 +2701,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
r = update_schedule_file(m);
if (r < 0) {
reset_scheduled_shutdown(m);
manager_reset_scheduled_shutdown(m);
return r;
}
@ -2913,7 +2761,7 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
}
cancel_delayed_action(m);
reset_scheduled_shutdown(m);
manager_reset_scheduled_shutdown(m);
return sd_bus_reply_method_return(message, "b", true);
}
@ -2969,7 +2817,7 @@ static int method_can_shutdown_or_sleep(
if (r < 0)
return r;
r = have_multiple_sessions(m, uid);
r = manager_have_multiple_sessions(m, uid);
if (r < 0)
return r;

250
src/login/logind-shutdown.c Normal file
View File

@ -0,0 +1,250 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <unistd.h>
#include "sd-bus.h"
#include "sd-event.h"
#include "sd-varlink.h"
#include "bus-common-errors.h"
#include "bus-polkit.h"
#include "cgroup-util.h"
#include "format-util.h"
#include "fs-util.h"
#include "hashmap.h"
#include "log.h"
#include "login-util.h"
#include "logind.h"
#include "logind-dbus.h"
#include "logind-inhibit.h"
#include "logind-session.h"
#include "logind-shutdown.h"
#include "logind-user.h"
#include "pidref.h"
#include "process-util.h"
#include "user-record.h"
int manager_have_multiple_sessions(
Manager *m,
uid_t uid) {
Session *session;
assert(m);
/* Check for other users' sessions. Greeter sessions do not
* count, and non-login sessions do not count either. */
HASHMAP_FOREACH(session, m->sessions)
if (SESSION_CLASS_IS_INHIBITOR_LIKE(session->class) &&
session->user->user_record->uid != uid)
return true;
return false;
}
void log_shutdown_caller(const PidRef *caller, const char *method) {
_cleanup_free_ char *comm = NULL, *unit = NULL;
assert(method);
if (!pidref_is_set(caller)) {
return log_notice("%s requested from unknown client PID...", method);
}
(void) pidref_get_comm(caller, &comm);
(void) cg_pidref_get_unit(caller, &unit);
log_notice("%s requested from client PID " PID_FMT "%s%s%s%s%s%s...",
method, caller->pid,
comm ? " ('" : "", strempty(comm), comm ? "')" : "",
unit ? " (unit " : "", strempty(unit), unit ? ")" : "");
}
int manager_verify_shutdown_creds(
Manager *m,
sd_bus_message *message,
sd_varlink *link,
const HandleActionData *a,
uint64_t flags,
sd_bus_error *error) {
bool multiple_sessions, blocked, interactive;
_unused_ bool error_or_denial = false;
Inhibitor *offending = NULL;
uid_t uid;
int r;
assert(m);
assert(a);
assert(!!message != !!link); /* exactly one transport */
assert(!link || !error); /* varlink doesn't use sd_bus_error */
if (message) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
if (r < 0)
return r;
r = sd_bus_creds_get_euid(creds, &uid);
if (r < 0)
return r;
} else {
r = sd_varlink_get_peer_uid(link, &uid);
if (r < 0)
return r;
}
r = manager_have_multiple_sessions(m, uid);
if (r < 0)
return r;
multiple_sessions = r > 0;
blocked = manager_is_inhibited(m, a->inhibit_what, NULL, /* flags= */ 0, uid, &offending);
interactive = flags & SD_LOGIND_INTERACTIVE;
if (multiple_sessions) {
if (message)
r = bus_verify_polkit_async_full(
message,
a->polkit_action_multiple_sessions,
/* details= */ NULL,
/* good_user= */ UID_INVALID,
interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
&m->polkit_registry,
error);
else
r = varlink_verify_polkit_async_full(
link,
m->bus,
a->polkit_action_multiple_sessions,
/* details= */ NULL,
/* good_user= */ UID_INVALID,
interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
&m->polkit_registry);
if (r < 0) {
/* If we get -EBUSY, it means a polkit decision was made, but not for
* this action in particular. Assuming we are blocked on inhibitors,
* ignore that error and allow the decision to be revealed below. */
if (blocked && r == -EBUSY)
error_or_denial = true;
else
return r;
}
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
}
if (blocked) {
PolkitFlags polkit_flags = 0;
/* With a strong inhibitor, if the skip flag is not set, reject outright.
* With a weak inhibitor, if root is asking and the root flag is set, reject outright.
* All else, check polkit first. */
if (!FLAGS_SET(flags, SD_LOGIND_SKIP_INHIBITORS) &&
(offending->mode != INHIBIT_BLOCK_WEAK ||
(uid == 0 && FLAGS_SET(flags, SD_LOGIND_ROOT_CHECK_INHIBITORS)))) {
if (link)
return sd_varlink_errorbo(
link,
"io.systemd.Shutdown.BlockedByInhibitor",
SD_JSON_BUILD_PAIR_STRING("who", offending->who),
SD_JSON_BUILD_PAIR_STRING("why", offending->why));
if (error)
return sd_bus_error_set(error, BUS_ERROR_BLOCKED_BY_INHIBITOR_LOCK,
"Operation denied due to active block inhibitor");
return -EACCES;
}
/* We want to always ask here, even for root, to only allow bypassing if explicitly allowed
* by polkit, unless a weak blocker is used, in which case it will be authorized. */
if (offending->mode != INHIBIT_BLOCK_WEAK)
polkit_flags |= POLKIT_ALWAYS_QUERY;
if (interactive)
polkit_flags |= POLKIT_ALLOW_INTERACTIVE;
if (message)
r = bus_verify_polkit_async_full(
message,
a->polkit_action_ignore_inhibit,
/* details= */ NULL,
/* good_user= */ UID_INVALID,
polkit_flags,
&m->polkit_registry,
error);
else
r = varlink_verify_polkit_async_full(
link,
m->bus,
a->polkit_action_ignore_inhibit,
/* details= */ NULL,
/* good_user= */ UID_INVALID,
polkit_flags,
&m->polkit_registry);
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 */
}
if (!multiple_sessions && !blocked) {
if (message)
r = bus_verify_polkit_async_full(
message,
a->polkit_action,
/* details= */ NULL,
/* good_user= */ UID_INVALID,
interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
&m->polkit_registry,
error);
else
r = varlink_verify_polkit_async_full(
link,
m->bus,
a->polkit_action,
/* details= */ NULL,
/* good_user= */ UID_INVALID,
interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
&m->polkit_registry);
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 */
}
/* If error_or_denial was set above, it means that a polkit denial or
* error was deferred for a future call to bus_verify_polkit_async_full()
* to catch. In any case, it also means that the payload guarded by
* these polkit calls should never be executed, and hence we should
* never reach this point. */
assert(!error_or_denial);
return 0;
}
void manager_reset_scheduled_shutdown(Manager *m) {
assert(m);
m->scheduled_shutdown_timeout_source = sd_event_source_disable_unref(m->scheduled_shutdown_timeout_source);
m->wall_message_timeout_source = sd_event_source_disable_unref(m->wall_message_timeout_source);
m->nologin_timeout_source = sd_event_source_disable_unref(m->nologin_timeout_source);
m->scheduled_shutdown_action = _HANDLE_ACTION_INVALID;
m->scheduled_shutdown_timeout = USEC_INFINITY;
m->scheduled_shutdown_uid = UID_INVALID;
m->scheduled_shutdown_tty = mfree(m->scheduled_shutdown_tty);
m->shutdown_dry_run = false;
if (m->unlink_nologin) {
(void) unlink_or_warn("/run/nologin");
m->unlink_nologin = false;
}
(void) unlink(SHUTDOWN_SCHEDULE_FILE);
manager_send_changed(m, "ScheduledShutdown");
}

View File

@ -0,0 +1,17 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "logind-forward.h"
#define SHUTDOWN_SCHEDULE_FILE "/run/systemd/shutdown/scheduled"
int manager_have_multiple_sessions(Manager *m, uid_t uid);
void log_shutdown_caller(const PidRef *caller, const char *method);
/* manager_verify_shutdown_creds() takes *either* a "message" or "link" depending on if it is used
* to validate a D-Bus or Varlink shutdown request. When varlink is used the sd_bus_error *error
* must be NULL */
int manager_verify_shutdown_creds(Manager *m, sd_bus_message *message, sd_varlink *link, const HandleActionData *a, uint64_t flags, sd_bus_error *error);
void manager_reset_scheduled_shutdown(Manager *m);

View File

@ -4,15 +4,19 @@
#include "sd-event.h"
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-polkit.h"
#include "cgroup-util.h"
#include "fd-util.h"
#include "format-util.h"
#include "hashmap.h"
#include "json-util.h"
#include "logind-session.h"
#include "login-util.h"
#include "logind.h"
#include "logind-dbus.h"
#include "logind-seat.h"
#include "logind-session.h"
#include "logind-shutdown.h"
#include "logind-user.h"
#include "logind-varlink.h"
#include "strv.h"
@ -20,6 +24,7 @@
#include "user-record.h"
#include "user-util.h"
#include "varlink-io.systemd.Login.h"
#include "varlink-io.systemd.Shutdown.h"
#include "varlink-io.systemd.service.h"
#include "varlink-util.h"
@ -336,6 +341,101 @@ static int vl_method_release_session(sd_varlink *link, sd_json_variant *paramete
return sd_varlink_reply(link, NULL);
}
static int setup_wall_message_timer(Manager *m, sd_varlink *link) {
uid_t uid = UID_INVALID;
int r;
(void) sd_varlink_get_peer_uid(link, &uid);
m->scheduled_shutdown_uid = uid;
_cleanup_free_ char *tty = NULL;
pid_t pid = 0;
r = sd_varlink_get_peer_pid(link, &pid);
if (r >= 0)
(void) get_ctty(pid, /* ret_devnr= */ NULL, &tty);
r = free_and_strdup_warn(&m->scheduled_shutdown_tty, tty);
if (r < 0)
return log_oom();
return manager_setup_wall_message_timer(m);
}
static int manager_do_shutdown_action(sd_varlink *link, sd_json_variant *parameters, HandleAction action) {
Manager *m = ASSERT_PTR(sd_varlink_get_userdata(link));
int skip_inhibitors = -1;
int r;
static const sd_json_dispatch_field dispatch_table[] = {
{ "skipInhibitors", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, 0, 0 },
VARLINK_DISPATCH_POLKIT_FIELD,
{}
};
r = sd_varlink_dispatch(link, parameters, dispatch_table, &skip_inhibitors);
if (r != 0)
return r;
uint64_t flags = skip_inhibitors > 0 ? SD_LOGIND_SKIP_INHIBITORS : 0;
const HandleActionData *a = handle_action_lookup(action);
assert(a);
r = manager_verify_shutdown_creds(m, /* message= */ NULL, link, a, flags, /* error= */ NULL);
if (r != 0)
return r;
{
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
(void) varlink_get_peer_pidref(link, &pidref);
log_shutdown_caller(&pidref, handle_action_to_string(action));
}
if (m->delayed_action)
return sd_varlink_error(link, "io.systemd.Shutdown.AlreadyInProgress", /* parameters= */ NULL);
/* Reset in case we're short-circuiting a scheduled shutdown */
m->unlink_nologin = false;
manager_reset_scheduled_shutdown(m);
m->scheduled_shutdown_timeout = 0;
m->scheduled_shutdown_action = action;
(void) setup_wall_message_timer(m, link);
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
r = bus_manager_shutdown_or_sleep_now_or_later(m, a, &error);
if (r < 0) {
log_warning_errno(r, "Failed to execute %s: %s",
handle_action_to_string(action),
bus_error_message(&error, r));
return sd_varlink_error_errno(link, r);
}
return sd_varlink_reply(link, NULL);
}
static int vl_method_power_off(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
return manager_do_shutdown_action(link, parameters, HANDLE_POWEROFF);
}
static int vl_method_reboot(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
return manager_do_shutdown_action(link, parameters, HANDLE_REBOOT);
}
static int vl_method_halt(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
return manager_do_shutdown_action(link, parameters, HANDLE_HALT);
}
static int vl_method_kexec(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
return manager_do_shutdown_action(link, parameters, HANDLE_KEXEC);
}
static int vl_method_soft_reboot(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
return manager_do_shutdown_action(link, parameters, HANDLE_SOFT_REBOOT);
}
int manager_varlink_init(Manager *m, int fd) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
_unused_ _cleanup_close_ int fd_close = fd;
@ -358,14 +458,20 @@ int manager_varlink_init(Manager *m, int fd) {
r = sd_varlink_server_add_interface_many(
s,
&vl_interface_io_systemd_Login,
&vl_interface_io_systemd_Shutdown,
&vl_interface_io_systemd_service);
if (r < 0)
return log_error_errno(r, "Failed to add Login interface to varlink server: %m");
return log_error_errno(r, "Failed to add varlink interfaces: %m");
r = sd_varlink_server_bind_method_many(
s,
"io.systemd.Login.CreateSession", vl_method_create_session,
"io.systemd.Login.ReleaseSession", vl_method_release_session,
"io.systemd.Shutdown.PowerOff", vl_method_power_off,
"io.systemd.Shutdown.Reboot", vl_method_reboot,
"io.systemd.Shutdown.Halt", vl_method_halt,
"io.systemd.Shutdown.KExec", vl_method_kexec,
"io.systemd.Shutdown.SoftReboot", vl_method_soft_reboot,
"io.systemd.service.Ping", varlink_method_ping,
"io.systemd.service.SetLogLevel", varlink_method_set_log_level,
"io.systemd.service.GetEnvironment", varlink_method_get_environment);

View File

@ -21,6 +21,7 @@ systemd_logind_extract_sources = files(
'logind-session-dbus.c',
'logind-session-device.c',
'logind-session.c',
'logind-shutdown.c',
'logind-user-dbus.c',
'logind-user.c',
'logind-utmp.c',

View File

@ -624,7 +624,7 @@ int oomd_select_by_swap_usage(Hashmap *h, uint64_t threshold_usage, OomdCGroupCo
int oomd_cgroup_context_acquire(const char *path, OomdCGroupContext **ret) {
_cleanup_(oomd_cgroup_context_unrefp) OomdCGroupContext *ctx = NULL;
_cleanup_free_ char *p = NULL, *val = NULL;
_cleanup_free_ char *p = NULL;
bool is_root;
int r;
@ -678,13 +678,9 @@ int oomd_cgroup_context_acquire(const char *path, OomdCGroupContext **ret) {
else if (r < 0)
return log_debug_errno(r, "Error getting memory.swap.current from %s: %m", path);
r = cg_get_keyed_attribute(path, "memory.stat", STRV_MAKE("pgscan"), &val);
r = cg_get_keyed_attribute_uint64(path, "memory.stat", "pgscan", &ctx->pgscan);
if (r < 0)
return log_debug_errno(r, "Error getting pgscan from memory.stat under %s: %m", path);
r = safe_atou64(val, &ctx->pgscan);
if (r < 0)
return log_debug_errno(r, "Error converting pgscan value to uint64_t: %m");
}
*ret = TAKE_PTR(ctx);

View File

@ -15,4 +15,11 @@ executables += [
'report-basic.c',
),
},
libexec_template + {
'name' : 'systemd-report-cgroup',
'sources' : files(
'report-cgroup.c',
'report-cgroup-server.c',
),
},
]

View File

@ -0,0 +1,131 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include "sd-varlink.h"
#include "alloc-util.h"
#include "ansi-color.h"
#include "build.h"
#include "log.h"
#include "main-func.h"
#include "pretty-print.h"
#include "report-cgroup.h"
#include "varlink-io.systemd.Metrics.h"
#include "varlink-util.h"
static int vl_server(void) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *vs = NULL;
_cleanup_(cgroup_context_freep) CGroupContext *ctx = NULL;
int r;
ctx = new0(CGroupContext, 1);
if (!ctx)
return log_oom();
r = varlink_server_new(&vs, SD_VARLINK_SERVER_INHERIT_USERDATA, ctx);
if (r < 0)
return log_error_errno(r, "Failed to allocate Varlink server: %m");
r = sd_varlink_server_add_interface(vs, &vl_interface_io_systemd_Metrics);
if (r < 0)
return log_error_errno(r, "Failed to add Varlink interface: %m");
r = sd_varlink_server_bind_method_many(
vs,
"io.systemd.Metrics.List", vl_method_list_metrics,
"io.systemd.Metrics.Describe", vl_method_describe_metrics);
if (r < 0)
return log_error_errno(r, "Failed to bind Varlink methods: %m");
r = sd_varlink_server_loop_auto(vs);
if (r < 0)
return log_error_errno(r, "Failed to run Varlink event loop: %m");
return 0;
}
static int help(void) {
_cleanup_free_ char *url = NULL;
int r;
r = terminal_urlify_man("systemd-report-cgroup", "8", &url);
if (r < 0)
return log_oom();
printf("%s [OPTIONS...]\n"
"\n%sReport cgroup metrics.%s\n"
"\n%sOptions:%s\n"
" -h --help Show this help\n"
" --version Show package version\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
ansi_normal(),
ansi_underline(),
ansi_normal(),
url);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{}
};
int c, r;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
switch (c) {
case 'h':
return help();
case ARG_VERSION:
return version();
case '?':
return -EINVAL;
default:
assert_not_reached();
}
if (optind < argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"This program takes no arguments.");
r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
if (r < 0)
return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"This program can only run as a Varlink service.");
return 1;
}
static int run(int argc, char *argv[]) {
int r;
log_setup();
r = parse_argv(argc, argv);
if (r <= 0)
return r;
return vl_server();
}
DEFINE_MAIN_FUNCTION(run);

495
src/report/report-cgroup.c Normal file
View File

@ -0,0 +1,495 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "sd-json.h"
#include "sd-varlink.h"
#include "alloc-util.h"
#include "cgroup-util.h"
#include "extract-word.h"
#include "fd-util.h"
#include "fileio.h"
#include "log.h"
#include "metrics.h"
#include "parse-util.h"
#include "path-util.h"
#include "report-cgroup.h"
#include "string-util.h"
#include "time-util.h"
typedef struct CGroupInfo {
char *unit;
char *path;
uint64_t io_rbytes;
uint64_t io_rios;
int io_stat_cached; /* 0 = not attempted, > 0 = cached, < 0 = -errno */
} CGroupInfo;
static CGroupInfo *cgroup_info_free(CGroupInfo *info) {
if (!info)
return NULL;
free(info->unit);
free(info->path);
return mfree(info);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(CGroupInfo*, cgroup_info_free);
static void cgroup_info_array_free(CGroupInfo **infos, size_t n) {
FOREACH_ARRAY(i, infos, n)
cgroup_info_free(*i);
free(infos);
}
static void cgroup_context_flush(CGroupContext *ctx) {
assert(ctx);
cgroup_info_array_free(ctx->cgroups, ctx->n_cgroups);
ctx->cgroups = NULL;
ctx->n_cgroups = 0;
ctx->cache_populated = false;
}
CGroupContext *cgroup_context_free(CGroupContext *ctx) {
if (!ctx)
return NULL;
cgroup_context_flush(ctx);
return mfree(ctx);
}
static int walk_cgroups_recursive(const char *path, CGroupInfo ***infos, size_t *n_infos) {
_cleanup_closedir_ DIR *d = NULL;
int r;
assert(path);
assert(infos);
assert(n_infos);
/* Collect any unit cgroup we encounter */
_cleanup_free_ char *name = NULL;
r = cg_path_get_unit(path, &name);
if (r >= 0) {
_cleanup_(cgroup_info_freep) CGroupInfo *info = new(CGroupInfo, 1);
if (!info)
return log_oom();
*info = (CGroupInfo) {
.unit = TAKE_PTR(name),
.path = strdup(path),
};
if (!info->path)
return log_oom();
if (!GREEDY_REALLOC(*infos, *n_infos + 1))
return log_oom();
(*infos)[(*n_infos)++] = TAKE_PTR(info);
return 0; /* Unit cgroups are leaf nodes for our purposes */
}
/* Stop at delegation boundaries — don't descend into delegated subtrees */
r = cg_is_delegated(path);
if (r == -ENOENT)
return 0;
if (r < 0)
return log_debug_errno(r, "Failed to check delegation for '%s': %m", path);
if (r > 0)
return 0;
r = cg_enumerate_subgroups(path, &d);
if (r == -ENOENT)
return 0;
if (r < 0)
return log_debug_errno(r, "Failed to enumerate cgroup '%s': %m", path);
for (;;) {
_cleanup_free_ char *fn = NULL, *child = NULL;
r = cg_read_subgroup(d, &fn);
if (r < 0)
return log_debug_errno(r, "Failed to read subgroup from '%s': %m", path);
if (r == 0)
break;
child = path_join(empty_to_root(path), fn);
if (!child)
return log_oom();
path_simplify(child);
r = walk_cgroups_recursive(child, infos, n_infos);
if (r < 0)
return r;
}
return 0;
}
static int walk_cgroups(CGroupContext *ctx, CGroupInfo ***ret, size_t *ret_n) {
int r;
assert(ctx);
assert(ret);
assert(ret_n);
/* Return cached result if available */
if (ctx->cache_populated) {
*ret = ctx->cgroups;
*ret_n = ctx->n_cgroups;
return 0;
}
CGroupInfo **infos = NULL;
size_t n_infos = 0;
CLEANUP_ARRAY(infos, n_infos, cgroup_info_array_free);
r = walk_cgroups_recursive("", &infos, &n_infos);
if (r < 0)
return r;
ctx->cgroups = TAKE_PTR(infos);
ctx->n_cgroups = TAKE_GENERIC(n_infos, size_t, 0);
ctx->cache_populated = true;
*ret = ctx->cgroups;
*ret_n = ctx->n_cgroups;
return 0;
}
static int cpu_usage_build_json(MetricFamilyContext *context, void *userdata) {
CGroupContext *ctx = ASSERT_PTR(userdata);
CGroupInfo **cgroups;
size_t n_cgroups;
int r;
assert(context);
r = walk_cgroups(ctx, &cgroups, &n_cgroups);
if (r < 0)
return 0; /* Skip metric on failure */
FOREACH_ARRAY(c, cgroups, n_cgroups) {
uint64_t us;
r = cg_get_keyed_attribute_uint64((*c)->path, "cpu.stat", "usage_usec", &us);
if (r < 0)
continue;
r = metric_build_send_unsigned(
context,
(*c)->unit,
us * NSEC_PER_USEC,
/* fields= */ NULL);
if (r < 0)
return r;
}
return 0;
}
static int memory_usage_build_json(MetricFamilyContext *context, void *userdata) {
CGroupContext *ctx = ASSERT_PTR(userdata);
CGroupInfo **cgroups;
size_t n_cgroups;
int r;
assert(context);
r = walk_cgroups(ctx, &cgroups, &n_cgroups);
if (r < 0)
return 0;
FOREACH_ARRAY(c, cgroups, n_cgroups) {
uint64_t current = 0, limit = UINT64_MAX;
r = cg_get_attribute_as_uint64((*c)->path, "memory.current", &current);
if (r >= 0) {
/* Walk up the cgroup tree to find the tightest memory limit */
_cleanup_free_ char *path_buf = strdup((*c)->path);
if (!path_buf)
return log_oom();
for (char *p = path_buf;;) {
uint64_t high, max;
r = cg_get_attribute_as_uint64(p, "memory.max", &max);
if (r >= 0 && max < limit)
limit = max;
r = cg_get_attribute_as_uint64(p, "memory.high", &high);
if (r >= 0 && high < limit)
limit = high;
/* Move to parent */
const char *e;
r = path_find_last_component(p, /* accept_dot_dot= */ false, &e, NULL);
if (r <= 0)
break;
p[e - p] = '\0';
}
if (limit != UINT64_MAX && limit > current) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL;
r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", "available"));
if (r < 0)
return r;
r = metric_build_send_unsigned(
context,
(*c)->unit,
limit - current,
fields);
if (r < 0)
return r;
}
_cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL;
r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", "current"));
if (r < 0)
return r;
r = metric_build_send_unsigned(
context,
(*c)->unit,
current,
fields);
if (r < 0)
return r;
}
uint64_t val;
r = cg_get_attribute_as_uint64((*c)->path, "memory.peak", &val);
if (r >= 0) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL;
r = sd_json_buildo(&fields, SD_JSON_BUILD_PAIR_STRING("type", "peak"));
if (r < 0)
return r;
r = metric_build_send_unsigned(
context,
(*c)->unit,
val,
fields);
if (r < 0)
return r;
}
}
return 0;
}
/* Parse io.stat for a cgroup once, summing both rbytes= and rios= fields in a
* single pass to avoid reading the file twice. */
static int io_stat_parse(const char *cgroup_path, uint64_t *ret_rbytes, uint64_t *ret_rios) {
_cleanup_free_ char *path = NULL;
_cleanup_fclose_ FILE *f = NULL;
uint64_t rbytes = 0, rios = 0;
int r;
r = cg_get_path(cgroup_path, "io.stat", &path);
if (r < 0)
return r;
f = fopen(path, "re");
if (!f)
return -errno;
for (;;) {
_cleanup_free_ char *line = NULL;
const char *p;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return r;
if (r == 0)
break;
p = line;
p += strcspn(p, WHITESPACE);
p += strspn(p, WHITESPACE);
for (;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return r;
if (r == 0)
break;
const char *v;
uint64_t val;
v = startswith(word, "rbytes=");
if (v && safe_atou64(v, &val) >= 0) {
rbytes += val;
continue;
}
v = startswith(word, "rios=");
if (v && safe_atou64(v, &val) >= 0)
rios += val;
}
}
*ret_rbytes = rbytes;
*ret_rios = rios;
return 0;
}
static int ensure_io_stat_cached(CGroupInfo *info) {
int r;
assert(info);
if (info->io_stat_cached > 0)
return 0;
if (info->io_stat_cached < 0)
return info->io_stat_cached;
r = io_stat_parse(info->path, &info->io_rbytes, &info->io_rios);
if (r < 0) {
if (r != -ENOENT)
log_debug_errno(r, "Failed to parse IO stats for '%s': %m", info->path);
info->io_stat_cached = r;
return r;
}
info->io_stat_cached = 1;
return 0;
}
static int io_read_bytes_build_json(MetricFamilyContext *context, void *userdata) {
CGroupContext *ctx = ASSERT_PTR(userdata);
CGroupInfo **cgroups;
size_t n_cgroups;
int r;
assert(context);
r = walk_cgroups(ctx, &cgroups, &n_cgroups);
if (r < 0)
return 0;
FOREACH_ARRAY(c, cgroups, n_cgroups) {
if (ensure_io_stat_cached(*c) < 0)
continue;
r = metric_build_send_unsigned(
context,
(*c)->unit,
(*c)->io_rbytes,
/* fields= */ NULL);
if (r < 0)
return r;
}
return 0;
}
static int io_read_operations_build_json(MetricFamilyContext *context, void *userdata) {
CGroupContext *ctx = ASSERT_PTR(userdata);
CGroupInfo **cgroups;
size_t n_cgroups;
int r;
assert(context);
r = walk_cgroups(ctx, &cgroups, &n_cgroups);
if (r < 0)
return 0;
FOREACH_ARRAY(c, cgroups, n_cgroups) {
if (ensure_io_stat_cached(*c) < 0)
continue;
r = metric_build_send_unsigned(
context,
(*c)->unit,
(*c)->io_rios,
/* fields= */ NULL);
if (r < 0)
return r;
}
return 0;
}
static int tasks_current_build_json(MetricFamilyContext *context, void *userdata) {
CGroupContext *ctx = ASSERT_PTR(userdata);
CGroupInfo **cgroups;
size_t n_cgroups;
int r;
assert(context);
r = walk_cgroups(ctx, &cgroups, &n_cgroups);
if (r < 0)
return 0;
FOREACH_ARRAY(c, cgroups, n_cgroups) {
uint64_t val;
r = cg_get_attribute_as_uint64((*c)->path, "pids.current", &val);
if (r < 0)
continue;
r = metric_build_send_unsigned(
context,
(*c)->unit,
val,
/* fields= */ NULL);
if (r < 0)
return r;
}
return 0;
}
static const MetricFamily cgroup_metric_family_table[] = {
/* Keep metrics ordered alphabetically */
{
.name = METRIC_IO_SYSTEMD_CGROUP_PREFIX "CpuUsage",
.description = "Per unit metric: CPU usage in nanoseconds",
.type = METRIC_FAMILY_TYPE_COUNTER,
.generate = cpu_usage_build_json,
},
{
.name = METRIC_IO_SYSTEMD_CGROUP_PREFIX "IOReadBytes",
.description = "Per unit metric: IO bytes read",
.type = METRIC_FAMILY_TYPE_COUNTER,
.generate = io_read_bytes_build_json,
},
{
.name = METRIC_IO_SYSTEMD_CGROUP_PREFIX "IOReadOperations",
.description = "Per unit metric: IO read operations",
.type = METRIC_FAMILY_TYPE_COUNTER,
.generate = io_read_operations_build_json,
},
{
.name = METRIC_IO_SYSTEMD_CGROUP_PREFIX "MemoryUsage",
.description = "Per unit metric: memory usage in bytes",
.type = METRIC_FAMILY_TYPE_GAUGE,
.generate = memory_usage_build_json,
},
{
.name = METRIC_IO_SYSTEMD_CGROUP_PREFIX "TasksCurrent",
.description = "Per unit metric: current number of tasks",
.type = METRIC_FAMILY_TYPE_GAUGE,
.generate = tasks_current_build_json,
},
{}
};
int vl_method_describe_metrics(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
return metrics_method_describe(cgroup_metric_family_table, link, parameters, flags, userdata);
}
int vl_method_list_metrics(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
CGroupContext *ctx = ASSERT_PTR(userdata);
int r;
r = metrics_method_list(cgroup_metric_family_table, link, parameters, flags, userdata);
cgroup_context_flush(ctx);
return r;
}

View File

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "shared-forward.h"
#define METRIC_IO_SYSTEMD_CGROUP_PREFIX "io.systemd.CGroup."
typedef struct CGroupInfo CGroupInfo;
typedef struct CGroupContext {
CGroupInfo **cgroups;
size_t n_cgroups;
bool cache_populated;
} CGroupContext;
CGroupContext *cgroup_context_free(CGroupContext *ctx);
DEFINE_TRIVIAL_CLEANUP_FUNC(CGroupContext*, cgroup_context_free);
int vl_method_list_metrics(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
int vl_method_describe_metrics(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);

View File

@ -100,7 +100,8 @@ static int dnssec_rsa_verify_raw(
return -EIO;
e = m = NULL;
assert((size_t) RSA_size(rpubkey) == signature_size);
if ((size_t) RSA_size(rpubkey) != signature_size)
return -EINVAL;
epubkey = EVP_PKEY_new();
if (!epubkey)
@ -230,9 +231,11 @@ static int dnssec_ecdsa_verify_raw(
if (EC_KEY_set_public_key(eckey, p) <= 0)
return log_debug_errno(SYNTHETIC_ERRNO(EIO),
"EC_POINT_bn2point failed: 0x%lx", ERR_get_error());
"EC_KEY_set_public_key failed: 0x%lx", ERR_get_error());
assert(EC_KEY_check_key(eckey) == 1);
if (EC_KEY_check_key(eckey) != 1)
return log_debug_errno(SYNTHETIC_ERRNO(EIO),
"EC_KEY_check_key failed: 0x%lx", ERR_get_error());
r = BN_bin2bn(signature_r, signature_r_size, NULL);
if (!r)

View File

@ -1036,7 +1036,8 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
dns_server_unref(m->current_dns_server);
m->current_dns_server = dns_server_ref(s);
if (m->unicast_scope)
/* Skip flushing the cache if server stale feature is enabled. */
if (m->unicast_scope && m->stale_retention_usec == 0)
dns_cache_flush(&m->unicast_scope->cache);
(void) manager_send_changed(m, "CurrentDNSServer");
@ -1155,6 +1156,10 @@ void dns_server_flush_cache(DnsServer *s) {
if (!scope)
return;
/* Skip flushing the cache if server stale feature is enabled. */
if (s->manager->stale_retention_usec > 0)
return;
dns_cache_flush(&scope->cache);
}

View File

@ -230,6 +230,7 @@ shared_sources = files(
'varlink-io.systemd.Resolve.c',
'varlink-io.systemd.Resolve.Hook.c',
'varlink-io.systemd.Resolve.Monitor.c',
'varlink-io.systemd.Shutdown.c',
'varlink-io.systemd.Udev.c',
'varlink-io.systemd.Unit.c',
'varlink-io.systemd.UserDatabase.c',

View File

@ -0,0 +1,57 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "bus-polkit.h"
#include "varlink-io.systemd.Shutdown.h"
static SD_VARLINK_DEFINE_METHOD(
PowerOff,
VARLINK_DEFINE_POLKIT_INPUT,
SD_VARLINK_FIELD_COMMENT("Skip active inhibitors and force the operation"),
SD_VARLINK_DEFINE_INPUT(skipInhibitors, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
Reboot,
VARLINK_DEFINE_POLKIT_INPUT,
SD_VARLINK_FIELD_COMMENT("Skip active inhibitors and force the operation"),
SD_VARLINK_DEFINE_INPUT(skipInhibitors, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
Halt,
VARLINK_DEFINE_POLKIT_INPUT,
SD_VARLINK_FIELD_COMMENT("Skip active inhibitors and force the operation"),
SD_VARLINK_DEFINE_INPUT(skipInhibitors, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
KExec,
VARLINK_DEFINE_POLKIT_INPUT,
SD_VARLINK_FIELD_COMMENT("Skip active inhibitors and force the operation"),
SD_VARLINK_DEFINE_INPUT(skipInhibitors, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
SoftReboot,
VARLINK_DEFINE_POLKIT_INPUT,
SD_VARLINK_FIELD_COMMENT("Skip active inhibitors and force the operation"),
SD_VARLINK_DEFINE_INPUT(skipInhibitors, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_ERROR(AlreadyInProgress);
static SD_VARLINK_DEFINE_ERROR(
BlockedByInhibitor,
SD_VARLINK_FIELD_COMMENT("Who is holding the inhibitor"),
SD_VARLINK_DEFINE_FIELD(who, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Why the inhibitor is held"),
SD_VARLINK_DEFINE_FIELD(why, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
SD_VARLINK_DEFINE_INTERFACE(
io_systemd_Shutdown,
"io.systemd.Shutdown",
SD_VARLINK_INTERFACE_COMMENT("APIs for shutting down or rebooting the system."),
SD_VARLINK_SYMBOL_COMMENT("Power off the system"),
&vl_method_PowerOff,
SD_VARLINK_SYMBOL_COMMENT("Reboot the system"),
&vl_method_Reboot,
SD_VARLINK_SYMBOL_COMMENT("Halt the system"),
&vl_method_Halt,
SD_VARLINK_SYMBOL_COMMENT("Reboot the system via kexec"),
&vl_method_KExec,
SD_VARLINK_SYMBOL_COMMENT("Reboot userspace only"),
&vl_method_SoftReboot,
SD_VARLINK_SYMBOL_COMMENT("Another shutdown or sleep operation is already in progress"),
&vl_error_AlreadyInProgress,
SD_VARLINK_SYMBOL_COMMENT("Operation denied due to active block inhibitor"),
&vl_error_BlockedByInhibitor);

View File

@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-varlink-idl.h"
extern const sd_varlink_interface vl_interface_io_systemd_Shutdown;

View File

@ -51,7 +51,7 @@ foreach p : out.stdout().split()
#
# Also, backslashes get mangled, so skip test. See
# https://github.com/mesonbuild/meson/issues/1564.
if p.contains('\\')
if p.contains('\\') or not fs.exists(p)
continue
endif
fuzzer = fs.name(fs.parent(p))

View File

@ -30,6 +30,13 @@ REPORT=/usr/lib/systemd/systemd-report
"$REPORT" describe-metrics io.systemd piff
"$REPORT" describe-metrics piff
# test io.systemd.CGroup Metrics
systemctl start systemd-report-cgroup.socket
varlinkctl info /run/systemd/report/io.systemd.CGroup
varlinkctl list-methods /run/systemd/report/io.systemd.CGroup
varlinkctl --more call /run/systemd/report/io.systemd.CGroup io.systemd.Metrics.List {}
varlinkctl --more call /run/systemd/report/io.systemd.CGroup io.systemd.Metrics.Describe {}
# test io.systemd.Network Metrics
varlinkctl info /run/systemd/report/io.systemd.Network
varlinkctl list-methods /run/systemd/report/io.systemd.Network

View File

@ -6,6 +6,7 @@ endif
files = [['README' ],
['home.conf' ],
['root.conf' ],
['journal-nocow.conf' ],
['portables.conf', 'ENABLE_PORTABLED'],
['systemd-network.conf', 'ENABLE_NETWORKD' ],

10
tmpfiles.d/root.conf Normal file
View File

@ -0,0 +1,10 @@
# 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.
# See tmpfiles.d(5) for details.
z / 555 - - -

View File

@ -724,6 +724,8 @@ units = [
'file' : 'systemd-repart@.service',
'conditions' : ['ENABLE_REPART'],
},
{ 'file' : 'systemd-report-cgroup.socket' },
{ 'file' : 'systemd-report-cgroup@.service.in' },
{
'file' : 'systemd-resolved.service.in',
'conditions' : ['ENABLE_RESOLVE'],

View File

@ -13,7 +13,7 @@ Documentation=man:systemd-logind.service(8)
[Socket]
ListenStream=/run/systemd/io.systemd.Login
Symlinks=/run/varlink/registry/io.systemd.Login
Symlinks=/run/varlink/registry/io.systemd.Login /run/varlink/registry/io.systemd.Shutdown /run/systemd/io.systemd.Shutdown
FileDescriptorName=varlink
SocketMode=0666
Service=systemd-logind.service

View File

@ -0,0 +1,25 @@
# 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.
[Unit]
Description=CGroup Report Varlink Socket
DefaultDependencies=no
Before=sockets.target shutdown.target
Conflicts=shutdown.target
[Socket]
ListenStream=/run/systemd/report/io.systemd.CGroup
FileDescriptorName=varlink
SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16
RemoveOnStop=yes
[Install]
WantedBy=sockets.target

View File

@ -0,0 +1,42 @@
# 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.
[Unit]
Description=CGroup Report Service
DefaultDependencies=no
Conflicts=shutdown.target
Before=shutdown.target
[Service]
CapabilityBoundingSet=
DeviceAllow=
DynamicUser=yes
IPAddressDeny=any
LockPersonality=yes
MemoryDenyWriteExecute=yes
PrivateDevices=yes
PrivateIPC=yes
PrivateNetwork=yes
PrivateTmp=disconnected
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectSystem=strict
RestrictAddressFamilies=AF_UNIX
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
RuntimeMaxSec=1min
SystemCallArchitectures=native
SystemCallErrorNumber=EPERM
SystemCallFilter=@system-service
ExecStart={{LIBEXECDIR}}/systemd-report-cgroup