teknoraver / rpms / systemd

Forked from rpms/systemd 2 months ago
Clone

Blame SOURCES/0547-support-ranges-when-parsing-CPUAffinity.patch

ecbff1
From 6e00430563108b98230abd7407ac54fde61ae93c Mon Sep 17 00:00:00 2001
ecbff1
From: Jan Synacek <jsynacek@redhat.com>
ecbff1
Date: Tue, 26 Sep 2017 12:34:19 +0200
ecbff1
Subject: [PATCH] support ranges when parsing CPUAffinity
ecbff1
ecbff1
The functionality was implemented in https://github.com/systemd/systemd/pull/1699/.
ecbff1
However, it is not backportable without considerable code changes.
ecbff1
ecbff1
Implement parse_range() and parse_cpu_set_and_warn() from the upstream master
ecbff1
branch and use them in appropriate places. Also introduce relevant tests.
ecbff1
ecbff1
Resolves: #1493976
ecbff1
---
de8967
 src/core/load-fragment.c |  49 ++-----
de8967
 src/core/main.c          |  48 +------
de8967
 src/shared/util.c        |  91 ++++++++++++
ecbff1
 src/shared/util.h        |   9 ++
de8967
 src/test/test-util.c     | 296 +++++++++++++++++++++++++++++++++++++++
ecbff1
 5 files changed, 417 insertions(+), 76 deletions(-)
ecbff1
ecbff1
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
ecbff1
index 0c0fa0f50..a10e1903a 100644
ecbff1
--- a/src/core/load-fragment.c
ecbff1
+++ b/src/core/load-fragment.c
ecbff1
@@ -884,50 +884,29 @@ int config_parse_exec_cpu_affinity(const char *unit,
ecbff1
                                    void *userdata) {
ecbff1
 
ecbff1
         ExecContext *c = data;
ecbff1
-        const char *word, *state;
ecbff1
-        size_t l;
ecbff1
+        _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
ecbff1
+        int ncpus;
ecbff1
 
ecbff1
         assert(filename);
ecbff1
         assert(lvalue);
ecbff1
         assert(rvalue);
ecbff1
         assert(data);
ecbff1
 
ecbff1
-        if (isempty(rvalue)) {
ecbff1
-                /* An empty assignment resets the CPU list */
ecbff1
-                if (c->cpuset)
ecbff1
-                        CPU_FREE(c->cpuset);
ecbff1
-                c->cpuset = NULL;
ecbff1
-                return 0;
ecbff1
-        }
ecbff1
-
ecbff1
-        FOREACH_WORD_QUOTED(word, l, rvalue, state) {
ecbff1
-                _cleanup_free_ char *t = NULL;
ecbff1
-                int r;
ecbff1
-                unsigned cpu;
ecbff1
-
ecbff1
-                t = strndup(word, l);
ecbff1
-                if (!t)
ecbff1
-                        return log_oom();
ecbff1
-
ecbff1
-                r = safe_atou(t, &cpu);
ecbff1
+        ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue);
ecbff1
+        if (ncpus < 0)
ecbff1
+                return ncpus;
ecbff1
 
ecbff1
-                if (!c->cpuset) {
ecbff1
-                        c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
ecbff1
-                        if (!c->cpuset)
ecbff1
-                                return log_oom();
ecbff1
-                }
ecbff1
+        if (c->cpuset)
ecbff1
+                CPU_FREE(c->cpuset);
ecbff1
 
ecbff1
-                if (r < 0 || cpu >= c->cpuset_ncpus) {
ecbff1
-                        log_syntax(unit, LOG_ERR, filename, line, ERANGE,
ecbff1
-                                   "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
ecbff1
-                        return 0;
ecbff1
-                }
ecbff1
-
ecbff1
-                CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
ecbff1
+        if (ncpus == 0)
ecbff1
+                /* An empty assignment resets the CPU list */
ecbff1
+                c->cpuset = NULL;
ecbff1
+        else {
ecbff1
+                c->cpuset = cpuset;
ecbff1
+                cpuset = NULL;
ecbff1
         }
ecbff1
-        if (!isempty(state))
ecbff1
-                log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
ecbff1
-                           "Trailing garbage, ignoring.");
ecbff1
+        c->cpuset_ncpus = ncpus;
ecbff1
 
ecbff1
         return 0;
ecbff1
 }
