chiark / gitweb /
logind: make $XDG_RUNTIME_DIR a per-user tmpfs
authorLennart Poettering <lennart@poettering.net>
Tue, 4 Mar 2014 18:20:21 +0000 (19:20 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 4 Mar 2014 19:02:50 +0000 (20:02 +0100)
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
man/logind.conf.xml
src/login/logind-gperf.gperf
src/login/logind-user.c
src/login/logind.c
src/login/logind.conf
src/login/logind.h
src/shared/macro.h
src/shared/util.c
src/shared/util.h

index 7920d6c..880672d 100644 (file)
@@ -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
index 7673201..ce8f509 100644 (file)
                                 </para></listitem>
                         </varlistentry>
 
+                        <varlistentry>
+                                <term><varname>RuntimeDirectorySize=</varname></term>
+
+                                <listitem><para>Sets the size limit on
+                                the
+                                <varname>$XDG_RUNTIME_DIR</varname>
+                                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 <literal>%</literal> 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.</para></listitem>
+                        </varlistentry>
+
                 </variablelist>
         </refsect1>
 
index 845302a..d870f88 100644 (file)
@@ -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)
index 4af0e90..9bbe879 100644 (file)
@@ -19,6 +19,7 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <sys/mount.h>
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
 #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;
+}
index fd113b3..03b7753 100644 (file)
@@ -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);
index c0abf01..ddc23b8 100644 (file)
@@ -24,3 +24,4 @@
 #LidSwitchIgnoreInhibited=yes
 #IdleAction=ignore
 #IdleActionSec=30min
+#RuntimeDirectorySize=10%
index 4bb8e7b..533554d 100644 (file)
@@ -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);
index 5fd67c7..08a036b 100644 (file)
@@ -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]))
 
index 285a263..10daff3 100644 (file)
@@ -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();
+}
index 78b1444..e430071 100644 (file)
 #  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);