Skip to content

Commit

Permalink
core: support <soft:hard> ranges for RLIMIT options
Browse files Browse the repository at this point in the history
The new parser supports:

 <value>       - specify both limits to the same value
 <soft:hard>   - specify both limits

the size or time specific suffixes are supported, for example

  LimitRTTIME=1sec
  LimitAS=4G:16G

The patch introduces parse_rlimit_range() and rlim type (size, sec,
usec, etc.) specific parsers. No code is duplicated now.

References: systemd#1769
  • Loading branch information
karelzak committed Nov 23, 2015
1 parent 6098bb0 commit a665d39
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 112 deletions.
229 changes: 117 additions & 112 deletions src/core/load-fragment.c
Original file line number Diff line number Diff line change
Expand Up @@ -1089,59 +1089,120 @@ int config_parse_bounding_set(
return 0;
}

int config_parse_limit(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {

struct rlimit **rl = data;
rlim_t v;
int r;

assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
static int rlim_parse_u64(const char *val, rlim_t *res) {
int r = 0;

rl += ltype;

if (streq(rvalue, "infinity"))
v = RLIM_INFINITY;
if (streq(val, "infinity"))
*res = RLIM_INFINITY;
else {
uint64_t u;

/* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
assert_cc(sizeof(rlim_t) == sizeof(uint64_t));

r = safe_atou64(rvalue, &u);
r = safe_atou64(val, &u);
if (r >= 0 && u >= (uint64_t) RLIM_INFINITY)
r = -ERANGE;
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
return 0;
}
if (r == 0)
*res = (rlim_t) u;
}
return r;
}

static int rlim_parse_size(const char *val, rlim_t *res) {
int r = 0;

if (streq(val, "infinity"))
*res = RLIM_INFINITY;
else {
uint64_t u;

r = parse_size(val, 1024, &u);
if (r >= 0 && u >= (uint64_t) RLIM_INFINITY)
r = -ERANGE;
if (r == 0)
*res = (rlim_t) u;
}
return r;
}

static int rlim_parse_sec(const char *val, rlim_t *res) {
int r = 0;

if (streq(val, "infinity"))
*res = RLIM_INFINITY;
else {
usec_t t;

r = parse_sec(val, &t);
if (r < 0)
return r;
if (t == USEC_INFINITY)
*res = RLIM_INFINITY;
else
*res = (rlim_t) (DIV_ROUND_UP(t, USEC_PER_SEC));

}
return r;
}

static int rlim_parse_usec(const char *val, rlim_t *res) {
int r = 0;

if (streq(val, "infinity"))
*res = RLIM_INFINITY;
else {
usec_t t;

r = parse_time(val, &t, 1);
if (r < 0)
return r;
if (t == USEC_INFINITY)
*res = RLIM_INFINITY;
else
*res = (rlim_t) t;
}
return r;
}

v = (rlim_t) u;
static int parse_rlimit_range(
const char *unit,
const char *filename,
unsigned line,
const char *value,
struct rlimit **rl,
int (*rlim_parser)(const char *, rlim_t *)) {

rlim_t soft, hard;
_cleanup_free_ char *sword = NULL, *hword = NULL;
int nwords, r;

assert(value);

/* <value> or <soft:hard> */
nwords = extract_many_words(&value, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &sword, &hword, NULL);
r = nwords < 0 ? nwords : nwords == 0 ? -EINVAL : 0;

if (r == 0)
r = rlim_parser(sword, &soft);
if (r == 0 && nwords == 2)
r = rlim_parser(hword, &hard);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", value);
return 0;
}

if (!*rl) {
*rl = new(struct rlimit, 1);
if (!*rl)
return log_oom();
}

(*rl)->rlim_cur = (*rl)->rlim_max = v;
(*rl)->rlim_cur = soft;
(*rl)->rlim_max = nwords == 2 ? hard : soft;
return 0;
}

int config_parse_bytes_limit(
int config_parse_limit(
const char *unit,
const char *filename,
unsigned line,
Expand All @@ -1154,40 +1215,37 @@ int config_parse_bytes_limit(
void *userdata) {

struct rlimit **rl = data;
rlim_t bytes;
int r;

assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);

rl += ltype;
return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_u64);
}

if (streq(rvalue, "infinity"))
bytes = RLIM_INFINITY;
else {
uint64_t u;

r = parse_size(rvalue, 1024, &u);
if (r >= 0 && u >= (uint64_t) RLIM_INFINITY)
r = -ERANGE;
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
return 0;
}
int config_parse_bytes_limit(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {

bytes = (rlim_t) u;
}
struct rlimit **rl = data;

if (!*rl) {
*rl = new(struct rlimit, 1);
if (!*rl)
return log_oom();
}
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);

(*rl)->rlim_cur = (*rl)->rlim_max = bytes;
return 0;
rl += ltype;
return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_size);
}

