cgroup-util: modernize cg_get_keyed_attribute()

- assert on supplied keys being unique
- Reject duplicate attributes with -EBADMSG
- Rename 'ret_values' to just 'values', given we don't
  allocate the array
- Remove now unused cg_get_keyed_attribute_graceful()
This commit is contained in:
Mike Yuan
2025-05-19 21:06:17 +02:00
parent 88f4bf8a8b
commit dbe592a067
3 changed files with 35 additions and 83 deletions

View File

@@ -1777,56 +1777,61 @@ int cg_get_owner(const char *path, uid_t *ret_uid) {
return 0;
}
int cg_get_keyed_attribute_full(
int cg_get_keyed_attribute(
const char *controller,
const char *path,
const char *attribute,
char **keys,
char **ret_values,
CGroupKeyMode mode) {
char * const *keys,
char **values) {
_cleanup_free_ char *filename = NULL, *contents = NULL;
const char *p;
size_t n, i, n_done = 0;
char **v;
size_t n;
int r;
assert(path);
assert(attribute);
/* Reads one or more fields of a cgroup v2 keyed attribute file. The 'keys' parameter should be an strv with
* all keys to retrieve. The 'ret_values' parameter should be passed as string size with the same number of
* all keys to retrieve. The 'values' parameter should be passed as string size with the same number of
* entries as 'keys'. On success each entry will be set to the value of the matching key.
*
* If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. If mode
* is set to GG_KEY_MODE_GRACEFUL we ignore missing keys and return those that were parsed successfully. */
* If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. */
r = cg_get_path(controller, path, attribute, &filename);
if (r < 0)
return r;
r = read_full_file(filename, &contents, NULL);
r = read_full_file(filename, &contents, /* ret_size = */ NULL);
if (r < 0)
return r;
n = strv_length(keys);
if (n == 0) /* No keys to retrieve? That's easy, we are done then */
return 0;
assert(strv_is_uniq(keys));
/* Let's build this up in a temporary array for now in order not to clobber the return parameter on failure */
v = newa0(char*, n);
char **v = newa0(char*, n);
size_t n_done = 0;
for (p = contents; *p;) {
const char *w = NULL;
for (const char *p = contents; *p;) {
const char *w;
size_t i;
for (i = 0; i < n; i++)
if (!v[i]) {
w = first_word(p, keys[i]);
if (w)
break;
}
for (i = 0; i < n; i++) {
w = first_word(p, keys[i]);
if (w)
break;
}
if (w) {
size_t l;
if (v[i]) { /* duplicate entry? */
r = -EBADMSG;
goto fail;
}
size_t l = strcspn(w, NEWLINE);
l = strcspn(w, NEWLINE);
v[i] = strndup(w, l);
if (!v[i]) {
r = -ENOMEM;
@@ -1835,7 +1840,7 @@ int cg_get_keyed_attribute_full(
n_done++;
if (n_done >= n)
goto done;
break;
p = w + l;
} else
@@ -1844,21 +1849,17 @@ int cg_get_keyed_attribute_full(
p += strspn(p, NEWLINE);
}
if (mode & CG_KEY_MODE_GRACEFUL)
goto done;
if (n_done < n) {
r = -ENXIO;
goto fail;
}
r = -ENXIO;
memcpy(values, v, sizeof(char*) * n);
return 0;
fail:
free_many_charp(v, n);
return r;
done:
memcpy(ret_values, v, sizeof(char*) * n);
if (mode & CG_KEY_MODE_GRACEFUL)
return n_done;
return 0;
}
int cg_mask_to_string(CGroupMask mask, char **ret) {

View File

@@ -226,31 +226,9 @@ int cg_is_delegated_fd(int fd);
int cg_has_coredump_receive(const char *path);
typedef enum {
CG_KEY_MODE_GRACEFUL = 1 << 0,
} CGroupKeyMode;
int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value);
int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret);
int cg_get_keyed_attribute_full(const char *controller, const char *path, const char *attribute, char **keys, char **values, CGroupKeyMode mode);
static inline int cg_get_keyed_attribute(
const char *controller,
const char *path,
const char *attribute,
char **keys,
char **ret_values) {
return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, 0);
}
static inline int cg_get_keyed_attribute_graceful(
const char *controller,
const char *path,
const char *attribute,
char **keys,
char **ret_values) {
return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, CG_KEY_MODE_GRACEFUL);
}
int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, char * const *keys, char **values);
int cg_get_attribute_as_uint64(const char *controller, const char *path, const char *attribute, uint64_t *ret);

View File

@@ -426,49 +426,22 @@ TEST(cg_get_keyed_attribute) {
}
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("no_such_attr"), &val) == -ENXIO);
assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("no_such_attr"), &val) == 0);
ASSERT_NULL(val);
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec"), &val) == 0);
val = mfree(val);
assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec"), &val) == 1);
log_info("cpu /init.scope cpu.stat [usage_usec] → \"%s\"", val);
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "no_such_attr"), vals3) == -ENXIO);
assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "no_such_attr"), vals3) == 1);
assert_se(vals3[0] && !vals3[1]);
free(vals3[0]);
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "usage_usec"), vals3) == -ENXIO);
assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "usage_usec"), vals3) == 1);
assert_se(vals3[0] && !vals3[1]);
free(vals3[0]);
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat",
STRV_MAKE("usage_usec", "user_usec", "system_usec"), vals3) == 0);
for (size_t i = 0; i < 3; i++)
free(vals3[i]);
assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat",
STRV_MAKE("usage_usec", "user_usec", "system_usec"), vals3) == 3);
log_info("cpu /init.scope cpu.stat [usage_usec user_usec system_usec] → \"%s\", \"%s\", \"%s\"",
vals3[0], vals3[1], vals3[2]);
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat",
STRV_MAKE("system_usec", "user_usec", "usage_usec"), vals3a) == 0);
for (size_t i = 0; i < 3; i++)
free(vals3a[i]);
assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat",
STRV_MAKE("system_usec", "user_usec", "usage_usec"), vals3a) == 3);
log_info("cpu /init.scope cpu.stat [system_usec user_usec usage_usec] → \"%s\", \"%s\", \"%s\"",
vals3a[0], vals3a[1], vals3a[2]);
for (size_t i = 0; i < 3; i++) {
free(vals3[i]);
free(vals3a[i]);
}
}
TEST(bfq_weight_conversion) {