ecbff1
diff --git a/src/core/main.c b/src/core/main.c
ecbff1
index 66393ed6a..5554ef468 100644
ecbff1
--- a/src/core/main.c
ecbff1
+++ b/src/core/main.c
ecbff1
@@ -438,49 +438,15 @@ static int config_parse_cpu_affinity2(
ecbff1
                 void *data,
ecbff1
                 void *userdata) {
ecbff1
 
ecbff1
-        const char *word, *state;
ecbff1
-        size_t l;
ecbff1
-        cpu_set_t *c = NULL;
ecbff1
-        unsigned ncpus = 0;
ecbff1
-
ecbff1
-        assert(filename);
ecbff1
-        assert(lvalue);
ecbff1
-        assert(rvalue);
ecbff1
+        _cleanup_cpu_free_ cpu_set_t *c = NULL;
ecbff1
+        int ncpus;
ecbff1
 
ecbff1
-        FOREACH_WORD_QUOTED(word, l, rvalue, state) {
ecbff1
-                char *t;
ecbff1
-                int r;
ecbff1
-                unsigned cpu;
ecbff1
-
ecbff1
-                if (!(t = strndup(word, l)))
ecbff1
-                        return log_oom();
ecbff1
-
ecbff1
-                r = safe_atou(t, &cpu);
ecbff1
-                free(t);
ecbff1
-
ecbff1
-                if (!c)
ecbff1
-                        if (!(c = cpu_set_malloc(&ncpus)))
ecbff1
-                                return log_oom();
ecbff1
+        ncpus = parse_cpu_set_and_warn(rvalue, &c, unit, filename, line, lvalue);
ecbff1
+        if (ncpus < 0)
ecbff1
+                return ncpus;
ecbff1
 
ecbff1
-                if (r < 0 || cpu >= ncpus) {
ecbff1
-                        log_syntax(unit, LOG_ERR, filename, line, -r,
ecbff1
-                                   "Failed to parse CPU affinity '%s'", rvalue);
ecbff1
-                        CPU_FREE(c);
ecbff1
-                        return -EBADMSG;
ecbff1
-                }
ecbff1
-
ecbff1
-                CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c);
ecbff1
-        }
ecbff1
-        if (!isempty(state))
ecbff1
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
ecbff1
-                           "Trailing garbage, ignoring.");
ecbff1
-
ecbff1
-        if (c) {
ecbff1
-                if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
ecbff1
-                        log_unit_warning(unit, "Failed to set CPU affinity: %m");
ecbff1
-
ecbff1
-                CPU_FREE(c);
ecbff1
-        }
ecbff1
+        if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
ecbff1
+                log_warning_errno(errno, "Failed to set CPU affinity: %m");
ecbff1
 
ecbff1
         return 0;
ecbff1
 }
ecbff1
diff --git a/src/shared/util.c b/src/shared/util.c
ecbff1
index bbb457759..39359fcc8 100644
ecbff1
--- a/src/shared/util.c
ecbff1
+++ b/src/shared/util.c
ecbff1
@@ -2727,6 +2727,43 @@ int parse_size(const char *t, off_t base, off_t *size) {
ecbff1
         return 0;
ecbff1
 }
ecbff1
 
ecbff1
+int parse_range(const char *t, unsigned *lower, unsigned *upper) {
ecbff1
+        _cleanup_free_ char *word = NULL;
ecbff1
+        unsigned l, u;
ecbff1
+        int r;
ecbff1
+
ecbff1
+        assert(lower);
ecbff1
+        assert(upper);
ecbff1
+
ecbff1
+        /* Extract the lower bound. */
ecbff1
+        r = extract_first_word(&t, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS);
ecbff1
+        if (r < 0)
ecbff1
+                return r;
ecbff1
+        if (r == 0)
ecbff1
+                return -EINVAL;
ecbff1
+
ecbff1
+        r = safe_atou(word, &l);
ecbff1
+        if (r < 0)
ecbff1
+                return r;
ecbff1
+
ecbff1
+        /* Check for the upper bound and extract it if needed */
ecbff1
+        if (!t)
ecbff1
+                /* Single number with no dashes. */
ecbff1
+                u = l;
ecbff1
+        else if (!*t)
ecbff1
+                /* Trailing dash is an error. */
ecbff1
+                return -EINVAL;
ecbff1
+        else {
ecbff1
+                r = safe_atou(t, &u);
ecbff1
+                if (r < 0)
ecbff1
+                        return r;
ecbff1
+        }
ecbff1
+
ecbff1
+        *lower = l;
ecbff1
+        *upper = u;
ecbff1
+        return 0;
ecbff1
+}
ecbff1
+
ecbff1
 int make_stdio(int fd) {
ecbff1
         int r, s, t;
ecbff1
 
ecbff1
@@ -3460,6 +3497,60 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
ecbff1
         }
