From ba22ff13a07a9478921c5342ef07dd5dcdd0e7d1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 18 Mar 2019 16:43:33 +0100 Subject: [PATCH 1/7] condition: split out kernel version comparison steps Let's split out the operator parsing and the final verdict determination. That way we can reuse this logic for other purposes later on. --- src/shared/condition.c | 113 ++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 47 deletions(-) diff --git a/src/shared/condition.c b/src/shared/condition.c index fb77966264..12c685acd5 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -128,29 +128,72 @@ 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_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] = "=", }; - 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_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); @@ -158,38 +201,14 @@ 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)); - - switch (i) { - - case LOWER: - return k < 0; - - case LOWER_OR_EQUAL: - return k <= 0; - - case EQUAL: - return k == 0; - - case GREATER_OR_EQUAL: - return k >= 0; - - case GREATER: - return k > 0; - - default: - assert_not_reached("Can't compare"); - } + return test_order(str_verscmp(u.release, skip_leading_chars(p, NULL)), order); } static int condition_test_user(Condition *c) { From 20ee849d57e141e03a577554803da00d84b8f5ac Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 18 Mar 2019 17:41:27 +0100 Subject: [PATCH 2/7] process-util: add new helper cpus_in_affinity_mask() Let's add a new helper for determining how many CPUs are configured for us --- src/basic/process-util.c | 34 ++++++++++++++++++++++++++++++++++ src/basic/process-util.h | 2 ++ 2 files changed, 36 insertions(+) diff --git a/src/basic/process-util.c b/src/basic/process-util.c index c41a2aa5c9..1f228be5d3 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1535,6 +1535,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", diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 9950723996..f6eb82d6b6 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -193,3 +193,5 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX) (pid) = 0; \ _pid_; \ }) + +int cpus_in_affinity_mask(void); From 78d7652549b9733e978fe0686f6318bd7fd6e2f2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 18 Mar 2019 17:42:56 +0100 Subject: [PATCH 3/7] condition: use structured initialization --- src/shared/condition.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/shared/condition.c b/src/shared/condition.c index 12c685acd5..5853b4d61f 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -22,6 +22,7 @@ #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" @@ -31,7 +32,6 @@ #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 +48,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; From 2877d4287068f3fdc188b6c0b52bd859da229d80 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 18 Mar 2019 17:43:29 +0100 Subject: [PATCH 4/7] condition: for completenes sake at != comparator for ConditionKernelVersion= --- man/systemd.unit.xml | 11 ++++++----- src/shared/condition.c | 5 +++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 7338b11280..8f317c25a7 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -1117,11 +1117,12 @@ the exact assignment is looked for with right and left hand side matching. - ConditionKernelVersion= may be used to check whether the kernel version (as reported - by uname -r) 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 <, - <=, =, >=, > a relative - version comparison is done, otherwise the specified string is matched with shell-style globs. + ConditionKernelVersion= may be used to check whether the kernel version (as + reported by uname -r) 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 <, <=, =, + !=, >=, > a relative version + comparison is done, otherwise the specified string is matched with shell-style globs. 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 diff --git a/src/shared/condition.c b/src/shared/condition.c index 5853b4d61f..38ebaef62b 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -138,6 +138,7 @@ typedef enum { ORDER_LOWER, ORDER_GREATER, ORDER_EQUAL, + ORDER_UNEQUAL, _ORDER_MAX, _ORDER_INVALID = -1 } OrderOperator; @@ -150,6 +151,7 @@ static OrderOperator parse_order(const char **s) { [ORDER_LOWER] = "<", [ORDER_GREATER] = ">", [ORDER_EQUAL] = "=", + [ORDER_UNEQUAL] = "!=", }; OrderOperator i; @@ -180,6 +182,9 @@ static bool test_order(int k, OrderOperator p) { case ORDER_EQUAL: return k == 0; + case ORDER_UNEQUAL: + return k != 0; + case ORDER_GREATER_OR_EQUAL: return k >= 0; From 754f719af2631a0345aba4e8146140840de9c87e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 18 Mar 2019 17:44:18 +0100 Subject: [PATCH 5/7] condition: add ConditionMemory= and ConditionCPUs= We have all the building blocks in place already, let's add this. Fixes: #8990 --- src/shared/condition.c | 61 ++++++++++++++++++++++++++++++++++++++++-- src/shared/condition.h | 2 ++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/shared/condition.c b/src/shared/condition.c index 38ebaef62b..5366d30e67 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -29,6 +29,7 @@ #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" @@ -218,6 +219,56 @@ static int condition_test_kernel_version(Condition *c) { return test_order(str_verscmp(u.release, skip_leading_chars(p, NULL)), order); } +static int condition_test_memory(Condition *c) { + OrderOperator order; + uint64_t m, k; + const char *p; + int r; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_MEMORY); + + m = physical_memory(); + + p = c->parameter; + order = parse_order(&p); + if (order < 0) + order = ORDER_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */ + + r = safe_atou64(p, &k); + if (r < 0) + return log_debug_errno(r, "Failed to parse size: %m"); + + return test_order(CMP(m, k), order); +} + +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) { uid_t id; int r; @@ -651,6 +702,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; @@ -716,7 +769,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); @@ -744,7 +799,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); diff --git a/src/shared/condition.h b/src/shared/condition.h index e69fc366f7..9d196d3345 100644 --- a/src/shared/condition.h +++ b/src/shared/condition.h @@ -16,6 +16,8 @@ typedef enum ConditionType { CONDITION_SECURITY, CONDITION_CAPABILITY, CONDITION_AC_POWER, + CONDITION_MEMORY, + CONDITION_CPUS, CONDITION_NEEDS_UPDATE, CONDITION_FIRST_BOOT, From 2b60d7ea54d92ec0552f15f5725e16f697a628c3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 18 Mar 2019 17:47:13 +0100 Subject: [PATCH 6/7] man: document ConditionMemory= + ConditionCPUs= --- man/systemd.unit.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 8f317c25a7..e9601569ff 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -1002,6 +1002,8 @@ ConditionUser= ConditionGroup= ConditionControlGroupController= + ConditionMemory= + ConditionCPUs=