Unit.ConditionUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, conditions)
Unit.ConditionGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, conditions)
Unit.ConditionControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, conditions)
+Unit.ConditionOSRelease, config_parse_unit_condition_string, CONDITION_OS_RELEASE, offsetof(Unit, conditions)
Unit.AssertPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, asserts)
Unit.AssertPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, asserts)
Unit.AssertPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, asserts)
Unit.AssertUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, asserts)
Unit.AssertGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, asserts)
Unit.AssertControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, asserts)
+Unit.AssertOSRelease, config_parse_unit_condition_string, CONDITION_OS_RELEASE, offsetof(Unit, asserts)
Unit.CollectMode, config_parse_collect_mode, 0, offsetof(Unit, collect_mode)
Service.PIDFile, config_parse_pid_file, 0, offsetof(Service, pid_file)
Service.ExecCondition, config_parse_exec, SERVICE_EXEC_CONDITION, offsetof(Service, exec_command)
#include "cpu-set-util.h"
#include "efi-loader.h"
#include "env-file.h"
+#include "env-util.h"
#include "extract-word.h"
#include "fd-util.h"
#include "fileio.h"
#include "list.h"
#include "macro.h"
#include "mountpoint-util.h"
+#include "os-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
return true;
}
+static int condition_test_osrelease(Condition *c, char **env) {
+ const char *parameter = c->parameter;
+ int r;
+
+ assert(c);
+ assert(c->parameter);
+ assert(c->type == CONDITION_OS_RELEASE);
+
+ for (;;) {
+ _cleanup_free_ char *key = NULL, *condition = NULL, *actual_value = NULL;
+ OrderOperator order;
+ const char *word;
+ bool matches;
+
+ r = extract_first_word(¶meter, &condition, NULL, EXTRACT_UNQUOTE);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse parameter: %m");
+ if (r == 0)
+ break;
+
+ /* parse_order() needs the string to start with the comparators */
+ word = condition;
+ r = extract_first_word(&word, &key, "!<=>", EXTRACT_RETAIN_SEPARATORS);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse parameter: %m");
+ /* The os-release spec mandates env-var-like key names */
+ if (r == 0 || isempty(word) || !env_name_is_valid(key))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse parameter, key/value format expected: %m");
+
+ /* Do not allow whitespace after the separator, as that's not a valid os-release format */
+ order = parse_order(&word);
+ if (order < 0 || isempty(word) || strchr(WHITESPACE, *word) != NULL)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse parameter, key/value format expected: %m");
+
+ r = parse_os_release(NULL, key, &actual_value);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse os-release: %m");
+
+ /* Might not be comparing versions, so do exact string matching */
+ if (order == ORDER_EQUAL)
+ matches = streq_ptr(actual_value, word);
+ else if (order == ORDER_UNEQUAL)
+ matches = !streq_ptr(actual_value, word);
+ else
+ matches = test_order(strverscmp_improved(actual_value, word), order);
+
+ if (!matches)
+ return false;
+ }
+
+ return true;
+}
+
static int condition_test_memory(Condition *c, char **env) {
OrderOperator order;
uint64_t m, k;
[CONDITION_MEMORY] = condition_test_memory,
[CONDITION_ENVIRONMENT] = condition_test_environment,
[CONDITION_CPU_FEATURE] = condition_test_cpufeature,
+ [CONDITION_OS_RELEASE] = condition_test_osrelease,
};
int r, b;
[CONDITION_MEMORY] = "ConditionMemory",
[CONDITION_ENVIRONMENT] = "ConditionEnvironment",
[CONDITION_CPU_FEATURE] = "ConditionCPUFeature",
+ [CONDITION_OS_RELEASE] = "ConditionOSRelease",
};
DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
[CONDITION_MEMORY] = "AssertMemory",
[CONDITION_ENVIRONMENT] = "AssertEnvironment",
[CONDITION_CPU_FEATURE] = "AssertCPUFeature",
+ [CONDITION_OS_RELEASE] = "AssertOSRelease",
};
DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
#include "log.h"
#include "macro.h"
#include "nulstr-util.h"
+#include "os-util.h"
#include "process-util.h"
#include "selinux-util.h"
#include "set.h"
test_condition_test_environment_one("EXISTINGENVVAR=", false);
}
+static void test_condition_test_os_release(void) {
+ _cleanup_strv_free_ char **os_release_pairs = NULL;
+ _cleanup_free_ char *version_id = NULL;
+ const char *key_value_pair;
+ Condition *condition;
+
+ /* Should not happen, but it's a test so we don't know the environment. */
+ if (load_os_release_pairs(NULL, &os_release_pairs) < 0)
+ return;
+ if (strv_length(os_release_pairs) < 2)
+ return;
+
+ condition = condition_new(CONDITION_OS_RELEASE, "_THISHOPEFULLYWONTEXIST=01234 56789", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == 0);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_OS_RELEASE, "WRONG FORMAT", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == -EINVAL);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_OS_RELEASE, "WRONG!<>=FORMAT", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == -EINVAL);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_OS_RELEASE, "WRONG FORMAT=", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == -EINVAL);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_OS_RELEASE, "WRONG =FORMAT", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == -EINVAL);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_OS_RELEASE, "WRONG = FORMAT", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == -EINVAL);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_OS_RELEASE, "WRONGFORMAT= ", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == -EINVAL);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_OS_RELEASE, "WRO NG=FORMAT", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == -EINVAL);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_OS_RELEASE, "", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ));
+ condition_free(condition);
+
+ /* load_os_release_pairs() removes quotes, we have to add them back,
+ * otherwise we get a string: "PRETTY_NAME=Debian GNU/Linux 10 (buster)"
+ * which is wrong, as the value is not quoted anymore. */
+ const char *quote = strchr(os_release_pairs[1], ' ') ? "\"" : "";
+ key_value_pair = strjoina(os_release_pairs[0], "=", quote, os_release_pairs[1], quote);
+ condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ));
+ condition_free(condition);
+
+ key_value_pair = strjoina(os_release_pairs[0], "!=", quote, os_release_pairs[1], quote);
+ condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == 0);
+ condition_free(condition);
+
+ /* Some distros (eg: Arch) do not set VERSION_ID */
+ if (parse_os_release(NULL, "VERSION_ID", &version_id) <= 0)
+ return;
+
+ key_value_pair = strjoina("VERSION_ID", "=", version_id);
+ condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ));
+ condition_free(condition);
+
+ key_value_pair = strjoina("VERSION_ID", "!=", version_id);
+ condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == 0);
+ condition_free(condition);
+
+ key_value_pair = strjoina("VERSION_ID", "<=", version_id);
+ condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ));
+ condition_free(condition);
+
+ key_value_pair = strjoina("VERSION_ID", ">=", version_id);
+ condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ));
+ condition_free(condition);
+
+ key_value_pair = strjoina("VERSION_ID", "<", version_id, ".1");
+ condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ));
+ condition_free(condition);
+
+ key_value_pair = strjoina("VERSION_ID", ">", version_id, ".1");
+ condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == 0);
+ condition_free(condition);
+
+ key_value_pair = strjoina("VERSION_ID", "=", version_id, " ", os_release_pairs[0], "=", quote, os_release_pairs[1], quote);
+ condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ));
+ condition_free(condition);
+
+ key_value_pair = strjoina("VERSION_ID", "!=", version_id, " ", os_release_pairs[0], "=", quote, os_release_pairs[1], quote);
+ condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == 0);
+ condition_free(condition);
+
+ key_value_pair = strjoina("VERSION_ID", "=", version_id, " ", os_release_pairs[0], "!=", quote, os_release_pairs[1], quote);
+ condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == 0);
+ condition_free(condition);
+
+ key_value_pair = strjoina("VERSION_ID", "!=", version_id, " ", os_release_pairs[0], "!=", quote, os_release_pairs[1], quote);
+ condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == 0);
+ condition_free(condition);
+
+ key_value_pair = strjoina("VERSION_ID", "<", version_id, ".1", " ", os_release_pairs[0], "=", quote, os_release_pairs[1], quote);
+ condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ));
+ condition_free(condition);
+}
+
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
#if defined(__i386__) || defined(__x86_64__)
test_condition_test_cpufeature();
#endif
+ test_condition_test_os_release();
return 0;
}