int config_parse_sec_limit(
Expand All @@ -1203,44 +1261,16 @@ int config_parse_sec_limit(
void *userdata) {

struct rlimit **rl = data;
rlim_t seconds;
int r;

assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);

rl += ltype;

if (streq(rvalue, "infinity"))
seconds = RLIM_INFINITY;
else {
usec_t t;

r = parse_sec(rvalue, &t);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
return 0;
}

if (t == USEC_INFINITY)
seconds = RLIM_INFINITY;
else
seconds = (rlim_t) (DIV_ROUND_UP(t, USEC_PER_SEC));
}

if (!*rl) {
*rl = new(struct rlimit, 1);
if (!*rl)
return log_oom();
}

(*rl)->rlim_cur = (*rl)->rlim_max = seconds;
return 0;
return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_sec);
}


int config_parse_usec_limit(
const char *unit,
const char *filename,
Expand All @@ -1254,42 +1284,17 @@ int config_parse_usec_limit(
void *userdata) {

struct rlimit **rl = data;
rlim_t useconds;
int r;

assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);

rl += ltype;
return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_usec);
}

if (streq(rvalue, "infinity"))
useconds = RLIM_INFINITY;
else {
usec_t t;

r = parse_time(rvalue, &t, 1);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
return 0;
}

if (t == USEC_INFINITY)
useconds = RLIM_INFINITY;
else
useconds = (rlim_t) t;
}

if (!*rl) {
*rl = new(struct rlimit, 1);
if (!*rl)
return log_oom();
}

(*rl)->rlim_cur = (*rl)->rlim_max = useconds;
return 0;
}

#ifdef HAVE_SYSV_COMPAT
int config_parse_sysv_priority(const char *unit,
Expand Down
30 changes: 30 additions & 0 deletions src/test/test-unit-file.c
Original file line number Diff line number Diff line change
Expand Up @@ -680,11 +680,21 @@ static void test_config_parse_rlimit(void) {
assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 55);
assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);

assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "55:66", rl, NULL) >= 0);
assert_se(rl[RLIMIT_NOFILE]);
assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 55);
assert_se(rl[RLIMIT_NOFILE]->rlim_max == 66);

assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity", rl, NULL) >= 0);
assert_se(rl[RLIMIT_NOFILE]);
assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY);
assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);

assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity:infinity", rl, NULL) >= 0);
assert_se(rl[RLIMIT_NOFILE]);
assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY);
assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);

rl[RLIMIT_NOFILE] = mfree(rl[RLIMIT_NOFILE]);

assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "56", rl, NULL) >= 0);
Expand All @@ -697,6 +707,11 @@ static void test_config_parse_rlimit(void) {
assert_se(rl[RLIMIT_CPU]->rlim_cur == 57);
assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);

assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "40s:1m", rl, NULL) >= 0);
assert_se(rl[RLIMIT_CPU]);
assert_se(rl[RLIMIT_CPU]->rlim_cur == 40);
assert_se(rl[RLIMIT_CPU]->rlim_max == 60);

assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "infinity", rl, NULL) >= 0);
assert_se(rl[RLIMIT_CPU]);
assert_se(rl[RLIMIT_CPU]->rlim_cur == RLIM_INFINITY);
Expand All @@ -714,16 +729,31 @@ static void test_config_parse_rlimit(void) {
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);

assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58:60", rl, NULL) >= 0);
assert_se(rl[RLIMIT_RTTIME]);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58);
assert_se(rl[RLIMIT_RTTIME]->rlim_max == 60);

assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s", rl, NULL) >= 0);
assert_se(rl[RLIMIT_RTTIME]);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);

assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s:123s", rl, NULL) >= 0);
assert_se(rl[RLIMIT_RTTIME]);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC);
assert_se(rl[RLIMIT_RTTIME]->rlim_max == 123 * USEC_PER_SEC);

assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity", rl, NULL) >= 0);
assert_se(rl[RLIMIT_RTTIME]);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);

assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity:infinity", rl, NULL) >= 0);
assert_se(rl[RLIMIT_RTTIME]);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);

assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "2345ms", rl, NULL) >= 0);
assert_se(rl[RLIMIT_RTTIME]);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 2345 * USEC_PER_MSEC);
Expand Down

0 comments on commit a665d39

Please sign in to comment.