ryantimwilson / rpms / systemd

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