ecbff1
 }
ecbff1
 
ecbff1
+int parse_cpu_set_and_warn(
ecbff1
+                const char *rvalue,
ecbff1
+                cpu_set_t **cpu_set,
ecbff1
+                const char *unit,
ecbff1
+                const char *filename,
ecbff1
+                unsigned line,
ecbff1
+                const char *lvalue) {
ecbff1
+
ecbff1
+        const char *whole_rvalue = rvalue;
ecbff1
+        _cleanup_cpu_free_ cpu_set_t *c = NULL;
ecbff1
+        unsigned ncpus = 0;
ecbff1
+
ecbff1
+        assert(lvalue);
ecbff1
+        assert(rvalue);
ecbff1
+
ecbff1
+        for (;;) {
ecbff1
+                _cleanup_free_ char *word = NULL;
ecbff1
+                unsigned cpu, cpu_lower, cpu_upper;
ecbff1
+                int r;
ecbff1
+
ecbff1
+                r = extract_first_word(&rvalue, &word, WHITESPACE ",", EXTRACT_QUOTES);
ecbff1
+                if (r < 0)
ecbff1
+                        return log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
ecbff1
+                if (r == 0)
ecbff1
+                        break;
ecbff1
+
ecbff1
+                if (!c) {
ecbff1
+                        c = cpu_set_malloc(&ncpus);
ecbff1
+                        if (!c)
ecbff1
+                                return log_oom();
ecbff1
+                }
ecbff1
+
ecbff1
+                r = parse_range(word, &cpu_lower, &cpu_upper);
ecbff1
+                if (r < 0)
ecbff1
+                        return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU affinity '%s'", word);
ecbff1
+                if (cpu_lower >= ncpus || cpu_upper >= ncpus)
ecbff1
+                        return log_syntax(unit, LOG_ERR, filename, line, EINVAL, "CPU out of range '%s' ncpus is %u", word, ncpus);
ecbff1
+
ecbff1
+                if (cpu_lower > cpu_upper)
ecbff1
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Range '%s' is invalid, %u > %u", word, cpu_lower, cpu_upper);
ecbff1
+                else
ecbff1
+                        for (cpu = cpu_lower; cpu <= cpu_upper; cpu++)
ecbff1
+                                CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c);
ecbff1
+        }
ecbff1
+
ecbff1
+        /* On success, sets *cpu_set and returns ncpus for the system. */
ecbff1
+        if (c) {
ecbff1
+                *cpu_set = c;
ecbff1
+                c = NULL;
ecbff1
+        }
ecbff1
+
ecbff1
+        return (int) ncpus;
ecbff1
+}
ecbff1
+
ecbff1
 int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) {
ecbff1
         static const char status_indent[] = "         "; /* "[" STATUS "] " */
ecbff1
         _cleanup_free_ char *s = NULL;
ecbff1
diff --git a/src/shared/util.h b/src/shared/util.h
ecbff1
index 80ad18c0a..526a6fe84 100644
ecbff1
--- a/src/shared/util.h
ecbff1
+++ b/src/shared/util.h
ecbff1
@@ -136,6 +136,11 @@ bool streq_ptr(const char *a, const char *b) _pure_;
ecbff1
 
ecbff1
 #define malloc0(n) (calloc((n), 1))
ecbff1
 
ecbff1
+static inline void *mfree(void *memory) {
ecbff1
+        free(memory);
ecbff1
+        return NULL;
ecbff1
+}
ecbff1
+
ecbff1
 static inline const char* yes_no(bool b) {
ecbff1
         return b ? "yes" : "no";
ecbff1
 }
ecbff1
@@ -195,6 +200,7 @@ void safe_close_pair(int p[]);
ecbff1
 void close_many(const int fds[], unsigned n_fd);
ecbff1
 
ecbff1
 int parse_size(const char *t, off_t base, off_t *size);
ecbff1
+int parse_range(const char *t, unsigned *lower, unsigned *upper);
ecbff1
 
ecbff1
 int parse_boolean(const char *v) _pure_;
ecbff1
 int parse_pid(const char *s, pid_t* ret_pid);
ecbff1
@@ -474,6 +480,7 @@ int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool hon
ecbff1
 int pipe_eof(int fd);
ecbff1
 
ecbff1
 cpu_set_t* cpu_set_malloc(unsigned *ncpus);
ecbff1
+int parse_cpu_set_and_warn(const char *rvalue, cpu_set_t **cpu_set, const char *unit, const char *filename, unsigned line, const char *lvalue);
ecbff1
 
ecbff1
 int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) _printf_(4,0);
