mirror of
https://github.com/systemd/systemd
synced 2026-04-12 10:04:50 +02:00
Compare commits
No commits in common. "ff6b70fe7e2a1c3f0c88697bfa9ad625cd4c5013" and "d3a1710bc22b9047620d1a05f76dee8590255206" have entirely different histories.
ff6b70fe7e
...
d3a1710bc2
59
.github/workflows/claude-review.yml
vendored
59
.github/workflows/claude-review.yml
vendored
@ -256,34 +256,27 @@ jobs:
|
|||||||
|
|
||||||
## Phase 1: Review commits
|
## Phase 1: Review commits
|
||||||
|
|
||||||
First, list the directories in `worktrees/` and read `review-schema.json`.
|
List the directories in `worktrees/` — there is one per commit. Each
|
||||||
Then, spawn exactly one review subagent per worktree directory, all in a
|
worktree at `worktrees/<sha>/` contains the full source tree checked out at
|
||||||
single message so they run concurrently. Do NOT batch or group multiple
|
that commit, plus `commit.patch` (the diff) and `commit-message.txt`
|
||||||
commits into a single agent. Do NOT read any other files before spawning —
|
(the commit message). Spawn one
|
||||||
the subagents will do that themselves.
|
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
|
||||||
Each worktree at `worktrees/<sha>/` contains the full source tree checked
|
will do that themselves.
|
||||||
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
|
Each reviewer reviews design, code quality, style, potential bugs, and
|
||||||
security implications.
|
security implications.
|
||||||
|
|
||||||
Each subagent must be spawned with `model: "opus"`.
|
|
||||||
|
|
||||||
Each subagent prompt must include:
|
Each subagent prompt must include:
|
||||||
- Instructions to read `pr-context.json` in the repository root for additional
|
- Instructions to read `pr-context.json` in the repository root for additional
|
||||||
context.
|
context.
|
||||||
- The contents of `review-schema.json` (paste it into each prompt so the
|
- Instructions to read `review-schema.json` in the repository root and
|
||||||
agent doesn't have to read it separately).
|
return a JSON array matching the `comments` items schema from that file.
|
||||||
- The worktree path.
|
- The worktree path.
|
||||||
- Instructions to read `commit-message.txt` and `commit.patch` in the
|
- Instructions to read `commit-message.txt` and `commit.patch` in the
|
||||||
worktree for the commit message and diff.
|
worktree for the commit message and diff.
|
||||||
- Instructions to verify every `line` and `start_line` value
|
- Instructions to verify every `line` and `start_line` value
|
||||||
against the hunk ranges in `commit.patch` before returning.
|
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
|
## Phase 2: Collect, deduplicate, and summarize
|
||||||
|
|
||||||
@ -297,20 +290,26 @@ jobs:
|
|||||||
populate the `resolve` array.
|
populate the `resolve` array.
|
||||||
- If `tracking_comment` is non-null, use it as the basis for your summary.
|
- 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:
|
Then:
|
||||||
1. Collect all issues. Merge duplicates across agents (same file, same
|
1. Collect all issues. Merge duplicates (same file, lines within 3 of each other, same problem).
|
||||||
problem, lines within 3 of each other).
|
2. Drop low-confidence findings.
|
||||||
2. Drop issues that already have a review comment on the same file about
|
3. Check the existing inline review comments from `pr-context.json`. Do NOT
|
||||||
the same problem, or where the PR author replied disagreeing.
|
include a comment if one already exists on the same file about the same
|
||||||
3. Populate the `resolve` array with the `id` of your own review comment
|
problem, even if the exact line numbers differ (lines shift between
|
||||||
threads (user.login == "github-actions[bot]", body starts with
|
revisions). Also check for author replies that dismiss or reject a previous
|
||||||
"Claude: ") that should be resolved — either because the issue was
|
comment — do NOT re-raise an issue the PR author has already responded to
|
||||||
fixed or because the author dismissed it. Use the first comment `id`
|
disagreeing with.
|
||||||
in each thread. Do not resolve threads from human reviewers.
|
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.
|
||||||
4. Write a `summary` field in markdown for a top-level tracking comment.
|
4. Write a `summary` field in markdown for a top-level tracking comment.
|
||||||
|
|
||||||
**If no existing tracking comment was found (first run):**
|
**If no existing tracking comment was found (first run):**
|
||||||
@ -487,7 +486,7 @@ jobs:
|
|||||||
...(c.side != null && { side: c.side }),
|
...(c.side != null && { side: c.side }),
|
||||||
...(c.start_line != null && { start_line: c.start_line }),
|
...(c.start_line != null && { start_line: c.start_line }),
|
||||||
...(c.start_side != null && { start_side: c.start_side }),
|
...(c.start_side != null && { start_side: c.start_side }),
|
||||||
body: c.body,
|
body: `Claude: **${c.severity}**: ${c.body}`,
|
||||||
});
|
});
|
||||||
posted++;
|
posted++;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
3
NEWS
3
NEWS
@ -22,9 +22,6 @@ CHANGES WITH 261 in spe:
|
|||||||
|
|
||||||
New features:
|
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
|
* Networking to cloud IMDS services may be locked down for recognized
|
||||||
clouds. This is recommended for secure installations, but typically
|
clouds. This is recommended for secure installations, but typically
|
||||||
conflicts with traditional IMDS clients such as cloud-init, which
|
conflicts with traditional IMDS clients such as cloud-init, which
|
||||||
|
|||||||
9
TODO
9
TODO
@ -122,13 +122,12 @@ Features:
|
|||||||
* sysext: make systemd-{sys,conf}ext-sysroot.service work in the split '/var'
|
* sysext: make systemd-{sys,conf}ext-sysroot.service work in the split '/var'
|
||||||
configuration.
|
configuration.
|
||||||
|
|
||||||
* 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: add fully async modes of the protocol upgrade stuff
|
* sd-varlink: add fully async modes of the protocol upgrade stuff
|
||||||
|
|
||||||
|
* 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
|
||||||
|
|
||||||
* repart: maybe remove iso9660/eltorito superblock from disk when booting via
|
* repart: maybe remove iso9660/eltorito superblock from disk when booting via
|
||||||
gpt, if there is one.
|
gpt, if there is one.
|
||||||
|
|
||||||
|
|||||||
@ -84,15 +84,6 @@
|
|||||||
<citerefentry><refentrytitle>org.freedesktop.LogControl1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
<citerefentry><refentrytitle>org.freedesktop.LogControl1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||||
for information about the D-Bus APIs <filename>systemd-logind</filename> provides.</para>
|
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
|
<para>For more information see
|
||||||
<ulink url="https://systemd.io/INHIBITOR_LOCKS">Inhibitor Locks</ulink>.</para>
|
<ulink url="https://systemd.io/INHIBITOR_LOCKS">Inhibitor Locks</ulink>.</para>
|
||||||
|
|
||||||
|
|||||||
@ -2896,13 +2896,7 @@ if git.found()
|
|||||||
'ls-files', ':/*.[ch]', ':/*.cc',
|
'ls-files', ':/*.[ch]', ':/*.cc',
|
||||||
check : false)
|
check : false)
|
||||||
if all_files.returncode() == 0
|
if all_files.returncode() == 0
|
||||||
existing_files = []
|
all_files = files(all_files.stdout().split())
|
||||||
foreach f : all_files.stdout().split()
|
|
||||||
if fs.exists(f)
|
|
||||||
existing_files += f
|
|
||||||
endif
|
|
||||||
endforeach
|
|
||||||
all_files = files(existing_files)
|
|
||||||
|
|
||||||
custom_target(
|
custom_target(
|
||||||
output : 'tags',
|
output : 'tags',
|
||||||
|
|||||||
@ -1560,24 +1560,6 @@ fail:
|
|||||||
return r;
|
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) {
|
int cg_mask_to_string(CGroupMask mask, char **ret) {
|
||||||
_cleanup_free_ char *s = NULL;
|
_cleanup_free_ char *s = NULL;
|
||||||
bool space = false;
|
bool space = false;
|
||||||
|
|||||||
@ -165,7 +165,6 @@ 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_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(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);
|
int cg_get_owner(const char *path, uid_t *ret_uid);
|
||||||
|
|
||||||
|
|||||||
@ -285,14 +285,19 @@ static int process_cpu(Group *g, unsigned iteration) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
} else {
|
} else {
|
||||||
|
_cleanup_free_ char *val = NULL;
|
||||||
uint64_t u;
|
uint64_t u;
|
||||||
|
|
||||||
r = cg_get_keyed_attribute_uint64(g->path, "cpu.stat", "usage_usec", &u);
|
r = cg_get_keyed_attribute(g->path, "cpu.stat", STRV_MAKE("usage_usec"), &val);
|
||||||
if (IN_SET(r, -ENOENT, -ENXIO))
|
if (IN_SET(r, -ENOENT, -ENXIO))
|
||||||
return 0;
|
return 0;
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
r = safe_atou64(val, &u);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
new_usage = u * NSEC_PER_USEC;
|
new_usage = u * NSEC_PER_USEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2996,8 +2996,9 @@ int unit_check_oomd_kill(Unit *u) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int unit_check_oom(Unit *u) {
|
int unit_check_oom(Unit *u) {
|
||||||
|
_cleanup_free_ char *oom_kill = NULL;
|
||||||
bool increased;
|
bool increased;
|
||||||
uint64_t c = 0;
|
uint64_t c;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
|
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
|
||||||
@ -3012,25 +3013,33 @@ int unit_check_oom(Unit *u) {
|
|||||||
* back to reading oom_kill if we can't find the file or field. */
|
* back to reading oom_kill if we can't find the file or field. */
|
||||||
|
|
||||||
if (ctx->memory_oom_group) {
|
if (ctx->memory_oom_group) {
|
||||||
r = cg_get_keyed_attribute_uint64(
|
r = cg_get_keyed_attribute(
|
||||||
crt->cgroup_path,
|
crt->cgroup_path,
|
||||||
"memory.events.local",
|
"memory.events.local",
|
||||||
"oom_group_kill",
|
STRV_MAKE("oom_group_kill"),
|
||||||
&c);
|
&oom_kill);
|
||||||
if (r < 0 && !IN_SET(r, -ENOENT, -ENXIO))
|
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");
|
return log_unit_debug_errno(u, r, "Failed to read oom_group_kill field of memory.events.local cgroup attribute, ignoring: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ctx->memory_oom_group || r < 0) {
|
if (isempty(oom_kill)) {
|
||||||
r = cg_get_keyed_attribute_uint64(
|
r = cg_get_keyed_attribute(
|
||||||
crt->cgroup_path,
|
crt->cgroup_path,
|
||||||
"memory.events",
|
"memory.events",
|
||||||
"oom_kill",
|
STRV_MAKE("oom_kill"),
|
||||||
&c);
|
&oom_kill);
|
||||||
if (r < 0 && !IN_SET(r, -ENOENT, -ENXIO))
|
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");
|
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;
|
increased = c > crt->oom_kill_last;
|
||||||
crt->oom_kill_last = c;
|
crt->oom_kill_last = c;
|
||||||
|
|
||||||
@ -3576,9 +3585,14 @@ static int unit_get_cpu_usage_raw(const Unit *u, const CGroupRuntime *crt, nsec_
|
|||||||
if (unit_has_host_root_cgroup(u))
|
if (unit_has_host_root_cgroup(u))
|
||||||
return procfs_cpu_get_usage(ret);
|
return procfs_cpu_get_usage(ret);
|
||||||
|
|
||||||
|
_cleanup_free_ char *val = NULL;
|
||||||
uint64_t us;
|
uint64_t us;
|
||||||
|
|
||||||
r = cg_get_keyed_attribute_uint64(crt->cgroup_path, "cpu.stat", "usage_usec", &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);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|||||||
@ -1238,6 +1238,7 @@ int bus_socket_take_fd(sd_bus *b) {
|
|||||||
int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
|
int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
|
||||||
struct iovec *iov;
|
struct iovec *iov;
|
||||||
ssize_t k;
|
ssize_t k;
|
||||||
|
size_t n;
|
||||||
unsigned j;
|
unsigned j;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -1253,8 +1254,9 @@ int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
iov = newa(struct iovec, m->n_iovec);
|
n = m->n_iovec * sizeof(struct iovec);
|
||||||
memcpy_safe(iov, m->iovec, sizeof(struct iovec) * m->n_iovec);
|
iov = newa(struct iovec, n);
|
||||||
|
memcpy_safe(iov, m->iovec, n);
|
||||||
|
|
||||||
j = 0;
|
j = 0;
|
||||||
iovec_advance(iov, &j, *idx);
|
iovec_advance(iov, &j, *idx);
|
||||||
|
|||||||
@ -45,7 +45,6 @@
|
|||||||
#include "logind-seat.h"
|
#include "logind-seat.h"
|
||||||
#include "logind-seat-dbus.h"
|
#include "logind-seat-dbus.h"
|
||||||
#include "logind-session-dbus.h"
|
#include "logind-session-dbus.h"
|
||||||
#include "logind-shutdown.h"
|
|
||||||
#include "logind-user.h"
|
#include "logind-user.h"
|
||||||
#include "logind-user-dbus.h"
|
#include "logind-user-dbus.h"
|
||||||
#include "logind-utmp.h"
|
#include "logind-utmp.h"
|
||||||
@ -77,6 +76,10 @@
|
|||||||
*/
|
*/
|
||||||
#define WALL_MESSAGE_MAX 4096U
|
#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(
|
static int get_sender_session(
|
||||||
Manager *m,
|
Manager *m,
|
||||||
sd_bus_message *message,
|
sd_bus_message *message,
|
||||||
@ -1863,6 +1866,24 @@ static int method_flush_devices(sd_bus_message *message, void *userdata, sd_bus_
|
|||||||
return sd_bus_reply_method_return(message, NULL);
|
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(
|
static int bus_manager_log_shutdown(
|
||||||
Manager *m,
|
Manager *m,
|
||||||
const HandleActionData *a) {
|
const HandleActionData *a) {
|
||||||
@ -2168,6 +2189,121 @@ int bus_manager_shutdown_or_sleep_now_or_later(
|
|||||||
return r;
|
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) {
|
static int setup_wall_message_timer(Manager *m, sd_bus_message* message) {
|
||||||
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
|
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
|
||||||
int r;
|
int r;
|
||||||
@ -2306,17 +2442,10 @@ static int method_do_shutdown_or_sleep(
|
|||||||
} else if (!a)
|
} else if (!a)
|
||||||
assert_se(a = handle_action_lookup(action));
|
assert_se(a = handle_action_lookup(action));
|
||||||
|
|
||||||
r = manager_verify_shutdown_creds(m, message, /* link= */ NULL, a, flags, error);
|
r = verify_shutdown_creds(m, message, a, flags, error);
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
return r;
|
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)
|
if (m->delayed_action)
|
||||||
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
|
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
|
||||||
"Action %s already in progress, refusing requested %s operation.",
|
"Action %s already in progress, refusing requested %s operation.",
|
||||||
@ -2325,7 +2454,7 @@ static int method_do_shutdown_or_sleep(
|
|||||||
|
|
||||||
/* reset case we're shorting a scheduled shutdown */
|
/* reset case we're shorting a scheduled shutdown */
|
||||||
m->unlink_nologin = false;
|
m->unlink_nologin = false;
|
||||||
manager_reset_scheduled_shutdown(m);
|
reset_scheduled_shutdown(m);
|
||||||
|
|
||||||
m->scheduled_shutdown_timeout = 0;
|
m->scheduled_shutdown_timeout = 0;
|
||||||
m->scheduled_shutdown_action = action;
|
m->scheduled_shutdown_action = action;
|
||||||
@ -2439,6 +2568,29 @@ static usec_t nologin_timeout_usec(usec_t elapse) {
|
|||||||
return LESS_BY(elapse, 5 * USEC_PER_MINUTE);
|
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) {
|
static int update_schedule_file(Manager *m) {
|
||||||
_cleanup_(unlink_and_freep) char *temp_path = NULL;
|
_cleanup_(unlink_and_freep) char *temp_path = NULL;
|
||||||
_cleanup_fclose_ FILE *f = NULL;
|
_cleanup_fclose_ FILE *f = NULL;
|
||||||
@ -2517,7 +2669,7 @@ static int manager_scheduled_shutdown_handler(
|
|||||||
|
|
||||||
bus_manager_log_shutdown(m, a);
|
bus_manager_log_shutdown(m, a);
|
||||||
log_info("Running in dry run, suppressing action.");
|
log_info("Running in dry run, suppressing action.");
|
||||||
manager_reset_scheduled_shutdown(m);
|
reset_scheduled_shutdown(m);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2531,7 +2683,7 @@ static int manager_scheduled_shutdown_handler(
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
manager_reset_scheduled_shutdown(m);
|
reset_scheduled_shutdown(m);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2586,7 +2738,7 @@ void manager_load_scheduled_shutdown(Manager *m) {
|
|||||||
"TTY", &tty);
|
"TTY", &tty);
|
||||||
|
|
||||||
/* reset will delete the file */
|
/* reset will delete the file */
|
||||||
manager_reset_scheduled_shutdown(m);
|
reset_scheduled_shutdown(m);
|
||||||
|
|
||||||
if (r == -ENOENT)
|
if (r == -ENOENT)
|
||||||
return;
|
return;
|
||||||
@ -2632,7 +2784,7 @@ void manager_load_scheduled_shutdown(Manager *m) {
|
|||||||
|
|
||||||
r = manager_setup_shutdown_timers(m);
|
r = manager_setup_shutdown_timers(m);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return manager_reset_scheduled_shutdown(m);
|
return reset_scheduled_shutdown(m);
|
||||||
|
|
||||||
(void) manager_setup_wall_message_timer(m);
|
(void) manager_setup_wall_message_timer(m);
|
||||||
(void) update_schedule_file(m);
|
(void) update_schedule_file(m);
|
||||||
@ -2667,7 +2819,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
|
|||||||
assert_se(a = handle_action_lookup(handle));
|
assert_se(a = handle_action_lookup(handle));
|
||||||
assert(a->polkit_action);
|
assert(a->polkit_action);
|
||||||
|
|
||||||
r = manager_verify_shutdown_creds(m, message, /* link= */ NULL, a, 0, error);
|
r = verify_shutdown_creds(m, message, a, 0, error);
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -2701,7 +2853,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
|
|||||||
r = update_schedule_file(m);
|
r = update_schedule_file(m);
|
||||||
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
manager_reset_scheduled_shutdown(m);
|
reset_scheduled_shutdown(m);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2761,7 +2913,7 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
|
|||||||
}
|
}
|
||||||
|
|
||||||
cancel_delayed_action(m);
|
cancel_delayed_action(m);
|
||||||
manager_reset_scheduled_shutdown(m);
|
reset_scheduled_shutdown(m);
|
||||||
|
|
||||||
return sd_bus_reply_method_return(message, "b", true);
|
return sd_bus_reply_method_return(message, "b", true);
|
||||||
}
|
}
|
||||||
@ -2817,7 +2969,7 @@ static int method_can_shutdown_or_sleep(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = manager_have_multiple_sessions(m, uid);
|
r = have_multiple_sessions(m, uid);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|||||||
@ -1,250 +0,0 @@
|
|||||||
/* 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");
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
/* 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);
|
|
||||||
@ -4,19 +4,15 @@
|
|||||||
#include "sd-event.h"
|
#include "sd-event.h"
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "bus-error.h"
|
|
||||||
#include "bus-polkit.h"
|
|
||||||
#include "cgroup-util.h"
|
#include "cgroup-util.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "format-util.h"
|
#include "format-util.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "json-util.h"
|
#include "json-util.h"
|
||||||
#include "login-util.h"
|
#include "logind-session.h"
|
||||||
#include "logind.h"
|
#include "logind.h"
|
||||||
#include "logind-dbus.h"
|
#include "logind-dbus.h"
|
||||||
#include "logind-seat.h"
|
#include "logind-seat.h"
|
||||||
#include "logind-session.h"
|
|
||||||
#include "logind-shutdown.h"
|
|
||||||
#include "logind-user.h"
|
#include "logind-user.h"
|
||||||
#include "logind-varlink.h"
|
#include "logind-varlink.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
@ -24,7 +20,6 @@
|
|||||||
#include "user-record.h"
|
#include "user-record.h"
|
||||||
#include "user-util.h"
|
#include "user-util.h"
|
||||||
#include "varlink-io.systemd.Login.h"
|
#include "varlink-io.systemd.Login.h"
|
||||||
#include "varlink-io.systemd.Shutdown.h"
|
|
||||||
#include "varlink-io.systemd.service.h"
|
#include "varlink-io.systemd.service.h"
|
||||||
#include "varlink-util.h"
|
#include "varlink-util.h"
|
||||||
|
|
||||||
@ -341,101 +336,6 @@ static int vl_method_release_session(sd_varlink *link, sd_json_variant *paramete
|
|||||||
return sd_varlink_reply(link, NULL);
|
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) {
|
int manager_varlink_init(Manager *m, int fd) {
|
||||||
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
|
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
|
||||||
_unused_ _cleanup_close_ int fd_close = fd;
|
_unused_ _cleanup_close_ int fd_close = fd;
|
||||||
@ -458,20 +358,14 @@ int manager_varlink_init(Manager *m, int fd) {
|
|||||||
r = sd_varlink_server_add_interface_many(
|
r = sd_varlink_server_add_interface_many(
|
||||||
s,
|
s,
|
||||||
&vl_interface_io_systemd_Login,
|
&vl_interface_io_systemd_Login,
|
||||||
&vl_interface_io_systemd_Shutdown,
|
|
||||||
&vl_interface_io_systemd_service);
|
&vl_interface_io_systemd_service);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to add varlink interfaces: %m");
|
return log_error_errno(r, "Failed to add Login interface to varlink server: %m");
|
||||||
|
|
||||||
r = sd_varlink_server_bind_method_many(
|
r = sd_varlink_server_bind_method_many(
|
||||||
s,
|
s,
|
||||||
"io.systemd.Login.CreateSession", vl_method_create_session,
|
"io.systemd.Login.CreateSession", vl_method_create_session,
|
||||||
"io.systemd.Login.ReleaseSession", vl_method_release_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.Ping", varlink_method_ping,
|
||||||
"io.systemd.service.SetLogLevel", varlink_method_set_log_level,
|
"io.systemd.service.SetLogLevel", varlink_method_set_log_level,
|
||||||
"io.systemd.service.GetEnvironment", varlink_method_get_environment);
|
"io.systemd.service.GetEnvironment", varlink_method_get_environment);
|
||||||
|
|||||||
@ -21,7 +21,6 @@ systemd_logind_extract_sources = files(
|
|||||||
'logind-session-dbus.c',
|
'logind-session-dbus.c',
|
||||||
'logind-session-device.c',
|
'logind-session-device.c',
|
||||||
'logind-session.c',
|
'logind-session.c',
|
||||||
'logind-shutdown.c',
|
|
||||||
'logind-user-dbus.c',
|
'logind-user-dbus.c',
|
||||||
'logind-user.c',
|
'logind-user.c',
|
||||||
'logind-utmp.c',
|
'logind-utmp.c',
|
||||||
|
|||||||
@ -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) {
|
int oomd_cgroup_context_acquire(const char *path, OomdCGroupContext **ret) {
|
||||||
_cleanup_(oomd_cgroup_context_unrefp) OomdCGroupContext *ctx = NULL;
|
_cleanup_(oomd_cgroup_context_unrefp) OomdCGroupContext *ctx = NULL;
|
||||||
_cleanup_free_ char *p = NULL;
|
_cleanup_free_ char *p = NULL, *val = NULL;
|
||||||
bool is_root;
|
bool is_root;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -678,9 +678,13 @@ int oomd_cgroup_context_acquire(const char *path, OomdCGroupContext **ret) {
|
|||||||
else if (r < 0)
|
else if (r < 0)
|
||||||
return log_debug_errno(r, "Error getting memory.swap.current from %s: %m", path);
|
return log_debug_errno(r, "Error getting memory.swap.current from %s: %m", path);
|
||||||
|
|
||||||
r = cg_get_keyed_attribute_uint64(path, "memory.stat", "pgscan", &ctx->pgscan);
|
r = cg_get_keyed_attribute(path, "memory.stat", STRV_MAKE("pgscan"), &val);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_debug_errno(r, "Error getting pgscan from memory.stat under %s: %m", path);
|
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);
|
*ret = TAKE_PTR(ctx);
|
||||||
|
|||||||
@ -15,11 +15,4 @@ executables += [
|
|||||||
'report-basic.c',
|
'report-basic.c',
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
libexec_template + {
|
|
||||||
'name' : 'systemd-report-cgroup',
|
|
||||||
'sources' : files(
|
|
||||||
'report-cgroup.c',
|
|
||||||
'report-cgroup-server.c',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,131 +0,0 @@
|
|||||||
/* 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);
|
|
||||||
@ -1,495 +0,0 @@
|
|||||||
/* 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", ¤t);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
/* 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);
|
|
||||||
@ -100,8 +100,7 @@ static int dnssec_rsa_verify_raw(
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
e = m = NULL;
|
e = m = NULL;
|
||||||
|
|
||||||
if ((size_t) RSA_size(rpubkey) != signature_size)
|
assert((size_t) RSA_size(rpubkey) == signature_size);
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
epubkey = EVP_PKEY_new();
|
epubkey = EVP_PKEY_new();
|
||||||
if (!epubkey)
|
if (!epubkey)
|
||||||
@ -231,11 +230,9 @@ static int dnssec_ecdsa_verify_raw(
|
|||||||
|
|
||||||
if (EC_KEY_set_public_key(eckey, p) <= 0)
|
if (EC_KEY_set_public_key(eckey, p) <= 0)
|
||||||
return log_debug_errno(SYNTHETIC_ERRNO(EIO),
|
return log_debug_errno(SYNTHETIC_ERRNO(EIO),
|
||||||
"EC_KEY_set_public_key failed: 0x%lx", ERR_get_error());
|
"EC_POINT_bn2point failed: 0x%lx", ERR_get_error());
|
||||||
|
|
||||||
if (EC_KEY_check_key(eckey) != 1)
|
assert(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);
|
r = BN_bin2bn(signature_r, signature_r_size, NULL);
|
||||||
if (!r)
|
if (!r)
|
||||||
|
|||||||
@ -1036,8 +1036,7 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
|
|||||||
dns_server_unref(m->current_dns_server);
|
dns_server_unref(m->current_dns_server);
|
||||||
m->current_dns_server = dns_server_ref(s);
|
m->current_dns_server = dns_server_ref(s);
|
||||||
|
|
||||||
/* Skip flushing the cache if server stale feature is enabled. */
|
if (m->unicast_scope)
|
||||||
if (m->unicast_scope && m->stale_retention_usec == 0)
|
|
||||||
dns_cache_flush(&m->unicast_scope->cache);
|
dns_cache_flush(&m->unicast_scope->cache);
|
||||||
|
|
||||||
(void) manager_send_changed(m, "CurrentDNSServer");
|
(void) manager_send_changed(m, "CurrentDNSServer");
|
||||||
@ -1156,10 +1155,6 @@ void dns_server_flush_cache(DnsServer *s) {
|
|||||||
if (!scope)
|
if (!scope)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Skip flushing the cache if server stale feature is enabled. */
|
|
||||||
if (s->manager->stale_retention_usec > 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
dns_cache_flush(&scope->cache);
|
dns_cache_flush(&scope->cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -230,7 +230,6 @@ shared_sources = files(
|
|||||||
'varlink-io.systemd.Resolve.c',
|
'varlink-io.systemd.Resolve.c',
|
||||||
'varlink-io.systemd.Resolve.Hook.c',
|
'varlink-io.systemd.Resolve.Hook.c',
|
||||||
'varlink-io.systemd.Resolve.Monitor.c',
|
'varlink-io.systemd.Resolve.Monitor.c',
|
||||||
'varlink-io.systemd.Shutdown.c',
|
|
||||||
'varlink-io.systemd.Udev.c',
|
'varlink-io.systemd.Udev.c',
|
||||||
'varlink-io.systemd.Unit.c',
|
'varlink-io.systemd.Unit.c',
|
||||||
'varlink-io.systemd.UserDatabase.c',
|
'varlink-io.systemd.UserDatabase.c',
|
||||||
|
|||||||
@ -1,57 +0,0 @@
|
|||||||
/* 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);
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
/* 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;
|
|
||||||
@ -51,7 +51,7 @@ foreach p : out.stdout().split()
|
|||||||
#
|
#
|
||||||
# Also, backslashes get mangled, so skip test. See
|
# Also, backslashes get mangled, so skip test. See
|
||||||
# https://github.com/mesonbuild/meson/issues/1564.
|
# https://github.com/mesonbuild/meson/issues/1564.
|
||||||
if p.contains('\\') or not fs.exists(p)
|
if p.contains('\\')
|
||||||
continue
|
continue
|
||||||
endif
|
endif
|
||||||
fuzzer = fs.name(fs.parent(p))
|
fuzzer = fs.name(fs.parent(p))
|
||||||
|
|||||||
@ -30,13 +30,6 @@ REPORT=/usr/lib/systemd/systemd-report
|
|||||||
"$REPORT" describe-metrics io.systemd piff
|
"$REPORT" describe-metrics io.systemd piff
|
||||||
"$REPORT" describe-metrics 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
|
# test io.systemd.Network Metrics
|
||||||
varlinkctl info /run/systemd/report/io.systemd.Network
|
varlinkctl info /run/systemd/report/io.systemd.Network
|
||||||
varlinkctl list-methods /run/systemd/report/io.systemd.Network
|
varlinkctl list-methods /run/systemd/report/io.systemd.Network
|
||||||
|
|||||||
@ -6,7 +6,6 @@ endif
|
|||||||
|
|
||||||
files = [['README' ],
|
files = [['README' ],
|
||||||
['home.conf' ],
|
['home.conf' ],
|
||||||
['root.conf' ],
|
|
||||||
['journal-nocow.conf' ],
|
['journal-nocow.conf' ],
|
||||||
['portables.conf', 'ENABLE_PORTABLED'],
|
['portables.conf', 'ENABLE_PORTABLED'],
|
||||||
['systemd-network.conf', 'ENABLE_NETWORKD' ],
|
['systemd-network.conf', 'ENABLE_NETWORKD' ],
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
# 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 - - -
|
|
||||||
@ -724,8 +724,6 @@ units = [
|
|||||||
'file' : 'systemd-repart@.service',
|
'file' : 'systemd-repart@.service',
|
||||||
'conditions' : ['ENABLE_REPART'],
|
'conditions' : ['ENABLE_REPART'],
|
||||||
},
|
},
|
||||||
{ 'file' : 'systemd-report-cgroup.socket' },
|
|
||||||
{ 'file' : 'systemd-report-cgroup@.service.in' },
|
|
||||||
{
|
{
|
||||||
'file' : 'systemd-resolved.service.in',
|
'file' : 'systemd-resolved.service.in',
|
||||||
'conditions' : ['ENABLE_RESOLVE'],
|
'conditions' : ['ENABLE_RESOLVE'],
|
||||||
|
|||||||
@ -13,7 +13,7 @@ Documentation=man:systemd-logind.service(8)
|
|||||||
|
|
||||||
[Socket]
|
[Socket]
|
||||||
ListenStream=/run/systemd/io.systemd.Login
|
ListenStream=/run/systemd/io.systemd.Login
|
||||||
Symlinks=/run/varlink/registry/io.systemd.Login /run/varlink/registry/io.systemd.Shutdown /run/systemd/io.systemd.Shutdown
|
Symlinks=/run/varlink/registry/io.systemd.Login
|
||||||
FileDescriptorName=varlink
|
FileDescriptorName=varlink
|
||||||
SocketMode=0666
|
SocketMode=0666
|
||||||
Service=systemd-logind.service
|
Service=systemd-logind.service
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
# 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
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
# 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
|
|
||||||
Loading…
x
Reference in New Issue
Block a user