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] = {
[RLIMIT_CPU] = "LimitCPU",
[RLIMIT_FSIZE] = "LimitFSIZE",
[RLIMIT_DATA] = "LimitDATA",
[RLIMIT_STACK] = "LimitSTACK",
[RLIMIT_CORE] = "LimitCORE",
[RLIMIT_RSS] = "LimitRSS",
[RLIMIT_NOFILE] = "LimitNOFILE",
[RLIMIT_AS] = "LimitAS",
[RLIMIT_NPROC] = "LimitNPROC",
[RLIMIT_MEMLOCK] = "LimitMEMLOCK",
[RLIMIT_LOCKS] = "LimitLOCKS",
[RLIMIT_SIGPENDING] = "LimitSIGPENDING",
[RLIMIT_MSGQUEUE] = "LimitMSGQUEUE",
[RLIMIT_NICE] = "LimitNICE",
[RLIMIT_RTPRIO] = "LimitRTPRIO",
[RLIMIT_RTTIME] = "LimitRTTIME"
[RLIMIT_AS] = "AS",
[RLIMIT_CORE] = "CORE",
[RLIMIT_CPU] = "CPU",
[RLIMIT_DATA] = "DATA",
[RLIMIT_FSIZE] = "FSIZE",
[RLIMIT_LOCKS] = "LOCKS",
[RLIMIT_MEMLOCK] = "MEMLOCK",
[RLIMIT_MSGQUEUE] = "MSGQUEUE",
[RLIMIT_NICE] = "NICE",
[RLIMIT_NOFILE] = "NOFILE",
[RLIMIT_NPROC] = "NPROC",
[RLIMIT_RSS] = "RSS",
[RLIMIT_RTPRIO] = "RTPRIO",
[RLIMIT_RTTIME] = "RTTIME",
[RLIMIT_SIGPENDING] = "SIGPENDING",
[RLIMIT_STACK] = "STACK",
};
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_;
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);

View File

@ -1091,8 +1091,8 @@ int bus_exec_context_set_transient_property(
UnitWriteFlags flags,
sd_bus_error *error) {
const char *soft = NULL;
int r, ri;
const char *suffix;
int r;
assert(u);
assert(c);
@ -2313,73 +2313,77 @@ int bus_exec_context_set_transient_property(
}
return 1;
}
ri = rlimit_from_string(name);
if (ri < 0) {
soft = endswith(name, "Soft");
if (soft) {
const char *n;
} else if ((suffix = startswith(name, "Limit"))) {
const char *soft = NULL;
int ri;
n = strndupa(name, soft - name);
ri = rlimit_from_string(n);
if (ri >= 0)
name = n;
ri = rlimit_from_string(suffix);
if (ri < 0) {
soft = endswith(suffix, "Soft");
if (soft) {
const char *n;
}
}
if (ri >= 0) {
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;
n = strndupa(suffix, soft - suffix);
ri = rlimit_from_string(n);
if (ri >= 0)
name = strjoina("Limit", n);
}
}
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
_cleanup_free_ char *f = NULL;
struct rlimit nl;
if (ri >= 0) {
uint64_t rl;
rlim_t x;
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);
r = sd_bus_message_read(message, "t", &rl);
if (r < 0)
return r;
if (c->rlimit[ri])
*c->rlimit[ri] = nl;
if (rl == (uint64_t) -1)
x = RLIM_INFINITY;
else {
c->rlimit[ri] = newdup(struct rlimit, &nl, 1);
if (!c->rlimit[ri])
return -ENOMEM;
x = (rlim_t) rl;
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;

View File

@ -3176,7 +3176,7 @@ static int exec_child(
r = setrlimit_closest(i, context->rlimit[i]);
if (r < 0) {
*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++)
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);
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);
}

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) {
int r, rl;
const char *suffix;
int r;
if (STR_IN_SET(field,
"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);
}
rl = rlimit_from_string(field);
if (rl >= 0) {
const char *sn;
struct rlimit l;
if ((suffix = startswith(field, "Limit"))) {
int rl;
r = rlimit_parse(rl, eq, &l);
if (r < 0)
return log_error_errno(r, "Failed to parse resource limit: %s", eq);
rl = rlimit_from_string(suffix);
if (rl >= 0) {
const char *sn;
struct rlimit l;
r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max);
if (r < 0)
return bus_log_create_error(r);
r = rlimit_parse(rl, eq, &l);
if (r < 0)
return log_error_errno(r, "Failed to parse resource limit: %s", eq);
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);
r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max);
if (r < 0)
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")) {

View File

@ -1613,36 +1613,40 @@ int bus_property_get_rlimit(
void *userdata,
sd_bus_error *error) {
const char *is_soft;
struct rlimit *rl;
uint64_t u;
rlim_t x;
const char *is_soft;
assert(bus);
assert(reply);
assert(userdata);
is_soft = endswith(property, "Soft");
rl = *(struct rlimit**) userdata;
if (rl)
x = is_soft ? rl->rlim_cur : rl->rlim_max;
else {
struct rlimit buf = {};
const char *s, *p;
int z;
const char *s;
/* Chop off "Soft" suffix */
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);
getrlimit(z, &buf);
(void) getrlimit(z, &buf);
x = is_soft ? buf.rlim_cur : buf.rlim_max;
}
/* rlim_t might have different sizes, let's map
* RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on
* all archs */
/* rlim_t might have different sizes, let's map RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on all
* archs */
u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x;
return sd_bus_message_append(reply, "t", u);

View File

@ -42,6 +42,7 @@ int main(int argc, char *argv[]) {
.rlim_cur = 10,
.rlim_max = 5,
};
int i;
log_parse_environment();
log_open();
@ -53,10 +54,41 @@ int main(int argc, char *argv[]) {
new.rlim_max = old.rlim_max;
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(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(getrlimit(RLIMIT_NOFILE, &old) == 0);