From 1c231f56482546725c4dbd3303f70300bd3c63e9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 4 Mar 2014 19:20:21 +0100 Subject: [PATCH 1/1] logind: make $XDG_RUNTIME_DIR a per-user tmpfs This way each user allocates from his own pool, with its own size limit. This puts the size limit by default to 10% of the physical RAM size but makes it configurable in logind.conf. --- configure.ac | 1 + man/logind.conf.xml | 22 +++++++++ src/login/logind-gperf.gperf | 1 + src/login/logind-user.c | 92 +++++++++++++++++++++++++++++++++--- src/login/logind.c | 2 + src/login/logind.conf | 1 + src/login/logind.h | 4 ++ src/shared/macro.h | 2 +- src/shared/util.c | 12 +++++ src/shared/util.h | 10 ++++ 10 files changed, 139 insertions(+), 8 deletions(-) diff --git a/configure.ac b/configure.ac index 7920d6cad..880672d42 100644 --- a/configure.ac +++ b/configure.ac @@ -180,6 +180,7 @@ AC_SUBST([OUR_LDFLAGS], "$with_ldflags $address_sanitizer_ldflags") AC_CHECK_SIZEOF(pid_t) AC_CHECK_SIZEOF(uid_t) +AC_CHECK_SIZEOF(gid_t) # ------------------------------------------------------------------------------ # we use python to build the man page index, and for systemd-python diff --git a/man/logind.conf.xml b/man/logind.conf.xml index 767320166..ce8f5097c 100644 --- a/man/logind.conf.xml +++ b/man/logind.conf.xml @@ -295,6 +295,28 @@ + + RuntimeDirectorySize= + + Sets the size limit on + the + $XDG_RUNTIME_DIR + runtime directory for each user who + logs in. Takes a size in bytes, + possibly suffixed with the usual K, G, + M, T suffixes, to the base 1024 + (IEC). Alternatively, a percentage + suffixed by % may + be specified, which sets the size + limit relative to the amount of + physical RAM. Defaults to 10%. Note + that this size is a safety limit + only. As each runtime directory is a + tmpfs file system it will only consume + as much memory as it is filled up + to. + + diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf index 845302a54..d870f88cd 100644 --- a/src/login/logind-gperf.gperf +++ b/src/login/logind-gperf.gperf @@ -30,3 +30,4 @@ Login.HibernateKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manag Login.LidSwitchIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, lid_switch_ignore_inhibited) Login.IdleAction, config_parse_handle_action, 0, offsetof(Manager, idle_action) Login.IdleActionSec, config_parse_sec, 0, offsetof(Manager, idle_action_usec) +Login.RuntimeDirectorySize, config_parse_tmpfs_size, 0, offsetof(Manager, runtime_dir_size) diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 4af0e90c2..9bbe87957 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -19,6 +19,7 @@ along with systemd; If not, see . ***/ +#include #include #include #include @@ -28,10 +29,12 @@ #include "hashmap.h" #include "strv.h" #include "fileio.h" +#include "path-util.h" #include "special.h" #include "unit-name.h" #include "bus-util.h" #include "bus-error.h" +#include "conf-parser.h" #include "logind-user.h" User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) { @@ -311,21 +314,35 @@ static int user_mkdir_runtime_path(User *u) { } if (!u->runtime_path) { - if (asprintf(&p, "/run/user/%lu", (unsigned long) u->uid) < 0) + if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0) return log_oom(); } else p = u->runtime_path; - r = mkdir_safe_label(p, 0700, u->uid, u->gid); - if (r < 0) { - log_error("Failed to create runtime directory %s: %s", p, strerror(-r)); - free(p); - u->runtime_path = NULL; - return r; + if (path_is_mount_point(p, false) <= 0) { + _cleanup_free_ char *t = NULL; + + mkdir(p, 0700); + + if (asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size) < 0) { + r = log_oom(); + goto fail; + } + + r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t); + if (r < 0) { + log_error("Failed to mount per-user tmpfs directory %s: %s", p, strerror(-r)); + goto fail; + } } u->runtime_path = p; return 0; + +fail: + free(p); + u->runtime_path = NULL; + return r; } static int user_start_slice(User *u) { @@ -484,6 +501,13 @@ static int user_remove_runtime_path(User *u) { if (!u->runtime_path) return 0; + r = rm_rf(u->runtime_path, false, false, false); + if (r < 0) + log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r)); + + if (umount2(u->runtime_path, MNT_DETACH) < 0) + log_error("Failed to unmount user runtime directory %s: %m", u->runtime_path); + r = rm_rf(u->runtime_path, false, true, false); if (r < 0) log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r)); @@ -691,3 +715,57 @@ static const char* const user_state_table[_USER_STATE_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(user_state, UserState); + +int config_parse_tmpfs_size( + const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + size_t *sz = data; + const char *e; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + e = endswith(rvalue, "%"); + if (e) { + unsigned long ul; + char *f; + + errno = 0; + ul = strtoul(rvalue, &f, 10); + if (errno != 0 || f != e) { + log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue); + return 0; + } + + if (ul <= 0 || ul >= 100) { + log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue); + return 0; + } + + *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100)); + } else { + off_t o; + + r = parse_size(rvalue, 1024, &o); + if (r < 0 || (off_t) (size_t) o != o) { + log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue); + return 0; + } + + *sz = PAGE_ALIGN((size_t) o); + } + + return 0; +} diff --git a/src/login/logind.c b/src/login/logind.c index fd113b3e7..03b77539b 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -60,6 +60,8 @@ Manager *manager_new(void) { m->idle_action = HANDLE_IGNORE; m->idle_action_not_before_usec = now(CLOCK_MONOTONIC); + m->runtime_dir_size = PAGE_ALIGN((size_t) (physical_memory() / 10)); /* 10% */ + m->devices = hashmap_new(string_hash_func, string_compare_func); m->seats = hashmap_new(string_hash_func, string_compare_func); m->sessions = hashmap_new(string_hash_func, string_compare_func); diff --git a/src/login/logind.conf b/src/login/logind.conf index c0abf01b0..ddc23b8ac 100644 --- a/src/login/logind.conf +++ b/src/login/logind.conf @@ -24,3 +24,4 @@ #LidSwitchIgnoreInhibited=yes #IdleAction=ignore #IdleActionSec=30min +#RuntimeDirectorySize=10% diff --git a/src/login/logind.h b/src/login/logind.h index 4bb8e7b65..533554d4b 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -123,6 +123,8 @@ struct Manager { Hashmap *polkit_registry; sd_event_source *lid_switch_ignore_event_source; + + size_t runtime_dir_size; }; Manager *manager_new(void); @@ -185,3 +187,5 @@ int manager_watch_busname(Manager *manager, const char *name); void manager_drop_busname(Manager *manager, const char *name); int manager_set_lid_switch_ignore(Manager *m, usec_t until); + +int config_parse_tmpfs_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/shared/macro.h b/src/shared/macro.h index 5fd67c7ab..08a036b3b 100644 --- a/src/shared/macro.h +++ b/src/shared/macro.h @@ -98,7 +98,7 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) { return ((l + ali - 1) & ~(ali - 1)); } -#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) p)) +#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) p, ali)) #define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) diff --git a/src/shared/util.c b/src/shared/util.c index 285a263cd..10daff303 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -6291,3 +6291,15 @@ const char* personality_to_string(unsigned long p) { return NULL; } + +uint64_t physical_memory(void) { + long mem; + + /* We return this as uint64_t in case we are running as 32bit + * process on a 64bit kernel with huge amounts of memory */ + + mem = sysconf(_SC_PHYS_PAGES); + assert(mem > 0); + + return (uint64_t) mem * (uint64_t) page_size(); +} diff --git a/src/shared/util.h b/src/shared/util.h index 78b144473..e4300719a 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -58,6 +58,14 @@ # error Unknown uid_t size #endif +#if SIZEOF_GID_T == 4 +# define GID_FMT "%" PRIu32 +#elif SIZEOF_GID_T == 2 +# define GID_FMT "%" PRIu16 +#else +# error Unknown gid_t size +#endif + #include "macro.h" #include "time-util.h" @@ -883,3 +891,5 @@ int fd_warn_permissions(const char *path, int fd); unsigned long personality_from_string(const char *p); const char *personality_to_string(unsigned long); + +uint64_t physical_memory(void); -- 2.30.2