Add quota support for DBus

This commit is contained in:
Andres Beltran
2025-07-01 17:40:47 +00:00
parent 26c6f3271a
commit a89afe1948
7 changed files with 243 additions and 2 deletions

View File

@@ -31,7 +31,9 @@
#include "namespace.h"
#include "nsflags.h"
#include "ordered-set.h"
#include "parse-util.h"
#include "path-util.h"
#include "percent-util.h"
#include "pcre2-util.h"
#include "process-util.h"
#include "rlimit-util.h"
@@ -1000,6 +1002,22 @@ static int property_get_exec_dir_symlink(
return sd_bus_message_close_container(reply);
}
static int property_get_exec_quota(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
QuotaLimit *q = ASSERT_PTR(userdata);
assert(bus);
assert(reply);
return sd_bus_message_append(reply, "(tus)", q->quota_absolute, q->quota_scale, yes_no(q->quota_enforce));
}
static int property_get_image_policy(
sd_bus *bus,
const char *path,
@@ -1266,12 +1284,18 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("RuntimeDirectory", "as", property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_RUNTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StateDirectorySymlink", "a(sst)", property_get_exec_dir_symlink, offsetof(ExecContext, directories[EXEC_DIRECTORY_STATE]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StateDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_STATE].mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StateDirectoryAccounting", "b", bus_property_get_bool, offsetof(ExecContext, directories[EXEC_DIRECTORY_STATE].exec_quota.quota_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StateDirectoryQuota", "(tus)", property_get_exec_quota, offsetof(ExecContext, directories[EXEC_DIRECTORY_STATE].exec_quota), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StateDirectory", "as", property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_STATE]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CacheDirectorySymlink", "a(sst)", property_get_exec_dir_symlink, offsetof(ExecContext, directories[EXEC_DIRECTORY_CACHE]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CacheDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_CACHE].mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CacheDirectoryAccounting", "b", bus_property_get_bool, offsetof(ExecContext, directories[EXEC_DIRECTORY_CACHE].exec_quota.quota_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CacheDirectoryQuota", "(tus)", property_get_exec_quota, offsetof(ExecContext, directories[EXEC_DIRECTORY_CACHE].exec_quota), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CacheDirectory", "as", property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_CACHE]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogsDirectorySymlink", "a(sst)", property_get_exec_dir_symlink, offsetof(ExecContext, directories[EXEC_DIRECTORY_LOGS]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogsDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_LOGS].mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogsDirectoryAccounting", "b", bus_property_get_bool, offsetof(ExecContext, directories[EXEC_DIRECTORY_LOGS].exec_quota.quota_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogsDirectoryQuota", "(tus)", property_get_exec_quota, offsetof(ExecContext, directories[EXEC_DIRECTORY_LOGS].exec_quota), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogsDirectory", "as", property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_LOGS]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ConfigurationDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_CONFIGURATION].mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ConfigurationDirectory", "as", property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_CONFIGURATION]), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1309,6 +1333,60 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_VTABLE_END
};
static int property_get_quota_usage(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Unit *u = ASSERT_PTR(userdata);
ExecContext *c = ASSERT_PTR(unit_get_exec_context(u));
uint64_t current_usage_bytes = UINT64_MAX, limit_bytes = UINT64_MAX;
int r;
assert(bus);
assert(reply);
ExecDirectoryType dt;
if (streq(property, "StateDirectoryQuotaUsage"))
dt = EXEC_DIRECTORY_STATE;
else if (streq(property, "CacheDirectoryQuotaUsage"))
dt = EXEC_DIRECTORY_CACHE;
else if (streq(property, "LogsDirectoryQuotaUsage"))
dt = EXEC_DIRECTORY_LOGS;
else
assert_not_reached();
const QuotaLimit *q;
q = &c->directories[dt].exec_quota;
if (q->quota_enforce || q->quota_accounting) {
r = unit_get_exec_quota_stats(u, c, dt, &current_usage_bytes, &limit_bytes);
if (r < 0)
return r;
}
if (!q->quota_enforce)
limit_bytes = UINT64_MAX;
if (!q->quota_accounting)
current_usage_bytes = UINT64_MAX;
return sd_bus_message_append(reply, "(tt)", current_usage_bytes, limit_bytes);
}
const sd_bus_vtable bus_unit_exec_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("StateDirectoryQuotaUsage", "(tt)", property_get_quota_usage, 0, 0),
SD_BUS_PROPERTY("CacheDirectoryQuotaUsage", "(tt)", property_get_quota_usage, 0, 0),
SD_BUS_PROPERTY("LogsDirectoryQuotaUsage", "(tt)", property_get_quota_usage, 0, 0),
SD_BUS_VTABLE_END
};
static int append_exec_command(sd_bus_message *reply, ExecCommand *c) {
int r;
@@ -2210,6 +2288,15 @@ int bus_exec_context_set_transient_property(
if (streq(name, "RuntimeDirectoryMode"))
return bus_set_transient_mode_t(u, name, &c->directories[EXEC_DIRECTORY_RUNTIME].mode, message, flags, error);
if (streq(name, "StateDirectoryAccounting"))
return bus_set_transient_bool(u, name, &c->directories[EXEC_DIRECTORY_STATE].exec_quota.quota_accounting, message, flags, error);
if (streq(name, "CacheDirectoryAccounting"))
return bus_set_transient_bool(u, name, &c->directories[EXEC_DIRECTORY_CACHE].exec_quota.quota_accounting, message, flags, error);
if (streq(name, "LogsDirectoryAccounting"))
return bus_set_transient_bool(u, name, &c->directories[EXEC_DIRECTORY_LOGS].exec_quota.quota_accounting, message, flags, error);
if (streq(name, "StateDirectoryMode"))
return bus_set_transient_mode_t(u, name, &c->directories[EXEC_DIRECTORY_STATE].mode, message, flags, error);
@@ -3603,6 +3690,47 @@ int bus_exec_context_set_transient_property(
return 1;
} else if (STR_IN_SET(name, "StateDirectoryQuota", "CacheDirectoryQuota", "LogsDirectoryQuota")) {
uint64_t quota_absolute = UINT64_MAX;
uint32_t quota_scale = UINT32_MAX;
const char *enforce_flag;
int quota_enforce;
r = sd_bus_message_read(message, "(tus)", &quota_absolute, &quota_scale, &enforce_flag);
if (r < 0)
return r;
quota_enforce = parse_boolean(enforce_flag);
if (quota_enforce < 0)
return quota_enforce;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
ExecDirectoryType dt;
if (streq(name, "StateDirectoryQuota"))
dt = EXEC_DIRECTORY_STATE;
else if (streq(name, "CacheDirectoryQuota"))
dt = EXEC_DIRECTORY_CACHE;
else if (streq(name, "LogsDirectoryQuota"))
dt = EXEC_DIRECTORY_LOGS;
else
assert_not_reached();
if (quota_enforce) {
c->directories[dt].exec_quota.quota_absolute = quota_absolute;
c->directories[dt].exec_quota.quota_scale = quota_scale;
if (quota_absolute != UINT64_MAX)
unit_write_settingf(u, flags, name, "%s=%" PRIu64, name, quota_absolute);
else
unit_write_settingf(u, flags, name, "%s=%d%%", name, UINT32_SCALE_TO_PERCENT(quota_scale));
} else
unit_write_settingf(u, flags, name, "%s=", name);
c->directories[dt].exec_quota.quota_enforce = quota_enforce;
}
return 1;
} else if (STR_IN_SET(name, "AppArmorProfile", "SmackProcessLabel")) {
int ignore;
const char *s;

View File

@@ -24,6 +24,7 @@
SD_BUS_PROPERTY(name, "a(sasasttttuii)", bus_property_get_exec_ex_command_list, offset, flags)
extern const sd_bus_vtable bus_exec_vtable[];
extern const sd_bus_vtable bus_unit_exec_vtable[];
int bus_property_get_exec_output(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
int bus_property_get_exec_command(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);

View File

@@ -338,9 +338,8 @@ static int bus_cgroup_context_find(sd_bus *bus, const char *path, const char *in
return 1;
}
static int bus_exec_context_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
static int bus_unit_exec_context_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
Manager *m = ASSERT_PTR(userdata);
ExecContext *c;
Unit *u;
int r;
@@ -356,6 +355,27 @@ static int bus_exec_context_find(sd_bus *bus, const char *path, const char *inte
if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type)))
return 0;
if (!UNIT_HAS_EXEC_CONTEXT(u))
return 0;
*found = u;
return 1;
}
static int bus_exec_context_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
ExecContext *c;
int r;
assert(bus);
assert(path);
assert(interface);
assert(found);
Unit *u;
r = bus_unit_exec_context_find(bus, path, interface, userdata, (void**) &u, error);
if (r <= 0)
return r;
c = unit_get_exec_context(u);
if (!c)
return 0;
@@ -443,6 +463,7 @@ static const BusObjectImplementation bus_mount_object = {
{ bus_unit_cgroup_vtable, bus_unit_cgroup_find },
{ bus_cgroup_vtable, bus_cgroup_context_find },
{ bus_exec_vtable, bus_exec_context_find },
{ bus_unit_exec_vtable, bus_unit_exec_context_find },
{ bus_kill_vtable, bus_kill_context_find }),
};
@@ -471,6 +492,7 @@ static const BusObjectImplementation bus_service_object = {
{ bus_unit_cgroup_vtable, bus_unit_cgroup_find },
{ bus_cgroup_vtable, bus_cgroup_context_find },
{ bus_exec_vtable, bus_exec_context_find },
{ bus_unit_exec_vtable, bus_unit_exec_context_find },
{ bus_kill_vtable, bus_kill_context_find }),
};
@@ -491,6 +513,7 @@ static const BusObjectImplementation bus_socket_object = {
{ bus_unit_cgroup_vtable, bus_unit_cgroup_find },
{ bus_cgroup_vtable, bus_cgroup_context_find },
{ bus_exec_vtable, bus_exec_context_find },
{ bus_unit_exec_vtable, bus_unit_exec_context_find },
{ bus_kill_vtable, bus_kill_context_find }),
};
@@ -502,6 +525,7 @@ static const BusObjectImplementation bus_swap_object = {
{ bus_unit_cgroup_vtable, bus_unit_cgroup_find },
{ bus_cgroup_vtable, bus_cgroup_context_find },
{ bus_exec_vtable, bus_exec_context_find },
{ bus_unit_exec_vtable, bus_unit_exec_context_find },
{ bus_kill_vtable, bus_kill_context_find }),
};
@@ -1160,6 +1184,7 @@ void dump_bus_properties(FILE *f) {
vtable_dump_bus_properties(f, bus_cgroup_vtable);
vtable_dump_bus_properties(f, bus_device_vtable);
vtable_dump_bus_properties(f, bus_exec_vtable);
vtable_dump_bus_properties(f, bus_unit_exec_vtable);
vtable_dump_bus_properties(f, bus_job_vtable);
vtable_dump_bus_properties(f, bus_kill_vtable);
vtable_dump_bus_properties(f, bus_manager_vtable);

View File

@@ -19,6 +19,7 @@
#include "cgroup-setup.h"
#include "cgroup-util.h"
#include "chase.h"
#include "chattr-util.h"
#include "condition.h"
#include "dbus-unit.h"
#include "dropin.h"
@@ -45,6 +46,7 @@
#include "mountpoint-util.h"
#include "path-util.h"
#include "process-util.h"
#include "quota-util.h"
#include "rm-rf.h"
#include "serialize.h"
#include "set.h"
@@ -6722,6 +6724,52 @@ static uint64_t unit_get_cpu_weight(Unit *u) {
return cc ? cgroup_context_cpu_weight(cc, manager_state(u->manager)) : CGROUP_WEIGHT_DEFAULT;
}
int unit_get_exec_quota_stats(Unit *u, ExecContext *c, ExecDirectoryType dt, uint64_t *ret_usage, uint64_t *ret_limit) {
int r;
_cleanup_close_ int fd = -EBADF;
_cleanup_free_ char *p = NULL, *pp = NULL;
assert(u);
assert(c);
if (c->directories[dt].n_items == 0) {
*ret_usage = UINT64_MAX;
*ret_limit = UINT64_MAX;
return 0;
}
ExecDirectoryItem *i = &c->directories[dt].items[0];
p = path_join(u->manager->prefix[dt], i->path);
if (!p)
return log_oom_debug();
if (exec_directory_is_private(c, dt)) {
pp = path_join(u->manager->prefix[dt], "private", i->path);
if (!pp)
return log_oom_debug();
}
const char *target_dir = pp ?: p;
fd = open(target_dir, O_PATH | O_CLOEXEC | O_DIRECTORY);
if (fd < 0)
return log_unit_debug_errno(u, errno, "Failed to get exec quota stats: %m");
uint32_t proj_id;
r = read_fs_xattr_fd(fd, /* ret_xflags = */ NULL, &proj_id);
if (r < 0)
return log_unit_debug_errno(u, r, "Failed to get project ID for exec quota stats: %m");
struct dqblk req;
r = quota_query_proj_id(fd, proj_id, &req);
if (r <= 0)
return log_unit_debug_errno(u, r, "Failed to query project ID for exec quota stats: %m");
*ret_usage = req.dqb_curspace;
*ret_limit = req.dqb_bhardlimit * QIF_DQBLKSIZE;
return r;
}
int unit_compare_priority(Unit *a, Unit *b) {
int ret;

View File

@@ -938,6 +938,8 @@ int unit_add_default_target_dependency(Unit *u, Unit *target);
void unit_start_on_termination_deps(Unit *u, UnitDependencyAtom atom);
void unit_trigger_notify(Unit *u);
int unit_get_exec_quota_stats(Unit *u, ExecContext *c, ExecDirectoryType dt, uint64_t *ret_usage, uint64_t *ret_limit);
UnitFileState unit_get_unit_file_state(Unit *u);
PresetAction unit_get_unit_file_preset(Unit *u);

View File

@@ -2059,6 +2059,31 @@ static int bus_append_directory(sd_bus_message *m, const char *field, const char
return 1;
}
static int bus_append_quota_directory(sd_bus_message *m, const char *field, const char *eq) {
uint64_t quota_absolute = UINT64_MAX;
uint32_t quota_scale = UINT32_MAX;
int quota_enforce = false;
int r;
if (!isempty(eq) && !streq(eq, "off")) {
r = parse_permyriad(eq);
if (r < 0) {
r = parse_size(eq, 1024, &quota_absolute);
if (r < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse argument: %s=%s", field, eq);
} else
quota_scale = UINT32_SCALE_FROM_PERMYRIAD(r);
quota_enforce = true;
}
r = sd_bus_message_append(m, "(sv)", field, "(tus)", quota_absolute, quota_scale, yes_no(quota_enforce));
if (r < 0)
return bus_log_create_error(r);
return 1;
}
static int bus_append_protect_hostname(sd_bus_message *m, const char *field, const char *eq) {
int r;
@@ -2514,6 +2539,12 @@ static const BusProperty execute_properties[] = {
{ "ProtectControlGroupsEx", bus_append_boolean_or_ex_string }, /* compat */
{ "PrivateUsers", bus_append_boolean_or_ex_string },
{ "PrivateUsersEx", bus_append_boolean_or_ex_string }, /* compat */
{ "StateDirectoryQuota", bus_append_quota_directory },
{ "CacheDirectoryQuota", bus_append_quota_directory },
{ "LogsDirectoryQuota", bus_append_quota_directory },
{ "StateDirectoryAccounting", bus_append_parse_boolean },
{ "CacheDirectoryAccounting", bus_append_parse_boolean },
{ "LogsDirectoryAccounting", bus_append_parse_boolean },
{ NULL, bus_try_append_resource_limit, dump_resource_limits },
{}

View File

@@ -784,6 +784,8 @@ CPUSchedulingPriority=
CPUSchedulingResetOnFork=
CacheDirectory=
CacheDirectoryMode=
CacheDirectoryAccounting=
CacheDirectoryQuota=
Capability=
Compress=
ConfigurationDirectory=
@@ -861,6 +863,8 @@ LogRateLimitBurst=
LogFilterPatterns=
LogsDirectory=
LogsDirectoryMode=
LogsDirectoryAccounting=
LogsDirectoryQuota=
MACVLAN=
MachineID=
MaxFileSec=
@@ -943,6 +947,8 @@ StandardInputText=
StandardOutput=
StateDirectory=
StateDirectoryMode=
StateDirectoryAccounting=
StateDirectoryQuota=
Storage=
SuspendKeyIgnoreInhibited=
SyncIntervalSec=