ecbff1
 int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) _printf_(4,5);
ecbff1
@@ -692,6 +699,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, fclose);
ecbff1
 DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose);
ecbff1
 DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
ecbff1
 DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, endmntent);
ecbff1
+DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE);
ecbff1
 
ecbff1
 #define _cleanup_free_ _cleanup_(freep)
ecbff1
 #define _cleanup_close_ _cleanup_(closep)
ecbff1
@@ -702,6 +710,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, endmntent);
ecbff1
 #define _cleanup_closedir_ _cleanup_(closedirp)
ecbff1
 #define _cleanup_endmntent_ _cleanup_(endmntentp)
ecbff1
 #define _cleanup_close_pair_ _cleanup_(close_pairp)
ecbff1
+#define _cleanup_cpu_free_ _cleanup_(CPU_FREEp)
ecbff1
 
ecbff1
 _malloc_  _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) {
ecbff1
         if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
ecbff1
diff --git a/src/test/test-util.c b/src/test/test-util.c
ecbff1
index 971f97d7c..fcf5416c0 100644
ecbff1
--- a/src/test/test-util.c
ecbff1
+++ b/src/test/test-util.c
ecbff1
@@ -689,6 +689,300 @@ static void test_parse_size(void) {
ecbff1
         assert_se(parse_size("-10B 20K", 1024, &bytes) == -ERANGE);
ecbff1
 }
ecbff1
 
ecbff1
+static void test_parse_range(void) {
ecbff1
+        unsigned lower, upper;
ecbff1
+
ecbff1
+        /* Successful cases */
ecbff1
+        assert_se(parse_range("111", &lower, &upper) == 0);
ecbff1
+        assert_se(lower == 111);
ecbff1
+        assert_se(upper == 111);
ecbff1
+
ecbff1
+        assert_se(parse_range("111-123", &lower, &upper) == 0);
ecbff1
+        assert_se(lower == 111);
ecbff1
+        assert_se(upper == 123);
ecbff1
+
ecbff1
+        assert_se(parse_range("123-111", &lower, &upper) == 0);
ecbff1
+        assert_se(lower == 123);
ecbff1
+        assert_se(upper == 111);
ecbff1
+
ecbff1
+        assert_se(parse_range("123-123", &lower, &upper) == 0);
ecbff1
+        assert_se(lower == 123);
ecbff1
+        assert_se(upper == 123);
ecbff1
+
ecbff1
+        assert_se(parse_range("0", &lower, &upper) == 0);
ecbff1
+        assert_se(lower == 0);
ecbff1
+        assert_se(upper == 0);
ecbff1
+
ecbff1
+        assert_se(parse_range("0-15", &lower, &upper) == 0);
ecbff1
+        assert_se(lower == 0);
ecbff1
+        assert_se(upper == 15);
ecbff1
+
ecbff1
+        assert_se(parse_range("15-0", &lower, &upper) == 0);
ecbff1
+        assert_se(lower == 15);
ecbff1
+        assert_se(upper == 0);
ecbff1
+
ecbff1
+        assert_se(parse_range("128-65535", &lower, &upper) == 0);
ecbff1
+        assert_se(lower == 128);
ecbff1
+        assert_se(upper == 65535);
ecbff1
+
ecbff1
+        assert_se(parse_range("1024-4294967295", &lower, &upper) == 0);
ecbff1
+        assert_se(lower == 1024);
ecbff1
+        assert_se(upper == 4294967295);
ecbff1
+
ecbff1
+        /* Leading whitespace is acceptable */
ecbff1
+        assert_se(parse_range(" 111", &lower, &upper) == 0);
ecbff1
+        assert_se(lower == 111);
ecbff1
+        assert_se(upper == 111);
ecbff1
+
ecbff1
+        assert_se(parse_range(" 111-123", &lower, &upper) == 0);
ecbff1
+        assert_se(lower == 111);
ecbff1
+        assert_se(upper == 123);
ecbff1
+
ecbff1
+        assert_se(parse_range("111- 123", &lower, &upper) == 0);
ecbff1
+        assert_se(lower == 111);
ecbff1
+        assert_se(upper == 123);
ecbff1
+
ecbff1
+        assert_se(parse_range("\t111-\t123", &lower, &upper) == 0);
ecbff1
+        assert_se(lower == 111);
ecbff1
+        assert_se(upper == 123);
ecbff1
+
ecbff1
+        assert_se(parse_range(" \t 111- \t 123", &lower, &upper) == 0);
ecbff1
+        assert_se(lower == 111);
ecbff1
+        assert_se(upper == 123);
ecbff1
+
ecbff1
+        /* Error cases, make sure they fail as expected */
ecbff1
+        lower = upper = 9999;
ecbff1
+        assert_se(parse_range("111garbage", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("garbage111", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("garbage", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111-123garbage", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111garbage-123", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        /* Empty string */
ecbff1
+        lower = upper = 9999;
ecbff1
+        assert_se(parse_range("", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        /* 111--123 will pass -123 to safe_atou which returns -ERANGE for negative */
ecbff1
+        assert_se(parse_range("111--123", &lower, &upper) == -ERANGE);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("-111-123", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111-123-", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111.4-123", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111-123.4", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111,4-123", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111-123,4", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        /* Error on trailing dash */
ecbff1
+        assert_se(parse_range("111-", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111-123-", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111--", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111- ", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        /* Whitespace is not a separator */
ecbff1
+        assert_se(parse_range("111 123", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111\t123", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111 \t 123", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        /* Trailing whitespace is invalid (from safe_atou) */
ecbff1
+        assert_se(parse_range("111 ", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111-123 ", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111 -123", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111 -123 ", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111\t-123\t", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        assert_se(parse_range("111 \t -123 \t ", &lower, &upper) == -EINVAL);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+
ecbff1
+        /* Out of the "unsigned" range, this is 1<<64 */
ecbff1
+        assert_se(parse_range("0-18446744073709551616", &lower, &upper) == -ERANGE);
ecbff1
+        assert_se(lower == 9999);
ecbff1
+        assert_se(upper == 9999);
ecbff1
+}
ecbff1
+
ecbff1
+static void test_parse_cpu_set(void) {
ecbff1
+        cpu_set_t *c = NULL;
ecbff1
+        int ncpus;
ecbff1
+        int cpu;
ecbff1
+
ecbff1
+        /* Simple range (from CPUAffinity example) */
ecbff1
+        ncpus = parse_cpu_set_and_warn("1 2", &c, NULL, "fake", 1, "CPUAffinity");
ecbff1
+        assert_se(ncpus >= 1024);
ecbff1
+        assert_se(CPU_ISSET_S(1, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        assert_se(CPU_ISSET_S(2, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 2);
ecbff1
+        c = mfree(c);
ecbff1
+
ecbff1
+        /* A more interesting range */
ecbff1
+        ncpus = parse_cpu_set_and_warn("0 1 2 3 8 9 10 11", &c, NULL, "fake", 1, "CPUAffinity");
ecbff1
+        assert_se(ncpus >= 1024);
ecbff1
+        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
ecbff1
+        for (cpu = 0; cpu < 4; cpu++)
ecbff1
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        for (cpu = 8; cpu < 12; cpu++)
ecbff1
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        c = mfree(c);
ecbff1
+
ecbff1
+        /* Quoted strings */
ecbff1
+        ncpus = parse_cpu_set_and_warn("8 '9' 10 \"11\"", &c, NULL, "fake", 1, "CPUAffinity");
ecbff1
+        assert_se(ncpus >= 1024);
ecbff1
+        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 4);
ecbff1
+        for (cpu = 8; cpu < 12; cpu++)
ecbff1
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        c = mfree(c);
ecbff1
+
ecbff1
+        /* Use commas as separators */
ecbff1
+        ncpus = parse_cpu_set_and_warn("0,1,2,3 8,9,10,11", &c, NULL, "fake", 1, "CPUAffinity");
ecbff1
+        assert_se(ncpus >= 1024);
ecbff1
+        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
ecbff1
+        for (cpu = 0; cpu < 4; cpu++)
ecbff1
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        for (cpu = 8; cpu < 12; cpu++)
ecbff1
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        c = mfree(c);
ecbff1
+
ecbff1
+        /* Commas with spaces (and trailing comma, space) */
ecbff1
+        ncpus = parse_cpu_set_and_warn("0, 1, 2, 3, 4, 5, 6, 7, ", &c, NULL, "fake", 1, "CPUAffinity");
ecbff1
+        assert_se(ncpus >= 1024);
ecbff1
+        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
ecbff1
+        for (cpu = 0; cpu < 8; cpu++)
ecbff1
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        c = mfree(c);
ecbff1
+
ecbff1
+        /* Ranges */
ecbff1
+        ncpus = parse_cpu_set_and_warn("0-3,8-11", &c, NULL, "fake", 1, "CPUAffinity");
ecbff1
+        assert_se(ncpus >= 1024);
ecbff1
+        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
ecbff1
+        for (cpu = 0; cpu < 4; cpu++)
ecbff1
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        for (cpu = 8; cpu < 12; cpu++)
ecbff1
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        c = mfree(c);
ecbff1
+
ecbff1
+        /* Ranges with trailing comma, space */
ecbff1
+        ncpus = parse_cpu_set_and_warn("0-3  8-11, ", &c, NULL, "fake", 1, "CPUAffinity");
ecbff1
+        assert_se(ncpus >= 1024);
ecbff1
+        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
ecbff1
+        for (cpu = 0; cpu < 4; cpu++)
ecbff1
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        for (cpu = 8; cpu < 12; cpu++)
ecbff1
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        c = mfree(c);
ecbff1
+
ecbff1
+        /* Negative range (returns empty cpu_set) */
ecbff1
+        ncpus = parse_cpu_set_and_warn("3-0", &c, NULL, "fake", 1, "CPUAffinity");
ecbff1
+        assert_se(ncpus >= 1024);
ecbff1
+        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 0);
ecbff1
+        c = mfree(c);
ecbff1
+
ecbff1
+        /* Overlapping ranges */
ecbff1
+        ncpus = parse_cpu_set_and_warn("0-7 4-11", &c, NULL, "fake", 1, "CPUAffinity");
ecbff1
+        assert_se(ncpus >= 1024);
ecbff1
+        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 12);
ecbff1
+        for (cpu = 0; cpu < 12; cpu++)
ecbff1
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        c = mfree(c);
ecbff1
+
ecbff1
+        /* Mix ranges and individual CPUs */
ecbff1
+        ncpus = parse_cpu_set_and_warn("0,1 4-11", &c, NULL, "fake", 1, "CPUAffinity");
ecbff1
+        assert_se(ncpus >= 1024);
ecbff1
+        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 10);
ecbff1
+        assert_se(CPU_ISSET_S(0, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        assert_se(CPU_ISSET_S(1, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        for (cpu = 4; cpu < 12; cpu++)
ecbff1
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ecbff1
+        c = mfree(c);
ecbff1
+
ecbff1
+        /* Garbage */
ecbff1
+        ncpus = parse_cpu_set_and_warn("0 1 2 3 garbage", &c, NULL, "fake", 1, "CPUAffinity");
ecbff1
+        assert_se(ncpus < 0);
ecbff1
+        assert_se(!c);
ecbff1
+
ecbff1
+        /* Range with garbage */
ecbff1
+        ncpus = parse_cpu_set_and_warn("0-3 8-garbage", &c, NULL, "fake", 1, "CPUAffinity");
ecbff1
+        assert_se(ncpus < 0);
ecbff1
+        assert_se(!c);
ecbff1
+
ecbff1
+        /* Empty string */
ecbff1
+        c = NULL;
ecbff1
+        ncpus = parse_cpu_set_and_warn("", &c, NULL, "fake", 1, "CPUAffinity");
ecbff1
+        assert_se(ncpus == 0);  /* empty string returns 0 */
ecbff1
+        assert_se(!c);
ecbff1
+
ecbff1
+        /* Runaway quoted string */
ecbff1
+        ncpus = parse_cpu_set_and_warn("0 1 2 3 \"4 5 6 7 ", &c, NULL, "fake", 1, "CPUAffinity");
ecbff1
+        assert_se(ncpus < 0);
ecbff1
+        assert_se(!c);
ecbff1
+}
ecbff1
+
ecbff1
 static void test_config_parse_iec_off(void) {
ecbff1
         off_t offset = 0;
ecbff1
         assert_se(config_parse_iec_off(NULL, "/this/file", 11, "Section", 22, "Size", 0, "4M", &offset, NULL) == 0);
ecbff1
@@ -1605,6 +1899,8 @@ int main(int argc, char *argv[]) {
ecbff1
         test_get_process_comm();
ecbff1
         test_protect_errno();
ecbff1
         test_parse_size();
ecbff1
+        test_parse_range();
ecbff1
+        test_parse_cpu_set();
ecbff1
         test_config_parse_iec_off();
ecbff1
         test_strextend();
ecbff1
         test_strrep();