#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"
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,
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),
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, ¤t_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;
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);
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)", "a_absolute, "a_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;
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;
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;
{ 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 }),
};
{ 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 }),
};
{ 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 }),
};
{ 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 }),
};
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);
#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"
#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"
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;
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, "a_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;
{ "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 },
{}