rlimit-util: rework rlimit_{from|to}_string() to work without "Limit" prefix

let's make the call more generic, so that we can also easily use it for
parsing "RLIMIT_xyz" style constants.
This commit is contained in:
Lennart Poettering 2018-05-03 18:45:39 +02:00
parent 0e960f9b5c
commit 6550c24c7f
7 changed files with 161 additions and 99 deletions

View file

@ -289,22 +289,38 @@ int rlimit_format(const struct rlimit *rl, char **ret) {
} }
static const char* const rlimit_table[_RLIMIT_MAX] = { static const char* const rlimit_table[_RLIMIT_MAX] = {
[RLIMIT_CPU] = "LimitCPU", [RLIMIT_AS] = "AS",
[RLIMIT_FSIZE] = "LimitFSIZE", [RLIMIT_CORE] = "CORE",
[RLIMIT_DATA] = "LimitDATA", [RLIMIT_CPU] = "CPU",
[RLIMIT_STACK] = "LimitSTACK", [RLIMIT_DATA] = "DATA",
[RLIMIT_CORE] = "LimitCORE", [RLIMIT_FSIZE] = "FSIZE",
[RLIMIT_RSS] = "LimitRSS", [RLIMIT_LOCKS] = "LOCKS",
[RLIMIT_NOFILE] = "LimitNOFILE", [RLIMIT_MEMLOCK] = "MEMLOCK",
[RLIMIT_AS] = "LimitAS", [RLIMIT_MSGQUEUE] = "MSGQUEUE",
[RLIMIT_NPROC] = "LimitNPROC", [RLIMIT_NICE] = "NICE",
[RLIMIT_MEMLOCK] = "LimitMEMLOCK", [RLIMIT_NOFILE] = "NOFILE",
[RLIMIT_LOCKS] = "LimitLOCKS", [RLIMIT_NPROC] = "NPROC",
[RLIMIT_SIGPENDING] = "LimitSIGPENDING", [RLIMIT_RSS] = "RSS",
[RLIMIT_MSGQUEUE] = "LimitMSGQUEUE", [RLIMIT_RTPRIO] = "RTPRIO",
[RLIMIT_NICE] = "LimitNICE", [RLIMIT_RTTIME] = "RTTIME",
[RLIMIT_RTPRIO] = "LimitRTPRIO", [RLIMIT_SIGPENDING] = "SIGPENDING",
[RLIMIT_RTTIME] = "LimitRTTIME" [RLIMIT_STACK] = "STACK",
}; };
DEFINE_STRING_TABLE_LOOKUP(rlimit, int); DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
int rlimit_from_string_harder(const char *s) {
const char *suffix;
/* The official prefix */
suffix = startswith(s, "RLIMIT_");
if (suffix)
return rlimit_from_string(suffix);
/* Our own unit file setting prefix */
suffix = startswith(s, "Limit");
if (suffix)
return rlimit_from_string(suffix);
return rlimit_from_string(s);
}

View file

@ -13,6 +13,7 @@
const char *rlimit_to_string(int i) _const_; const char *rlimit_to_string(int i) _const_;
int rlimit_from_string(const char *s) _pure_; int rlimit_from_string(const char *s) _pure_;
int rlimit_from_string_harder(const char *s) _pure_;
int setrlimit_closest(int resource, const struct rlimit *rlim); int setrlimit_closest(int resource, const struct rlimit *rlim);

View file

