]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/shared/condition.c
Move cpus_in_affinity_mask() to cpu-set-util.[ch]
[thirdparty/systemd.git] / src / shared / condition.c
index 2acf36d468ed98fbbe49c91fd61af94704d171e4..2d521bc8c69aa0c277e7c02f1cffce7e7bdc2f5c 100644 (file)
 #include "cap-list.h"
 #include "cgroup-util.h"
 #include "condition.h"
+#include "cpu-set-util.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 "mount-util.h"
+#include "mountpoint-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "proc-cmdline.h"
 
 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;
@@ -76,13 +81,17 @@ void condition_free(Condition *c) {
         free(c);
 }
 
-Condition* condition_free_list(Condition *first) {
+Condition* condition_free_list_type(Condition *head, ConditionType type) {
         Condition *c, *n;
 
-        LIST_FOREACH_SAFE(conditions, c, n, first)
-                condition_free(c);
+        LIST_FOREACH_SAFE(conditions, c, n, head)
+                if (type < 0 || c->type == type) {
+                        LIST_REMOVE(conditions, head, c);
+                        condition_free(c);
+                }
 
-        return NULL;
+        assert(type >= 0 || !head);
+        return head;
 }
 
 static int condition_test_kernel_command_line(Condition *c) {
@@ -127,70 +136,144 @@ 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;
-        struct utsname u;
-        size_t i;
-        int k;
 
-        assert(c);
-        assert(c->parameter);
-        assert(c->type == CONDITION_KERNEL_VERSION);
-
-        assert_se(uname(&u) >= 0);
+        OrderOperator i;
 
         for (i = 0; i < _ORDER_MAX; i++) {
-                p = startswith(c->parameter, prefix[i]);
-                if (p)
-                        break;
+                const char *e;
+
+                e = startswith(*s, prefix[i]);
+                if (e) {
+                        *s = e;
+                        return i;
+                }
         }
 
-        /* No prefix? Then treat as glob string */
-        if (!p)
-                return fnmatch(skip_leading_chars(c->parameter, NULL), u.release, 0) == 0;
+        return _ORDER_INVALID;
+}
 
-        k = str_verscmp(u.release, skip_leading_chars(p, NULL));
+static bool test_order(int k, OrderOperator p) {
 
-        switch (i) {
+        switch (p) {
 
-        case LOWER:
+        case ORDER_LOWER:
                 return k < 0;
 
-        case LOWER_OR_EQUAL:
+        case ORDER_LOWER_OR_EQUAL:
                 return k <= 0;
 
-        case EQUAL:
+        case ORDER_EQUAL:
                 return k == 0;
 
-        case GREATER_OR_EQUAL:
+        case ORDER_UNEQUAL:
+                return k != 0;
+
+        case ORDER_GREATER_OR_EQUAL:
                 return k >= 0;
 
-        case GREATER:
+        case ORDER_GREATER:
                 return k > 0;
 
         default:
-                assert_not_reached("Can't compare");
+                assert_not_reached("unknown order");
+
         }
 }
 
+static int condition_test_kernel_version(Condition *c) {
+        OrderOperator order;
+        struct utsname u;
+        const char *p;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_KERNEL_VERSION);
+
+        assert_se(uname(&u) >= 0);
+
+        p = c->parameter;
+        order = parse_order(&p);
+
+        /* No prefix? Then treat as glob string */
+        if (order < 0)
+                return fnmatch(skip_leading_chars(c->parameter, NULL), u.release, 0) == 0;
+
+        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;
@@ -471,7 +554,7 @@ static int condition_test_needs_update(Condition *c) {
                 uint64_t timestamp;
                 int r;
 
-                r = parse_env_file(NULL, p, NULL, "TIMESTAMP_NSEC", &timestamp_str, NULL);
+                r = parse_env_file(NULL, p, "TIMESTAMP_NSEC", &timestamp_str);
                 if (r < 0) {
                         log_error_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p);
                         return true;
@@ -624,6 +707,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;
@@ -643,6 +728,52 @@ int condition_test(Condition *c) {
         return b;
 }
 
+bool condition_test_list(Condition *first, const char *(*to_string)(ConditionType t), condition_test_logger_t logger, void *userdata) {
+        Condition *c;
+        int triggered = -1;
+
+        assert(!!logger == !!to_string);
+
+        /* If the condition list is empty, then it is true */
+        if (!first)
+                return true;
+
+        /* Otherwise, if all of the non-trigger conditions apply and
+         * if any of the trigger conditions apply (unless there are
+         * none) we return true */
+        LIST_FOREACH(conditions, c, first) {
+                int r;
+
+                r = condition_test(c);
+
+                if (logger) {
+                        if (r < 0)
+                                logger(userdata, LOG_WARNING, r, __FILE__, __LINE__, __func__,
+                                       "Couldn't determine result for %s=%s%s%s, assuming failed: %m",
+                                       to_string(c->type),
+                                       c->trigger ? "|" : "",
+                                       c->negate ? "!" : "",
+                                       c->parameter);
+                        else
+                                logger(userdata, LOG_DEBUG, 0, __FILE__, __LINE__, __func__,
+                                       "%s=%s%s%s %s.",
+                                       to_string(c->type),
+                                       c->trigger ? "|" : "",
+                                       c->negate ? "!" : "",
+                                       c->parameter,
+                                       condition_result_to_string(c->result));
+                }
+
+                if (!c->trigger && r <= 0)
+                        return false;
+
+                if (c->trigger && triggered <= 0)
+                        triggered = r > 0;
+        }
+
+        return triggered != 0;
+}
+
 void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
         assert(c);
         assert(f);
@@ -689,7 +820,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);
@@ -717,7 +850,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);