mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
Merge pull request #12030 from poettering/condition-memory
add ConditionCPUs= + ConditionMemory=
This commit is contained in:
commit
3f8f021541
@ -1002,6 +1002,8 @@
|
||||
<term><varname>ConditionUser=</varname></term>
|
||||
<term><varname>ConditionGroup=</varname></term>
|
||||
<term><varname>ConditionControlGroupController=</varname></term>
|
||||
<term><varname>ConditionMemory=</varname></term>
|
||||
<term><varname>ConditionCPUs=</varname></term>
|
||||
|
||||
<!-- We do not document ConditionNull=
|
||||
here, as it is not particularly
|
||||
@ -1117,11 +1119,12 @@
|
||||
the exact assignment is looked for with right and left hand
|
||||
side matching.</para>
|
||||
|
||||
<para><varname>ConditionKernelVersion=</varname> may be used to check whether the kernel version (as reported
|
||||
by <command>uname -r</command>) matches a certain expression (or if prefixed with the exclamation mark does not
|
||||
match it). The argument must be a single string. If the string starts with one of <literal><</literal>,
|
||||
<literal><=</literal>, <literal>=</literal>, <literal>>=</literal>, <literal>></literal> a relative
|
||||
version comparison is done, otherwise the specified string is matched with shell-style globs.</para>
|
||||
<para><varname>ConditionKernelVersion=</varname> may be used to check whether the kernel version (as
|
||||
reported by <command>uname -r</command>) matches a certain expression (or if prefixed with the
|
||||
exclamation mark does not match it). The argument must be a single string. If the string starts with
|
||||
one of <literal><</literal>, <literal><=</literal>, <literal>=</literal>,
|
||||
<literal>!=</literal>, <literal>>=</literal>, <literal>></literal> a relative version
|
||||
comparison is done, otherwise the specified string is matched with shell-style globs.</para>
|
||||
|
||||
<para>Note that using the kernel version string is an unreliable way to determine which features are supported
|
||||
by a kernel, because of the widespread practice of backporting drivers, features, and fixes from newer upstream
|
||||
@ -1255,6 +1258,24 @@
|
||||
<literal>blkio</literal>, <literal>memory</literal>,
|
||||
<literal>devices</literal>, and <literal>pids</literal>.</para>
|
||||
|
||||
<para><varname>ConditionMemory=</varname> verifies if the specified amount of system memory is
|
||||
available to the current system. Takes a memory size in bytes as argument, optionally prefixed with a
|
||||
comparison operator <literal><</literal>, <literal><=</literal>, <literal>=</literal>,
|
||||
<literal>!=</literal>, <literal>>=</literal>, <literal>></literal>. On bare-metal systems
|
||||
compares the amount of physical memory in the system with the specified size, adhering to the
|
||||
specified comparison operator. In containers compares the amount of memory assigned to the container
|
||||
instead.</para>
|
||||
|
||||
<para><varname>ConditionCPUs=</varname> verifies if the specified number of CPUs is available to the
|
||||
current system. Takes a number of CPUs as argument, optionally prefixed with a comparison operator
|
||||
<literal><</literal>, <literal><=</literal>, <literal>=</literal>, <literal>!=</literal>,
|
||||
<literal>>=</literal>, <literal>></literal>. Compares the number of CPUs in the CPU affinity mask
|
||||
configured of the service manager itself with the specified number, adhering to the specified
|
||||
comparision operator. On physical systems the number of CPUs in the affinity mask of the service
|
||||
manager usually matches the number of physical CPUs, but in special and virtual environments might
|
||||
differ. In particular, in containers the affinity mask usually matches the number of CPUs assigned to
|
||||
the container and not the physically available ones.</para>
|
||||
|
||||
<para>If multiple conditions are specified, the unit will be
|
||||
executed if all of them apply (i.e. a logical AND is applied).
|
||||
Condition checks can be prefixed with a pipe symbol (|) in
|
||||
|
@ -1549,6 +1549,40 @@ int set_oom_score_adjust(int value) {
|
||||
WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
|
||||
}
|
||||
|
||||
int cpus_in_affinity_mask(void) {
|
||||
size_t n = 16;
|
||||
int r;
|
||||
|
||||
for (;;) {
|
||||
cpu_set_t *c;
|
||||
|
||||
c = CPU_ALLOC(n);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0) {
|
||||
int k;
|
||||
|
||||
k = CPU_COUNT_S(CPU_ALLOC_SIZE(n), c);
|
||||
CPU_FREE(c);
|
||||
|
||||
if (k <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
r = -errno;
|
||||
CPU_FREE(c);
|
||||
|
||||
if (r != -EINVAL)
|
||||
return r;
|
||||
if (n > SIZE_MAX/2)
|
||||
return -ENOMEM;
|
||||
n *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const ioprio_class_table[] = {
|
||||
[IOPRIO_CLASS_NONE] = "none",
|
||||
[IOPRIO_CLASS_RT] = "realtime",
|
||||
|
@ -194,3 +194,5 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX)
|
||||
(pid) = 0; \
|
||||
_pid_; \
|
||||
})
|
||||
|
||||
int cpus_in_affinity_mask(void);
|
||||
|
@ -22,16 +22,17 @@
|
||||
#include "cgroup-util.h"
|
||||
#include "condition.h"
|
||||
#include "efivars.h"
|
||||
#include "env-file.h"
|
||||
#include "extract-word.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "glob-util.h"
|
||||
#include "hostname-util.h"
|
||||
#include "ima-util.h"
|
||||
#include "limits-util.h"
|
||||
#include "list.h"
|
||||
#include "macro.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "env-file.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "proc-cmdline.h"
|
||||
@ -48,23 +49,25 @@
|
||||
|
||||
Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
|
||||
Condition *c;
|
||||
int r;
|
||||
|
||||
assert(type >= 0);
|
||||
assert(type < _CONDITION_TYPE_MAX);
|
||||
assert((!parameter) == (type == CONDITION_NULL));
|
||||
|
||||
c = new0(Condition, 1);
|
||||
c = new(Condition, 1);
|
||||
if (!c)
|
||||
return NULL;
|
||||
|
||||
c->type = type;
|
||||
c->trigger = trigger;
|
||||
c->negate = negate;
|
||||
*c = (Condition) {
|
||||
.type = type,
|
||||
.trigger = trigger,
|
||||
.negate = negate,
|
||||
};
|
||||
|
||||
r = free_and_strdup(&c->parameter, parameter);
|
||||
if (r < 0) {
|
||||
return mfree(c);
|
||||
if (parameter) {
|
||||
c->parameter = strdup(parameter);
|
||||
if (!c->parameter)
|
||||
return mfree(c);
|
||||
}
|
||||
|
||||
return c;
|
||||
@ -132,29 +135,77 @@ static int condition_test_kernel_command_line(Condition *c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static int condition_test_kernel_version(Condition *c) {
|
||||
enum {
|
||||
/* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest
|
||||
* should be listed first. */
|
||||
LOWER_OR_EQUAL,
|
||||
GREATER_OR_EQUAL,
|
||||
LOWER,
|
||||
GREATER,
|
||||
EQUAL,
|
||||
_ORDER_MAX,
|
||||
};
|
||||
typedef enum {
|
||||
/* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest
|
||||
* should be listed first. */
|
||||
ORDER_LOWER_OR_EQUAL,
|
||||
ORDER_GREATER_OR_EQUAL,
|
||||
ORDER_LOWER,
|
||||
ORDER_GREATER,
|
||||
ORDER_EQUAL,
|
||||
ORDER_UNEQUAL,
|
||||
_ORDER_MAX,
|
||||
_ORDER_INVALID = -1
|
||||
} OrderOperator;
|
||||
|
||||
static OrderOperator parse_order(const char **s) {
|
||||
|
||||
static const char *const prefix[_ORDER_MAX] = {
|
||||
[LOWER_OR_EQUAL] = "<=",
|
||||
[GREATER_OR_EQUAL] = ">=",
|
||||
[LOWER] = "<",
|
||||
[GREATER] = ">",
|
||||
[EQUAL] = "=",
|
||||
[ORDER_LOWER_OR_EQUAL] = "<=",
|
||||
[ORDER_GREATER_OR_EQUAL] = ">=",
|
||||
[ORDER_LOWER] = "<",
|
||||
[ORDER_GREATER] = ">",
|
||||
[ORDER_EQUAL] = "=",
|
||||
[ORDER_UNEQUAL] = "!=",
|
||||
};
|
||||
const char *p = NULL;
|
||||
|
||||
OrderOperator i;
|
||||
|
||||
for (i = 0; i < _ORDER_MAX; i++) {
|
||||
const char *e;
|
||||
|
||||
e = startswith(*s, prefix[i]);
|
||||
if (e) {
|
||||
*s = e;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return _ORDER_INVALID;
|
||||
}
|
||||
|
||||
static bool test_order(int k, OrderOperator p) {
|
||||
|
||||
switch (p) {
|
||||
|
||||
case ORDER_LOWER:
|
||||
return k < 0;
|
||||
|
||||
case ORDER_LOWER_OR_EQUAL:
|
||||
return k <= 0;
|
||||
|
||||
case ORDER_EQUAL:
|
||||
return k == 0;
|
||||
|
||||
case ORDER_UNEQUAL:
|
||||
return k != 0;
|
||||
|
||||
case ORDER_GREATER_OR_EQUAL:
|
||||
return k >= 0;
|
||||
|
||||
case ORDER_GREATER:
|
||||
return k > 0;
|
||||
|
||||
default:
|
||||
assert_not_reached("unknown order");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static int condition_test_kernel_version(Condition *c) {
|
||||
OrderOperator order;
|
||||
struct utsname u;
|
||||
size_t i;
|
||||
int k;
|
||||
const char *p;
|
||||
|
||||
assert(c);
|
||||
assert(c->parameter);
|
||||
@ -162,38 +213,64 @@ static int condition_test_kernel_version(Condition *c) {
|
||||
|
||||
assert_se(uname(&u) >= 0);
|
||||
|
||||
for (i = 0; i < _ORDER_MAX; i++) {
|
||||
p = startswith(c->parameter, prefix[i]);
|
||||
if (p)
|
||||
break;
|
||||
}
|
||||
p = c->parameter;
|
||||
order = parse_order(&p);
|
||||
|
||||
/* No prefix? Then treat as glob string */
|
||||
if (!p)
|
||||
if (order < 0)
|
||||
return fnmatch(skip_leading_chars(c->parameter, NULL), u.release, 0) == 0;
|
||||
|
||||
k = str_verscmp(u.release, skip_leading_chars(p, NULL));
|
||||
return test_order(str_verscmp(u.release, skip_leading_chars(p, NULL)), order);
|
||||
}
|
||||
|
||||
switch (i) {
|
||||
static int condition_test_memory(Condition *c) {
|
||||
OrderOperator order;
|
||||
uint64_t m, k;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
case LOWER:
|
||||
return k < 0;
|
||||
assert(c);
|
||||
assert(c->parameter);
|
||||
assert(c->type == CONDITION_MEMORY);
|
||||
|
||||
case LOWER_OR_EQUAL:
|
||||
return k <= 0;
|
||||
m = physical_memory();
|
||||
|
||||
case EQUAL:
|
||||
return k == 0;
|
||||
p = c->parameter;
|
||||
order = parse_order(&p);
|
||||
if (order < 0)
|
||||
order = ORDER_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */
|
||||
|
||||
case GREATER_OR_EQUAL:
|
||||
return k >= 0;
|
||||
r = safe_atou64(p, &k);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse size: %m");
|
||||
|
||||
case GREATER:
|
||||
return k > 0;
|
||||
return test_order(CMP(m, k), order);
|
||||
}
|
||||
|
||||
default:
|
||||
assert_not_reached("Can't compare");
|
||||
}
|
||||
static int condition_test_cpus(Condition *c) {
|
||||
OrderOperator order;
|
||||
const char *p;
|
||||
unsigned k;
|
||||
int r, n;
|
||||
|
||||
assert(c);
|
||||
assert(c->parameter);
|
||||
assert(c->type == CONDITION_CPUS);
|
||||
|
||||
n = cpus_in_affinity_mask();
|
||||
if (n < 0)
|
||||
return log_debug_errno(n, "Failed to determine CPUs in affinity mask: %m");
|
||||
|
||||
p = c->parameter;
|
||||
order = parse_order(&p);
|
||||
if (order < 0)
|
||||
order = ORDER_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */
|
||||
|
||||
r = safe_atou(p, &k);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse number of CPUs: %m");
|
||||
|
||||
return test_order(CMP((unsigned) n, k), order);
|
||||
}
|
||||
|
||||
static int condition_test_user(Condition *c) {
|
||||
@ -629,6 +706,8 @@ int condition_test(Condition *c) {
|
||||
[CONDITION_GROUP] = condition_test_group,
|
||||
[CONDITION_CONTROL_GROUP_CONTROLLER] = condition_test_control_group_controller,
|
||||
[CONDITION_NULL] = condition_test_null,
|
||||
[CONDITION_CPUS] = condition_test_cpus,
|
||||
[CONDITION_MEMORY] = condition_test_memory,
|
||||
};
|
||||
|
||||
int r, b;
|
||||
@ -740,7 +819,9 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
|
||||
[CONDITION_USER] = "ConditionUser",
|
||||
[CONDITION_GROUP] = "ConditionGroup",
|
||||
[CONDITION_CONTROL_GROUP_CONTROLLER] = "ConditionControlGroupController",
|
||||
[CONDITION_NULL] = "ConditionNull"
|
||||
[CONDITION_NULL] = "ConditionNull",
|
||||
[CONDITION_CPUS] = "ConditionCPUs",
|
||||
[CONDITION_MEMORY] = "ConditionMemory",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
|
||||
@ -768,7 +849,9 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
|
||||
[CONDITION_USER] = "AssertUser",
|
||||
[CONDITION_GROUP] = "AssertGroup",
|
||||
[CONDITION_CONTROL_GROUP_CONTROLLER] = "AssertControlGroupController",
|
||||
[CONDITION_NULL] = "AssertNull"
|
||||
[CONDITION_NULL] = "AssertNull",
|
||||
[CONDITION_CPUS] = "AssertCPUs",
|
||||
[CONDITION_MEMORY] = "AssertMemory",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
|
||||
|
@ -16,6 +16,8 @@ typedef enum ConditionType {
|
||||
CONDITION_SECURITY,
|
||||
CONDITION_CAPABILITY,
|
||||
CONDITION_AC_POWER,
|
||||
CONDITION_MEMORY,
|
||||
CONDITION_CPUS,
|
||||
|
||||
CONDITION_NEEDS_UPDATE,
|
||||
CONDITION_FIRST_BOOT,
|
||||
|
@ -17,9 +17,11 @@
|
||||
#include "hostname-util.h"
|
||||
#include "id128-util.h"
|
||||
#include "ima-util.h"
|
||||
#include "limits-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "process-util.h"
|
||||
#include "selinux-util.h"
|
||||
#include "set.h"
|
||||
#include "smack-util.h"
|
||||
@ -673,6 +675,127 @@ static void test_condition_test_group(void) {
|
||||
condition_free(condition);
|
||||
}
|
||||
|
||||
static void test_condition_test_cpus_one(const char *s, bool result) {
|
||||
Condition *condition;
|
||||
int r;
|
||||
|
||||
log_debug("%s=%s", condition_type_to_string(CONDITION_CPUS), s);
|
||||
|
||||
condition = condition_new(CONDITION_CPUS, s, false, false);
|
||||
assert_se(condition);
|
||||
|
||||
r = condition_test(condition);
|
||||
assert_se(r >= 0);
|
||||
assert_se(r == result);
|
||||
condition_free(condition);
|
||||
}
|
||||
|
||||
static void test_condition_test_cpus(void) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
int cpus;
|
||||
|
||||
cpus = cpus_in_affinity_mask();
|
||||
assert_se(cpus >= 0);
|
||||
|
||||
test_condition_test_cpus_one("> 0", true);
|
||||
test_condition_test_cpus_one(">= 0", true);
|
||||
test_condition_test_cpus_one("!= 0", true);
|
||||
test_condition_test_cpus_one("<= 0", false);
|
||||
test_condition_test_cpus_one("< 0", false);
|
||||
test_condition_test_cpus_one("= 0", false);
|
||||
|
||||
test_condition_test_cpus_one("> 100000", false);
|
||||
test_condition_test_cpus_one("= 100000", false);
|
||||
test_condition_test_cpus_one(">= 100000", false);
|
||||
test_condition_test_cpus_one("< 100000", true);
|
||||
test_condition_test_cpus_one("!= 100000", true);
|
||||
test_condition_test_cpus_one("<= 100000", true);
|
||||
|
||||
assert_se(asprintf(&t, "= %i", cpus) >= 0);
|
||||
test_condition_test_cpus_one(t, true);
|
||||
t = mfree(t);
|
||||
|
||||
assert_se(asprintf(&t, "<= %i", cpus) >= 0);
|
||||
test_condition_test_cpus_one(t, true);
|
||||
t = mfree(t);
|
||||
|
||||
assert_se(asprintf(&t, ">= %i", cpus) >= 0);
|
||||
test_condition_test_cpus_one(t, true);
|
||||
t = mfree(t);
|
||||
|
||||
assert_se(asprintf(&t, "!= %i", cpus) >= 0);
|
||||
test_condition_test_cpus_one(t, false);
|
||||
t = mfree(t);
|
||||
|
||||
assert_se(asprintf(&t, "< %i", cpus) >= 0);
|
||||
test_condition_test_cpus_one(t, false);
|
||||
t = mfree(t);
|
||||
|
||||
assert_se(asprintf(&t, "> %i", cpus) >= 0);
|
||||
test_condition_test_cpus_one(t, false);
|
||||
t = mfree(t);
|
||||
}
|
||||
|
||||
static void test_condition_test_memory_one(const char *s, bool result) {
|
||||
Condition *condition;
|
||||
int r;
|
||||
|
||||
log_debug("%s=%s", condition_type_to_string(CONDITION_MEMORY), s);
|
||||
|
||||
condition = condition_new(CONDITION_MEMORY, s, false, false);
|
||||
assert_se(condition);
|
||||
|
||||
r = condition_test(condition);
|
||||
assert_se(r >= 0);
|
||||
assert_se(r == result);
|
||||
condition_free(condition);
|
||||
}
|
||||
|
||||
static void test_condition_test_memory(void) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
uint64_t memory;
|
||||
|
||||
memory = physical_memory();
|
||||
|
||||
test_condition_test_memory_one("> 0", true);
|
||||
test_condition_test_memory_one(">= 0", true);
|
||||
test_condition_test_memory_one("!= 0", true);
|
||||
test_condition_test_memory_one("<= 0", false);
|
||||
test_condition_test_memory_one("< 0", false);
|
||||
test_condition_test_memory_one("= 0", false);
|
||||
|
||||
test_condition_test_memory_one("> 18446744073709547520", false);
|
||||
test_condition_test_memory_one("= 18446744073709547520", false);
|
||||
test_condition_test_memory_one(">= 18446744073709547520", false);
|
||||
test_condition_test_memory_one("< 18446744073709547520", true);
|
||||
test_condition_test_memory_one("!= 18446744073709547520", true);
|
||||
test_condition_test_memory_one("<= 18446744073709547520", true);
|
||||
|
||||
assert_se(asprintf(&t, "= %" PRIu64, memory) >= 0);
|
||||
test_condition_test_memory_one(t, true);
|
||||
t = mfree(t);
|
||||
|
||||
assert_se(asprintf(&t, "<= %" PRIu64, memory) >= 0);
|
||||
test_condition_test_memory_one(t, true);
|
||||
t = mfree(t);
|
||||
|
||||
assert_se(asprintf(&t, ">= %" PRIu64, memory) >= 0);
|
||||
test_condition_test_memory_one(t, true);
|
||||
t = mfree(t);
|
||||
|
||||
assert_se(asprintf(&t, "!= %" PRIu64, memory) >= 0);
|
||||
test_condition_test_memory_one(t, false);
|
||||
t = mfree(t);
|
||||
|
||||
assert_se(asprintf(&t, "< %" PRIu64, memory) >= 0);
|
||||
test_condition_test_memory_one(t, false);
|
||||
t = mfree(t);
|
||||
|
||||
assert_se(asprintf(&t, "> %" PRIu64, memory) >= 0);
|
||||
test_condition_test_memory_one(t, false);
|
||||
t = mfree(t);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
|
||||
@ -689,6 +812,8 @@ int main(int argc, char *argv[]) {
|
||||
test_condition_test_user();
|
||||
test_condition_test_group();
|
||||
test_condition_test_control_group_controller();
|
||||
test_condition_test_cpus();
|
||||
test_condition_test_memory();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user