@ -1091,8 +1091,8 @@ int bus_exec_context_set_transient_property(
UnitWriteFlags flags, UnitWriteFlags flags,
sd_bus_error *error) { sd_bus_error *error) {
const char *soft = NULL; const char *suffix;
int r, ri; int r;
assert(u); assert(u);
assert(c); assert(c);
@ -2313,73 +2313,77 @@ int bus_exec_context_set_transient_property(
} }
return 1; return 1;
}
ri = rlimit_from_string(name); } else if ((suffix = startswith(name, "Limit"))) {
if (ri < 0) { const char *soft = NULL;
soft = endswith(name, "Soft"); int ri;
if (soft) {
const char *n;
n = strndupa(name, soft - name); ri = rlimit_from_string(suffix);
ri = rlimit_from_string(n); if (ri < 0) {
if (ri >= 0) soft = endswith(suffix, "Soft");
name = n; if (soft) {
const char *n;
} n = strndupa(suffix, soft - suffix);
} ri = rlimit_from_string(n);
if (ri >= 0)
if (ri >= 0) { name = strjoina("Limit", n);
uint64_t rl; }
rlim_t x;
r = sd_bus_message_read(message, "t", &rl);
if (r < 0)
return r;
if (rl == (uint64_t) -1)
x = RLIM_INFINITY;
else {
x = (rlim_t) rl;
if ((uint64_t) x != rl)
return -ERANGE;
} }
if (!UNIT_WRITE_FLAGS_NOOP(flags)) { if (ri >= 0) {
_cleanup_free_ char *f = NULL; uint64_t rl;
struct rlimit nl; rlim_t x;
if (c->rlimit[ri]) { r = sd_bus_message_read(message, "t", &rl);
nl = *c->rlimit[ri];
if (soft)
nl.rlim_cur = x;
else
nl.rlim_max = x;
} else
/* When the resource limit is not initialized yet, then assign the value to both fields */
nl = (struct rlimit) {
.rlim_cur = x,
.rlim_max = x,
};
r = rlimit_format(&nl, &f);
if (r < 0) if (r < 0)
return r; return r;
if (c->rlimit[ri]) if (rl == (uint64_t) -1)
*c->rlimit[ri] = nl; x = RLIM_INFINITY;
else { else {
c->rlimit[ri] = newdup(struct rlimit, &nl, 1); x = (rlim_t) rl;
if (!c->rlimit[ri])
return -ENOMEM; if ((uint64_t) x != rl)
return -ERANGE;
} }
unit_write_settingf(u, flags, name, "%s=%s", name, f); if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
_cleanup_free_ char *f = NULL;
struct rlimit nl;
if (c->rlimit[ri]) {
nl = *c->rlimit[ri];
if (soft)
nl.rlim_cur = x;
else
nl.rlim_max = x;
} else
/* When the resource limit is not initialized yet, then assign the value to both fields */
nl = (struct rlimit) {
.rlim_cur = x,
.rlim_max = x,
};
r = rlimit_format(&nl, &f);
if (r < 0)
return r;
if (c->rlimit[ri])
*c->rlimit[ri] = nl;
else {
c->rlimit[ri] = newdup(struct rlimit, &nl, 1);
if (!c->rlimit[ri])
return -ENOMEM;
}
unit_write_settingf(u, flags, name, "%s=%s", name, f);
}
return 1;
} }
return 1;
} }
return 0; return 0;

View file

