Merge pull request #13246 from keszybz/add-SystemdOptions-efi-variable
Add efi variable to augment /proc/cmdline
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "acpi-fpdt.h"
|
||||
#include "boot-timestamps.h"
|
||||
#include "efivars.h"
|
||||
#include "efi-loader.h"
|
||||
#include "macro.h"
|
||||
#include "time-util.h"
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "device-nodes.h"
|
||||
#include "dirent-util.h"
|
||||
#include "efivars.h"
|
||||
#include "efi-loader.h"
|
||||
#include "env-file.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "bus-unit-util.h"
|
||||
#include "bus-util.h"
|
||||
#include "cap-list.h"
|
||||
#include "cgroup-setup.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "condition.h"
|
||||
#include "cpu-set-util.h"
|
||||
|
||||
@@ -0,0 +1,860 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <ftw.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cgroup-setup.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "mkdir.h"
|
||||
#include "process-util.h"
|
||||
#include "fileio.h"
|
||||
#include "user-util.h"
|
||||
#include "fd-util.h"
|
||||
|
||||
bool cg_is_unified_wanted(void) {
|
||||
static thread_local int wanted = -1;
|
||||
bool b;
|
||||
const bool is_default = DEFAULT_HIERARCHY == CGROUP_UNIFIED_ALL;
|
||||
_cleanup_free_ char *c = NULL;
|
||||
int r;
|
||||
|
||||
/* If we have a cached value, return that. */
|
||||
if (wanted >= 0)
|
||||
return wanted;
|
||||
|
||||
/* If the hierarchy is already mounted, then follow whatever was chosen for it. */
|
||||
r = cg_unified_cached(true);
|
||||
if (r >= 0)
|
||||
return (wanted = r >= CGROUP_UNIFIED_ALL);
|
||||
|
||||
/* If we were explicitly passed systemd.unified_cgroup_hierarchy, respect that. */
|
||||
r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
|
||||
if (r > 0)
|
||||
return (wanted = b);
|
||||
|
||||
/* If we passed cgroup_no_v1=all with no other instructions, it seems highly unlikely that we want to
|
||||
* use hybrid or legacy hierarchy. */
|
||||
r = proc_cmdline_get_key("cgroup_no_v1", 0, &c);
|
||||
if (r > 0 && streq_ptr(c, "all"))
|
||||
return (wanted = true);
|
||||
|
||||
return (wanted = is_default);
|
||||
}
|
||||
|
||||
bool cg_is_legacy_wanted(void) {
|
||||
static thread_local int wanted = -1;
|
||||
|
||||
/* If we have a cached value, return that. */
|
||||
if (wanted >= 0)
|
||||
return wanted;
|
||||
|
||||
/* Check if we have cgroup v2 already mounted. */
|
||||
if (cg_unified_cached(true) == CGROUP_UNIFIED_ALL)
|
||||
return (wanted = false);
|
||||
|
||||
/* Otherwise, assume that at least partial legacy is wanted,
|
||||
* since cgroup v2 should already be mounted at this point. */
|
||||
return (wanted = true);
|
||||
}
|
||||
|
||||
bool cg_is_hybrid_wanted(void) {
|
||||
static thread_local int wanted = -1;
|
||||
int r;
|
||||
bool b;
|
||||
const bool is_default = DEFAULT_HIERARCHY >= CGROUP_UNIFIED_SYSTEMD;
|
||||
/* We default to true if the default is "hybrid", obviously, but also when the default is "unified",
|
||||
* because if we get called, it means that unified hierarchy was not mounted. */
|
||||
|
||||
/* If we have a cached value, return that. */
|
||||
if (wanted >= 0)
|
||||
return wanted;
|
||||
|
||||
/* If the hierarchy is already mounted, then follow whatever was chosen for it. */
|
||||
if (cg_unified_cached(true) == CGROUP_UNIFIED_ALL)
|
||||
return (wanted = false);
|
||||
|
||||
/* Otherwise, let's see what the kernel command line has to say. Since checking is expensive, cache
|
||||
* a non-error result. */
|
||||
r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
|
||||
|
||||
/* The meaning of the kernel option is reversed wrt. to the return value of this function, hence the
|
||||
* negation. */
|
||||
return (wanted = r > 0 ? !b : is_default);
|
||||
}
|
||||
|
||||
int cg_weight_parse(const char *s, uint64_t *ret) {
|
||||
uint64_t u;
|
||||
int r;
|
||||
|
||||
if (isempty(s)) {
|
||||
*ret = CGROUP_WEIGHT_INVALID;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = safe_atou64(s, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u < CGROUP_WEIGHT_MIN || u > CGROUP_WEIGHT_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
*ret = u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cg_cpu_shares_parse(const char *s, uint64_t *ret) {
|
||||
uint64_t u;
|
||||
int r;
|
||||
|
||||
if (isempty(s)) {
|
||||
*ret = CGROUP_CPU_SHARES_INVALID;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = safe_atou64(s, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
*ret = u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cg_blkio_weight_parse(const char *s, uint64_t *ret) {
|
||||
uint64_t u;
|
||||
int r;
|
||||
|
||||
if (isempty(s)) {
|
||||
*ret = CGROUP_BLKIO_WEIGHT_INVALID;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = safe_atou64(s, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
*ret = u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
|
||||
assert(path);
|
||||
assert(sb);
|
||||
assert(ftwbuf);
|
||||
|
||||
if (typeflag != FTW_DP)
|
||||
return 0;
|
||||
|
||||
if (ftwbuf->level < 1)
|
||||
return 0;
|
||||
|
||||
(void) rmdir(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cg_trim(const char *controller, const char *path, bool delete_root) {
|
||||
_cleanup_free_ char *fs = NULL;
|
||||
int r = 0, q;
|
||||
|
||||
assert(path);
|
||||
|
||||
r = cg_get_path(controller, path, NULL, &fs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
errno = 0;
|
||||
if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) {
|
||||
if (errno == ENOENT)
|
||||
r = 0;
|
||||
else
|
||||
r = errno_or_else(EIO);
|
||||
}
|
||||
|
||||
if (delete_root) {
|
||||
if (rmdir(fs) < 0 && errno != ENOENT)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
q = cg_hybrid_unified();
|
||||
if (q < 0)
|
||||
return q;
|
||||
if (q > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
|
||||
q = cg_trim(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, delete_root);
|
||||
if (q < 0)
|
||||
log_warning_errno(q, "Failed to trim compat systemd cgroup %s: %m", path);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Create a cgroup in the hierarchy of controller.
|
||||
* Returns 0 if the group already existed, 1 on success, negative otherwise.
|
||||
*/
|
||||
int cg_create(const char *controller, const char *path) {
|
||||
_cleanup_free_ char *fs = NULL;
|
||||
int r;
|
||||
|
||||
r = cg_get_path_and_check(controller, path, NULL, &fs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = mkdir_parents(fs, 0755);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = mkdir_errno_wrapper(fs, 0755);
|
||||
if (r == -EEXIST)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = cg_hybrid_unified();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
|
||||
r = cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to create compat systemd cgroup %s: %m", path);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
|
||||
int r, q;
|
||||
|
||||
assert(pid >= 0);
|
||||
|
||||
r = cg_create(controller, path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
q = cg_attach(controller, path, pid);
|
||||
if (q < 0)
|
||||
return q;
|
||||
|
||||
/* This does not remove the cgroup on failure */
|
||||
return r;
|
||||
}
|
||||
|
||||
int cg_attach(const char *controller, const char *path, pid_t pid) {
|
||||
_cleanup_free_ char *fs = NULL;
|
||||
char c[DECIMAL_STR_MAX(pid_t) + 2];
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(pid >= 0);
|
||||
|
||||
r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (pid == 0)
|
||||
pid = getpid_cached();
|
||||
|
||||
xsprintf(c, PID_FMT "\n", pid);
|
||||
|
||||
r = write_string_file(fs, c, WRITE_STRING_FILE_DISABLE_BUFFER);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = cg_hybrid_unified();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
|
||||
r = cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, pid);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to attach "PID_FMT" to compat systemd cgroup %s: %m", pid, path);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
|
||||
int r;
|
||||
|
||||
assert(controller);
|
||||
assert(path);
|
||||
assert(pid >= 0);
|
||||
|
||||
r = cg_attach(controller, path, pid);
|
||||
if (r < 0) {
|
||||
char prefix[strlen(path) + 1];
|
||||
|
||||
/* This didn't work? Then let's try all prefixes of
|
||||
* the destination */
|
||||
|
||||
PATH_FOREACH_PREFIX(prefix, path) {
|
||||
int q;
|
||||
|
||||
q = cg_attach(controller, prefix, pid);
|
||||
if (q >= 0)
|
||||
return q;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int cg_set_access(
|
||||
const char *controller,
|
||||
const char *path,
|
||||
uid_t uid,
|
||||
gid_t gid) {
|
||||
|
||||
struct Attribute {
|
||||
const char *name;
|
||||
bool fatal;
|
||||
};
|
||||
|
||||
/* cgroup v1, aka legacy/non-unified */
|
||||
static const struct Attribute legacy_attributes[] = {
|
||||
{ "cgroup.procs", true },
|
||||
{ "tasks", false },
|
||||
{ "cgroup.clone_children", false },
|
||||
{},
|
||||
};
|
||||
|
||||
/* cgroup v2, aka unified */
|
||||
static const struct Attribute unified_attributes[] = {
|
||||
{ "cgroup.procs", true },
|
||||
{ "cgroup.subtree_control", true },
|
||||
{ "cgroup.threads", false },
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct Attribute* const attributes[] = {
|
||||
[false] = legacy_attributes,
|
||||
[true] = unified_attributes,
|
||||
};
|
||||
|
||||
_cleanup_free_ char *fs = NULL;
|
||||
const struct Attribute *i;
|
||||
int r, unified;
|
||||
|
||||
assert(path);
|
||||
|
||||
if (uid == UID_INVALID && gid == GID_INVALID)
|
||||
return 0;
|
||||
|
||||
unified = cg_unified_controller(controller);
|
||||
if (unified < 0)
|
||||
return unified;
|
||||
|
||||
/* Configure access to the cgroup itself */
|
||||
r = cg_get_path(controller, path, NULL, &fs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = chmod_and_chown(fs, 0755, uid, gid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Configure access to the cgroup's attributes */
|
||||
for (i = attributes[unified]; i->name; i++) {
|
||||
fs = mfree(fs);
|
||||
|
||||
r = cg_get_path(controller, path, i->name, &fs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = chmod_and_chown(fs, 0644, uid, gid);
|
||||
if (r < 0) {
|
||||
if (i->fatal)
|
||||
return r;
|
||||
|
||||
log_debug_errno(r, "Failed to set access on cgroup %s, ignoring: %m", fs);
|
||||
}
|
||||
}
|
||||
|
||||
if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
|
||||
r = cg_hybrid_unified();
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
/* Always propagate access mode from unified to legacy controller */
|
||||
r = cg_set_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, uid, gid);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to set access on compatibility systemd cgroup %s, ignoring: %m", path);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cg_migrate(
|
||||
const char *cfrom,
|
||||
const char *pfrom,
|
||||
const char *cto,
|
||||
const char *pto,
|
||||
CGroupFlags flags) {
|
||||
|
||||
bool done = false;
|
||||
_cleanup_set_free_ Set *s = NULL;
|
||||
int r, ret = 0;
|
||||
pid_t my_pid;
|
||||
|
||||
assert(cfrom);
|
||||
assert(pfrom);
|
||||
assert(cto);
|
||||
assert(pto);
|
||||
|
||||
s = set_new(NULL);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
my_pid = getpid_cached();
|
||||
|
||||
do {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
pid_t pid = 0;
|
||||
done = true;
|
||||
|
||||
r = cg_enumerate_processes(cfrom, pfrom, &f);
|
||||
if (r < 0) {
|
||||
if (ret >= 0 && r != -ENOENT)
|
||||
return r;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
while ((r = cg_read_pid(f, &pid)) > 0) {
|
||||
|
||||
/* This might do weird stuff if we aren't a
|
||||
* single-threaded program. However, we
|
||||
* luckily know we are not */
|
||||
if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
|
||||
continue;
|
||||
|
||||
if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
|
||||
continue;
|
||||
|
||||
/* Ignore kernel threads. Since they can only
|
||||
* exist in the root cgroup, we only check for
|
||||
* them there. */
|
||||
if (cfrom &&
|
||||
empty_or_root(pfrom) &&
|
||||
is_kernel_thread(pid) > 0)
|
||||
continue;
|
||||
|
||||
r = cg_attach(cto, pto, pid);
|
||||
if (r < 0) {
|
||||
if (ret >= 0 && r != -ESRCH)
|
||||
ret = r;
|
||||
} else if (ret == 0)
|
||||
ret = 1;
|
||||
|
||||
done = false;
|
||||
|
||||
r = set_put(s, PID_TO_PTR(pid));
|
||||
if (r < 0) {
|
||||
if (ret >= 0)
|
||||
return r;
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
if (ret >= 0)
|
||||
return r;
|
||||
|
||||
return ret;
|
||||
}
|
||||
} while (!done);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cg_migrate_recursive(
|
||||
const char *cfrom,
|
||||
const char *pfrom,
|
||||
const char *cto,
|
||||
const char *pto,
|
||||
CGroupFlags flags) {
|
||||
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
int r, ret = 0;
|
||||
char *fn;
|
||||
|
||||
assert(cfrom);
|
||||
assert(pfrom);
|
||||
assert(cto);
|
||||
assert(pto);
|
||||
|
||||
ret = cg_migrate(cfrom, pfrom, cto, pto, flags);
|
||||
|
||||
r = cg_enumerate_subgroups(cfrom, pfrom, &d);
|
||||
if (r < 0) {
|
||||
if (ret >= 0 && r != -ENOENT)
|
||||
return r;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
while ((r = cg_read_subgroup(d, &fn)) > 0) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
p = path_join(empty_to_root(pfrom), fn);
|
||||
free(fn);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
r = cg_migrate_recursive(cfrom, p, cto, pto, flags);
|
||||
if (r != 0 && ret >= 0)
|
||||
ret = r;
|
||||
}
|
||||
|
||||
if (r < 0 && ret >= 0)
|
||||
ret = r;
|
||||
|
||||
if (flags & CGROUP_REMOVE) {
|
||||
r = cg_rmdir(cfrom, pfrom);
|
||||
if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY))
|
||||
return r;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cg_migrate_recursive_fallback(
|
||||
const char *cfrom,
|
||||
const char *pfrom,
|
||||
const char *cto,
|
||||
const char *pto,
|
||||
CGroupFlags flags) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(cfrom);
|
||||
assert(pfrom);
|
||||
assert(cto);
|
||||
assert(pto);
|
||||
|
||||
r = cg_migrate_recursive(cfrom, pfrom, cto, pto, flags);
|
||||
if (r < 0) {
|
||||
char prefix[strlen(pto) + 1];
|
||||
|
||||
/* This didn't work? Then let's try all prefixes of the destination */
|
||||
|
||||
PATH_FOREACH_PREFIX(prefix, pto) {
|
||||
int q;
|
||||
|
||||
q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, flags);
|
||||
if (q >= 0)
|
||||
return q;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
|
||||
CGroupController c;
|
||||
CGroupMask done;
|
||||
bool created;
|
||||
int r;
|
||||
|
||||
/* This one will create a cgroup in our private tree, but also
|
||||
* duplicate it in the trees specified in mask, and remove it
|
||||
* in all others.
|
||||
*
|
||||
* Returns 0 if the group already existed in the systemd hierarchy,
|
||||
* 1 on success, negative otherwise.
|
||||
*/
|
||||
|
||||
/* First create the cgroup in our own hierarchy. */
|
||||
r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
created = r;
|
||||
|
||||
/* If we are in the unified hierarchy, we are done now */
|
||||
r = cg_all_unified();
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return created;
|
||||
|
||||
supported &= CGROUP_MASK_V1;
|
||||
mask = CGROUP_MASK_EXTEND_JOINED(mask);
|
||||
done = 0;
|
||||
|
||||
/* Otherwise, do the same in the other hierarchies */
|
||||
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
|
||||
CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
|
||||
const char *n;
|
||||
|
||||
if (!FLAGS_SET(supported, bit))
|
||||
continue;
|
||||
|
||||
if (FLAGS_SET(done, bit))
|
||||
continue;
|
||||
|
||||
n = cgroup_controller_to_string(c);
|
||||
if (FLAGS_SET(mask, bit))
|
||||
(void) cg_create(n, path);
|
||||
else
|
||||
(void) cg_trim(n, path, true);
|
||||
|
||||
done |= CGROUP_MASK_EXTEND_JOINED(bit);
|
||||
}
|
||||
|
||||
return created;
|
||||
}
|
||||
|
||||
int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
|
||||
CGroupController c;
|
||||
CGroupMask done;
|
||||
int r;
|
||||
|
||||
r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = cg_all_unified();
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return 0;
|
||||
|
||||
supported &= CGROUP_MASK_V1;
|
||||
done = 0;
|
||||
|
||||
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
|
||||
CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
|
||||
const char *p = NULL;
|
||||
|
||||
if (!FLAGS_SET(supported, bit))
|
||||
continue;
|
||||
|
||||
if (FLAGS_SET(done, bit))
|
||||
continue;
|
||||
|
||||
if (path_callback)
|
||||
p = path_callback(bit, userdata);
|
||||
if (!p)
|
||||
p = path;
|
||||
|
||||
(void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid);
|
||||
done |= CGROUP_MASK_EXTEND_JOINED(bit);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
|
||||
Iterator i;
|
||||
void *pidp;
|
||||
int r = 0;
|
||||
|
||||
SET_FOREACH(pidp, pids, i) {
|
||||
pid_t pid = PTR_TO_PID(pidp);
|
||||
int q;
|
||||
|
||||
q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
|
||||
CGroupController c;
|
||||
CGroupMask done;
|
||||
int r = 0, q;
|
||||
|
||||
if (!path_equal(from, to)) {
|
||||
r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
q = cg_all_unified();
|
||||
if (q < 0)
|
||||
return q;
|
||||
if (q > 0)
|
||||
return r;
|
||||
|
||||
supported &= CGROUP_MASK_V1;
|
||||
done = 0;
|
||||
|
||||
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
|
||||
CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
|
||||
const char *p = NULL;
|
||||
|
||||
if (!FLAGS_SET(supported, bit))
|
||||
continue;
|
||||
|
||||
if (FLAGS_SET(done, bit))
|
||||
continue;
|
||||
|
||||
if (to_callback)
|
||||
p = to_callback(bit, userdata);
|
||||
if (!p)
|
||||
p = to;
|
||||
|
||||
(void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0);
|
||||
done |= CGROUP_MASK_EXTEND_JOINED(bit);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
|
||||
CGroupController c;
|
||||
CGroupMask done;
|
||||
int r, q;
|
||||
|
||||
r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
q = cg_all_unified();
|
||||
if (q < 0)
|
||||
return q;
|
||||
if (q > 0)
|
||||
return r;
|
||||
|
||||
supported &= CGROUP_MASK_V1;
|
||||
done = 0;
|
||||
|
||||
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
|
||||
CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
|
||||
|
||||
if (!FLAGS_SET(supported, bit))
|
||||
continue;
|
||||
|
||||
if (FLAGS_SET(done, bit))
|
||||
continue;
|
||||
|
||||
(void) cg_trim(cgroup_controller_to_string(c), path, delete_root);
|
||||
done |= CGROUP_MASK_EXTEND_JOINED(bit);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int cg_enable_everywhere(
|
||||
CGroupMask supported,
|
||||
CGroupMask mask,
|
||||
const char *p,
|
||||
CGroupMask *ret_result_mask) {
|
||||
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_free_ char *fs = NULL;
|
||||
CGroupController c;
|
||||
CGroupMask ret = 0;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
||||
if (supported == 0) {
|
||||
if (ret_result_mask)
|
||||
*ret_result_mask = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = cg_all_unified();
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
/* On the legacy hierarchy there's no concept of "enabling" controllers in cgroups defined. Let's claim
|
||||
* complete success right away. (If you wonder why we return the full mask here, rather than zero: the
|
||||
* caller tends to use the returned mask later on to compare if all controllers where properly joined,
|
||||
* and if not requeues realization. This use is the primary purpose of the return value, hence let's
|
||||
* minimize surprises here and reduce triggers for re-realization by always saying we fully
|
||||
* succeeded.) */
|
||||
if (ret_result_mask)
|
||||
*ret_result_mask = mask & supported & CGROUP_MASK_V2; /* If you wonder why we mask this with
|
||||
* CGROUP_MASK_V2: The 'supported' mask
|
||||
* might contain pure-V1 or BPF
|
||||
* controllers, and we never want to
|
||||
* claim that we could enable those with
|
||||
* cgroup.subtree_control */
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
|
||||
CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
|
||||
const char *n;
|
||||
|
||||
if (!FLAGS_SET(CGROUP_MASK_V2, bit))
|
||||
continue;
|
||||
|
||||
if (!FLAGS_SET(supported, bit))
|
||||
continue;
|
||||
|
||||
n = cgroup_controller_to_string(c);
|
||||
{
|
||||
char s[1 + strlen(n) + 1];
|
||||
|
||||
s[0] = FLAGS_SET(mask, bit) ? '+' : '-';
|
||||
strcpy(s + 1, n);
|
||||
|
||||
if (!f) {
|
||||
f = fopen(fs, "we");
|
||||
if (!f)
|
||||
return log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p);
|
||||
}
|
||||
|
||||
r = write_string_stream(f, s, WRITE_STRING_FILE_DISABLE_BUFFER);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to %s controller %s for %s (%s): %m",
|
||||
FLAGS_SET(mask, bit) ? "enable" : "disable", n, p, fs);
|
||||
clearerr(f);
|
||||
|
||||
/* If we can't turn off a controller, leave it on in the reported resulting mask. This
|
||||
* happens for example when we attempt to turn off a controller up in the tree that is
|
||||
* used down in the tree. */
|
||||
if (!FLAGS_SET(mask, bit) && r == -EBUSY) /* You might wonder why we check for EBUSY
|
||||
* only here, and not follow the same logic
|
||||
* for other errors such as EINVAL or
|
||||
* EOPNOTSUPP or anything else. That's
|
||||
* because EBUSY indicates that the
|
||||
* controllers is currently enabled and
|
||||
* cannot be disabled because something down
|
||||
* the hierarchy is still using it. Any other
|
||||
* error most likely means something like "I
|
||||
* never heard of this controller" or
|
||||
* similar. In the former case it's hence
|
||||
* safe to assume the controller is still on
|
||||
* after the failed operation, while in the
|
||||
* latter case it's safer to assume the
|
||||
* controller is unknown and hence certainly
|
||||
* not enabled. */
|
||||
ret |= bit;
|
||||
} else {
|
||||
/* Otherwise, if we managed to turn on a controller, set the bit reflecting that. */
|
||||
if (FLAGS_SET(mask, bit))
|
||||
ret |= bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Let's return the precise set of controllers now enabled for the cgroup. */
|
||||
if (ret_result_mask)
|
||||
*ret_result_mask = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "cgroup-util.h"
|
||||
|
||||
bool cg_is_unified_wanted(void);
|
||||
bool cg_is_legacy_wanted(void);
|
||||
bool cg_is_hybrid_wanted(void);
|
||||
|
||||
int cg_weight_parse(const char *s, uint64_t *ret);
|
||||
int cg_cpu_shares_parse(const char *s, uint64_t *ret);
|
||||
int cg_blkio_weight_parse(const char *s, uint64_t *ret);
|
||||
|
||||
int cg_trim(const char *controller, const char *path, bool delete_root);
|
||||
|
||||
int cg_create(const char *controller, const char *path);
|
||||
int cg_attach(const char *controller, const char *path, pid_t pid);
|
||||
int cg_attach_fallback(const char *controller, const char *path, pid_t pid);
|
||||
int cg_create_and_attach(const char *controller, const char *path, pid_t pid);
|
||||
|
||||
int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
|
||||
int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
|
||||
int cg_migrate_recursive_fallback(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
|
||||
|
||||
int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path);
|
||||
int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata);
|
||||
int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
|
||||
int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
|
||||
int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root);
|
||||
int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p, CGroupMask *ret_result_mask);
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "cgroup-util.h"
|
||||
#include "condition.h"
|
||||
#include "cpu-set-util.h"
|
||||
#include "efivars.h"
|
||||
#include "efi-loader.h"
|
||||
#include "env-file.h"
|
||||
#include "extract-word.h"
|
||||
#include "fd-util.h"
|
||||
|
||||
@@ -1,30 +1,18 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <linux/fs.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "chattr-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "efi-loader.h"
|
||||
#include "efivars.h"
|
||||
#include "fd-util.h"
|
||||
#include "io-util.h"
|
||||
#include "macro.h"
|
||||
#include "parse-util.h"
|
||||
#include "sort-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "strv.h"
|
||||
#include "time-util.h"
|
||||
#include "string-util.h"
|
||||
#include "utf8.h"
|
||||
#include "virt.h"
|
||||
|
||||
@@ -193,202 +181,6 @@ int efi_set_reboot_to_firmware(bool value) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* efi_variable_path(sd_id128_t vendor, const char *name) {
|
||||
char *p;
|
||||
|
||||
if (asprintf(&p,
|
||||
"/sys/firmware/efi/efivars/%s-" SD_ID128_UUID_FORMAT_STR,
|
||||
name, SD_ID128_FORMAT_VAL(vendor)) < 0)
|
||||
return NULL;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
int efi_get_variable(
|
||||
sd_id128_t vendor,
|
||||
const char *name,
|
||||
uint32_t *ret_attribute,
|
||||
void **ret_value,
|
||||
size_t *ret_size) {
|
||||
|
||||
_cleanup_close_ int fd = -1;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
_cleanup_free_ void *buf = NULL;
|
||||
struct stat st;
|
||||
uint32_t a;
|
||||
ssize_t n;
|
||||
|
||||
assert(name);
|
||||
|
||||
p = efi_variable_path(vendor, name);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!ret_value && !ret_size && !ret_attribute) {
|
||||
/* If caller is not interested in anything, just check if the variable exists and is readable
|
||||
* to us. */
|
||||
if (access(p, R_OK) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
if (st.st_size < 4)
|
||||
return -ENODATA;
|
||||
if (st.st_size > 4*1024*1024 + 4)
|
||||
return -E2BIG;
|
||||
|
||||
if (ret_value || ret_attribute) {
|
||||
n = read(fd, &a, sizeof(a));
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if (n != sizeof(a))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (ret_value) {
|
||||
buf = malloc(st.st_size - 4 + 2);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
n = read(fd, buf, (size_t) st.st_size - 4);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if (n != st.st_size - 4)
|
||||
return -EIO;
|
||||
|
||||
/* Always NUL terminate (2 bytes, to protect UTF-16) */
|
||||
((char*) buf)[st.st_size - 4] = 0;
|
||||
((char*) buf)[st.st_size - 4 + 1] = 0;
|
||||
}
|
||||
|
||||
/* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
|
||||
* with a smaller value. */
|
||||
|
||||
if (ret_attribute)
|
||||
*ret_attribute = a;
|
||||
|
||||
if (ret_value)
|
||||
*ret_value = TAKE_PTR(buf);
|
||||
|
||||
if (ret_size)
|
||||
*ret_size = (size_t) st.st_size - 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
|
||||
_cleanup_free_ void *s = NULL;
|
||||
size_t ss = 0;
|
||||
int r;
|
||||
char *x;
|
||||
|
||||
r = efi_get_variable(vendor, name, NULL, &s, &ss);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
x = utf16_to_utf8(s, ss);
|
||||
if (!x)
|
||||
return -ENOMEM;
|
||||
|
||||
*p = x;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int efi_set_variable(
|
||||
sd_id128_t vendor,
|
||||
const char *name,
|
||||
const void *value,
|
||||
size_t size) {
|
||||
|
||||
struct var {
|
||||
uint32_t attr;
|
||||
char buf[];
|
||||
} _packed_ * _cleanup_free_ buf = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
bool saved_flags_valid = false;
|
||||
unsigned saved_flags;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
assert(value || size == 0);
|
||||
|
||||
p = efi_variable_path(vendor, name);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Newer efivarfs protects variables that are not in a whitelist with FS_IMMUTABLE_FL by default, to protect
|
||||
* them for accidental removal and modification. We are not changing these variables accidentally however,
|
||||
* hence let's unset the bit first. */
|
||||
|
||||
r = chattr_path(p, 0, FS_IMMUTABLE_FL, &saved_flags);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_debug_errno(r, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p);
|
||||
|
||||
saved_flags_valid = r >= 0;
|
||||
|
||||
if (size == 0) {
|
||||
if (unlink(p) < 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
|
||||
if (fd < 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
buf = malloc(sizeof(uint32_t) + size);
|
||||
if (!buf) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
|
||||
memcpy(buf->buf, value, size);
|
||||
|
||||
r = loop_write(fd, buf, sizeof(uint32_t) + size, false);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
if (saved_flags_valid) {
|
||||
int q;
|
||||
|
||||
/* Restore the original flags field, just in case */
|
||||
if (fd < 0)
|
||||
q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL, NULL);
|
||||
else
|
||||
q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL, NULL);
|
||||
if (q < 0)
|
||||
log_debug_errno(q, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *v) {
|
||||
_cleanup_free_ char16_t *u16 = NULL;
|
||||
|
||||
u16 = utf8_to_utf16(v, strlen(v));
|
||||
if (!u16)
|
||||
return -ENOMEM;
|
||||
|
||||
return efi_set_variable(vendor, name, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
|
||||
}
|
||||
|
||||
static ssize_t utf16_size(const uint16_t *s, size_t buf_len_bytes) {
|
||||
size_t l = 0;
|
||||
@@ -1,23 +1,7 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#if ! ENABLE_EFI
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "efi/loader-features.h"
|
||||
#include "time-util.h"
|
||||
|
||||
#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
|
||||
#define EFI_VENDOR_GLOBAL SD_ID128_MAKE(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
|
||||
#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001
|
||||
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
|
||||
#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004
|
||||
#include "efivars.h"
|
||||
|
||||
#if ENABLE_EFI
|
||||
|
||||
@@ -28,12 +12,6 @@ int efi_reboot_to_firmware_supported(void);
|
||||
int efi_get_reboot_to_firmware(void);
|
||||
int efi_set_reboot_to_firmware(bool value);
|
||||
|
||||
char* efi_variable_path(sd_id128_t vendor, const char *name);
|
||||
int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size);
|
||||
int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p);
|
||||
int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size);
|
||||
int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *p);
|
||||
|
||||
int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active);
|
||||
int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path);
|
||||
int efi_remove_boot_option(uint16_t id);
|
||||
@@ -74,26 +52,6 @@ static inline int efi_set_reboot_to_firmware(bool value) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline char* efi_variable_path(sd_id128_t vendor, const char *name) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *p) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
@@ -33,6 +33,8 @@ shared_sources = files('''
|
||||
bus-wait-for-units.h
|
||||
calendarspec.c
|
||||
calendarspec.h
|
||||
cgroup-setup.c
|
||||
cgroup-setup.h
|
||||
cgroup-show.c
|
||||
cgroup-show.h
|
||||
clean-ipc.c
|
||||
@@ -58,8 +60,8 @@ shared_sources = files('''
|
||||
dns-domain.h
|
||||
dropin.c
|
||||
dropin.h
|
||||
efivars.c
|
||||
efivars.h
|
||||
efi-loader.c
|
||||
efi-loader.h
|
||||
enable-mempool.c
|
||||
env-file-label.c
|
||||
env-file-label.h
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "alloc-util.h"
|
||||
#include "fileio.h"
|
||||
#include "log.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "raw-reboot.h"
|
||||
#include "reboot-util.h"
|
||||
#include "string-util.h"
|
||||
@@ -96,3 +97,14 @@ int reboot_with_parameter(RebootFlags flags) {
|
||||
|
||||
return log_full_errno(flags & REBOOT_LOG ? LOG_ERR : LOG_DEBUG, errno, "Failed to reboot: %m");
|
||||
}
|
||||
|
||||
int shall_restore_state(void) {
|
||||
bool ret;
|
||||
int r;
|
||||
|
||||
r = proc_cmdline_get_bool("systemd.restore_state", &ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return r > 0 ? ret : true;
|
||||
}
|
||||
|
||||
@@ -11,3 +11,5 @@ typedef enum RebootFlags {
|
||||
|
||||
int read_reboot_parameter(char **parameter);
|
||||
int reboot_with_parameter(RebootFlags flags);
|
||||
|
||||
int shall_restore_state(void);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "fdset.h"
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
#include "time-util.h"
|
||||
|
||||
int serialize_item(FILE *f, const char *key, const char *value);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "macro.h"
|
||||
#include "path-lookup.h"
|
||||
#include "set.h"
|
||||
#include "special.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
@@ -512,3 +513,47 @@ int unit_file_find_fragment(
|
||||
// FIXME: if instance, consider any unit names with different template name
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const rlmap[] = {
|
||||
"emergency", SPECIAL_EMERGENCY_TARGET,
|
||||
"-b", SPECIAL_EMERGENCY_TARGET,
|
||||
"rescue", SPECIAL_RESCUE_TARGET,
|
||||
"single", SPECIAL_RESCUE_TARGET,
|
||||
"-s", SPECIAL_RESCUE_TARGET,
|
||||
"s", SPECIAL_RESCUE_TARGET,
|
||||
"S", SPECIAL_RESCUE_TARGET,
|
||||
"1", SPECIAL_RESCUE_TARGET,
|
||||
"2", SPECIAL_MULTI_USER_TARGET,
|
||||
"3", SPECIAL_MULTI_USER_TARGET,
|
||||
"4", SPECIAL_MULTI_USER_TARGET,
|
||||
"5", SPECIAL_GRAPHICAL_TARGET,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char * const rlmap_initrd[] = {
|
||||
"emergency", SPECIAL_EMERGENCY_TARGET,
|
||||
"rescue", SPECIAL_RESCUE_TARGET,
|
||||
NULL
|
||||
};
|
||||
|
||||
const char* runlevel_to_target(const char *word) {
|
||||
const char * const *rlmap_ptr;
|
||||
size_t i;
|
||||
|
||||
if (!word)
|
||||
return NULL;
|
||||
|
||||
if (in_initrd()) {
|
||||
word = startswith(word, "rd.");
|
||||
if (!word)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rlmap_ptr = in_initrd() ? rlmap_initrd : rlmap;
|
||||
|
||||
for (i = 0; rlmap_ptr[i]; i += 2)
|
||||
if (streq(word, rlmap_ptr[i]))
|
||||
return rlmap_ptr[i+1];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -54,3 +54,5 @@ int unit_file_find_fragment(
|
||||
const char *unit_name,
|
||||
const char **ret_fragment_path,
|
||||
Set **names);
|
||||
|
||||
const char* runlevel_to_target(const char *rl);
|
||||
|
||||
Reference in New Issue
Block a user