@ -3176,7 +3176,7 @@ static int exec_child(
r = setrlimit_closest(i, context->rlimit[i]); r = setrlimit_closest(i, context->rlimit[i]);
if (r < 0) { if (r < 0) {
*exit_status = EXIT_LIMITS; *exit_status = EXIT_LIMITS;
return log_unit_error_errno(unit, r, "Failed to adjust resource limit %s: %m", rlimit_to_string(i)); return log_unit_error_errno(unit, r, "Failed to adjust resource limit RLIMIT_%s: %m", rlimit_to_string(i));
} }
} }
@ -3975,9 +3975,9 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
for (i = 0; i < RLIM_NLIMITS; i++) for (i = 0; i < RLIM_NLIMITS; i++)
if (c->rlimit[i]) { if (c->rlimit[i]) {
fprintf(f, "%s%s: " RLIM_FMT "\n", fprintf(f, "Limit%s%s: " RLIM_FMT "\n",
prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max); prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
fprintf(f, "%s%sSoft: " RLIM_FMT "\n", fprintf(f, "Limit%s%sSoft: " RLIM_FMT "\n",
prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur); prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
} }

View file

@ -684,7 +684,8 @@ static int bus_append_automount_property(sd_bus_message *m, const char *field, c
} }
static int bus_append_execute_property(sd_bus_message *m, const char *field, const char *eq) { static int bus_append_execute_property(sd_bus_message *m, const char *field, const char *eq) {
int r, rl; const char *suffix;
int r;
if (STR_IN_SET(field, if (STR_IN_SET(field,
"User", "Group", "User", "Group",
@ -863,25 +864,29 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
return bus_append_byte_array(m, field, decoded, sz); return bus_append_byte_array(m, field, decoded, sz);
} }
rl = rlimit_from_string(field); if ((suffix = startswith(field, "Limit"))) {
if (rl >= 0) { int rl;
const char *sn;
struct rlimit l;
r = rlimit_parse(rl, eq, &l); rl = rlimit_from_string(suffix);
if (r < 0) if (rl >= 0) {
return log_error_errno(r, "Failed to parse resource limit: %s", eq); const char *sn;
struct rlimit l;
r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max); r = rlimit_parse(rl, eq, &l);
if (r < 0) if (r < 0)
return bus_log_create_error(r); return log_error_errno(r, "Failed to parse resource limit: %s", eq);
sn = strjoina(field, "Soft"); r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max);
r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur); if (r < 0)
if (r < 0) return bus_log_create_error(r);
return bus_log_create_error(r);
return 1; sn = strjoina(field, "Soft");
r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
} }
if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) { if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) {

View file

@ -1613,36 +1613,40 @@ int bus_property_get_rlimit(
void *userdata, void *userdata,
sd_bus_error *error) { sd_bus_error *error) {
const char *is_soft;
struct rlimit *rl; struct rlimit *rl;
uint64_t u; uint64_t u;
rlim_t x; rlim_t x;
const char *is_soft;
assert(bus); assert(bus);
assert(reply); assert(reply);
assert(userdata); assert(userdata);
is_soft = endswith(property, "Soft"); is_soft = endswith(property, "Soft");
rl = *(struct rlimit**) userdata; rl = *(struct rlimit**) userdata;
if (rl) if (rl)
x = is_soft ? rl->rlim_cur : rl->rlim_max; x = is_soft ? rl->rlim_cur : rl->rlim_max;
else { else {
struct rlimit buf = {}; struct rlimit buf = {};
const char *s, *p;
int z; int z;
const char *s;
/* Chop off "Soft" suffix */
s = is_soft ? strndupa(property, is_soft - property) : property; s = is_soft ? strndupa(property, is_soft - property) : property;
z = rlimit_from_string(strstr(s, "Limit")); /* Skip over any prefix, such as "Default" */
assert_se(p = strstr(s, "Limit"));
z = rlimit_from_string(p + 5);
assert(z >= 0); assert(z >= 0);
getrlimit(z, &buf); (void) getrlimit(z, &buf);
x = is_soft ? buf.rlim_cur : buf.rlim_max; x = is_soft ? buf.rlim_cur : buf.rlim_max;
} }
/* rlim_t might have different sizes, let's map /* rlim_t might have different sizes, let's map RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on all
* RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on * archs */
* all archs */
u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x; u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x;
return sd_bus_message_append(reply, "t", u); return sd_bus_message_append(reply, "t", u);

View file

@ -42,6 +42,7 @@ int main(int argc, char *argv[]) {
.rlim_cur = 10, .rlim_cur = 10,
.rlim_max = 5, .rlim_max = 5,
}; };
int i;
log_parse_environment(); log_parse_environment();
log_open(); log_open();
@ -53,10 +54,41 @@ int main(int argc, char *argv[]) {
new.rlim_max = old.rlim_max; new.rlim_max = old.rlim_max;
assert_se(setrlimit(RLIMIT_NOFILE, &new) >= 0); assert_se(setrlimit(RLIMIT_NOFILE, &new) >= 0);
assert_se(rlimit_from_string("LimitNOFILE") == RLIMIT_NOFILE); assert_se(rlimit_from_string("NOFILE") == RLIMIT_NOFILE);
assert_se(rlimit_from_string("LimitNOFILE") == -1);
assert_se(rlimit_from_string("RLIMIT_NOFILE") == -1);
assert_se(rlimit_from_string("xxxNOFILE") == -1);
assert_se(rlimit_from_string("DefaultLimitNOFILE") == -1); assert_se(rlimit_from_string("DefaultLimitNOFILE") == -1);
assert_se(streq_ptr(rlimit_to_string(RLIMIT_NOFILE), "LimitNOFILE")); assert_se(rlimit_from_string_harder("NOFILE") == RLIMIT_NOFILE);
assert_se(rlimit_from_string_harder("LimitNOFILE") == RLIMIT_NOFILE);
assert_se(rlimit_from_string_harder("RLIMIT_NOFILE") == RLIMIT_NOFILE);
assert_se(rlimit_from_string_harder("xxxNOFILE") == -1);
assert_se(rlimit_from_string_harder("DefaultLimitNOFILE") == -1);
for (i = 0; i < _RLIMIT_MAX; i++) {
_cleanup_free_ char *prefixed = NULL;
const char *p;
assert_se(p = rlimit_to_string(i));
log_info("%i = %s", i, p);
assert_se(rlimit_from_string(p) == i);
assert_se(rlimit_from_string_harder(p) == i);
assert_se(prefixed = strjoin("Limit", p));
assert_se(rlimit_from_string(prefixed) < 0);
assert_se(rlimit_from_string_harder(prefixed) == i);
prefixed = mfree(prefixed);
assert_se(prefixed = strjoin("RLIMIT_", p));
assert_se(rlimit_from_string(prefixed) < 0);
assert_se(rlimit_from_string_harder(prefixed) == i);
}
assert_se(streq_ptr(rlimit_to_string(RLIMIT_NOFILE), "NOFILE"));
assert_se(rlimit_to_string(-1) == NULL); assert_se(rlimit_to_string(-1) == NULL);
assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0); assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0);