]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
logind: enable limiting of user session scopes using pam context objects (#8397)
authorJan Synacek <jan.synacek@gmail.com>
Tue, 17 Apr 2018 14:42:44 +0000 (16:42 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 17 Apr 2018 14:42:44 +0000 (16:42 +0200)
man/pam_systemd.xml
src/login/logind-dbus.c
src/login/logind-session.c
src/login/logind-session.h
src/login/logind.c
src/login/logind.h
src/login/pam_systemd.c

index 5aa12c704b7e756c4b690e151952498ee6de53aa..cdf45f8c20199e1bde94dcb482cffda8b1e5e55f 100644 (file)
     based on the <varname>$DISPLAY</varname> variable.</para>
   </refsect1>
 
+  <refsect1>
+    <title>Session limits</title>
+
+    <para>PAM modules earlier in the stack, that is those that come before <command>pam_systemd.so</command>,
+    can set session scope limits using the PAM context objects. The data for these objects is provided as NUL-terminated C strings
+    and maps directly to the respective unit resource control directives. Note that these limits apply to individual sessions of the user,
+    they do not apply to all user processes as a combined whole. In particular, the per-user <command>user@.service</command> unit instance,
+    which runs the <command>systemd --user</command> manager process and its children, and is tracked outside of any session, being shared
+    by all the user's sessions, is not covered by these limits.
+    </para>
+
+    <para> See
+    <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information about the resources.
+    Also, see <citerefentry><refentrytitle>pam_set_data</refentrytitle><manvolnum>3</manvolnum></citerefentry> for additional information about how to set
+    the context objects.
+    </para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>systemd.memory_max</varname></term>
+
+        <listitem><para>Sets unit <varname>MemoryMax=</varname>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.tasks_max</varname></term>
+
+        <listitem><para>Sets unit <varname>TasksMax=</varname>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.cpu_weight</varname></term>
+
+        <listitem><para>Sets unit <varname>CPUWeight=</varname>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.io_weight</varname></term>
+
+        <listitem><para>Sets unit <varname>IOWeight=</varname>.</para></listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>Example data as can be provided from an another PAM module:
+      <programlisting>
+pam_set_data(handle, "systemd.memory_max", (void *)"200M", cleanup);
+pam_set_data(handle, "systemd.tasks_max",  (void *)"50",   cleanup);
+pam_set_data(handle, "systemd.cpu_weight", (void *)"100",  cleanup);
+pam_set_data(handle, "systemd.io_weight",  (void *)"340",  cleanup);
+      </programlisting>
+    </para>
+
+  </refsect1>
+
   <refsect1>
     <title>Example</title>
 
index 563c7fef9f9dbfd50c9cad466f42a44f018a57f0..d92437b67f3f023e9652263029b7d28b336bf112 100644 (file)
@@ -744,10 +744,6 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                 }
         }
 
-        r = sd_bus_message_enter_container(message, 'a', "(sv)");
-        if (r < 0)
-                return r;
-
         if (t == _SESSION_TYPE_INVALID) {
                 if (!isempty(display))
                         t = SESSION_X11;
@@ -903,7 +899,15 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                         goto fail;
         }
 
-        r = session_start(session);
+        r = sd_bus_message_enter_container(message, 'a', "(sv)");
+        if (r < 0)
+                return r;
+
+        r = session_start(session, message);
+        if (r < 0)
+                goto fail;
+
+        r = sd_bus_message_exit_container(message);
         if (r < 0)
                 goto fail;
 
@@ -3056,7 +3060,7 @@ int manager_start_scope(
                 const char *description,
                 const char *after,
                 const char *after2,
-                uint64_t tasks_max,
+                sd_bus_message *more_properties,
                 sd_bus_error *error,
                 char **job) {
 
@@ -3120,9 +3124,17 @@ int manager_start_scope(
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max);
+        /* disable TasksMax= for the session scope, rely on the slice setting for it */
+        r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", (uint64_t)-1);
         if (r < 0)
-                return r;
+                return bus_log_create_error(r);
+
+        if (more_properties) {
+                /* If TasksMax also appears here, it will overwrite the default value set above */
+                r = sd_bus_message_copy(m, more_properties, true);
+                if (r < 0)
+                        return r;
+        }
 
         r = sd_bus_message_close_container(m);
         if (r < 0)
index bfdc21c2772a063c65a6cd5d1de4125979c6c80a..d9277501d2070bbc0104be299160448c6444046a 100644 (file)
@@ -545,7 +545,7 @@ int session_activate(Session *s) {
         return 0;
 }
 
-static int session_start_scope(Session *s) {
+static int session_start_scope(Session *s, sd_bus_message *properties) {
         int r;
 
         assert(s);
@@ -570,7 +570,7 @@ static int session_start_scope(Session *s) {
                                 description,
                                 "systemd-logind.service",
                                 "systemd-user-sessions.service",
-                                (uint64_t) -1, /* disable TasksMax= for the scope, rely on the slice setting for it */
+                                properties,
                                 &error,
                                 &job);
                 if (r < 0) {
@@ -591,7 +591,7 @@ static int session_start_scope(Session *s) {
         return 0;
 }
 
-int session_start(Session *s) {
+int session_start(Session *s, sd_bus_message *properties) {
         int r;
 
         assert(s);
@@ -607,7 +607,7 @@ int session_start(Session *s) {
                 return r;
 
         /* Create cgroup */
-        r = session_start_scope(s);
+        r = session_start_scope(s, properties);
         if (r < 0)
                 return r;
 
index d81a339379ef5a68a78bc7e60819092a6fbfceb1..6043048ae9dae4a9c4bc09a515fb5a190aa4116c 100644 (file)
@@ -127,7 +127,7 @@ void session_set_idle_hint(Session *s, bool b);
 int session_get_locked_hint(Session *s);
 void session_set_locked_hint(Session *s, bool b);
 int session_create_fifo(Session *s);
-int session_start(Session *s);
+int session_start(Session *s, sd_bus_message *properties);
 int session_stop(Session *s, bool force);
 int session_finalize(Session *s);
 int session_release(Session *s);
index 726eca73f91b0e1ac8d32d0ea8fe285bdea0302a..6bddcb541fc60a0212f6c49b78eee3f77a74909b 100644 (file)
@@ -1195,7 +1195,7 @@ static int manager_startup(Manager *m) {
                 user_start(user);
 
         HASHMAP_FOREACH(session, m->sessions, i)
-                session_start(session);
+                session_start(session, NULL);
 
         HASHMAP_FOREACH(inhibitor, m->inhibitors, i)
                 inhibitor_start(inhibitor);
index 4786392f8e27aafe3e10b97fc9f61dddd792b96d..5574fcd591919bfa72919ddd25b58e62b303bd87 100644 (file)
@@ -164,7 +164,7 @@ int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name
 int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_;
 
 int manager_start_slice(Manager *manager, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job);
-int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job);
+int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, sd_bus_message *more_properties, sd_bus_error *error, char **job);
 int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
 int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
 int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error);
index b811e62a067adf8427c018706cf1b96a4b1e69ad..e1c2615f61f73d4b1560c82b14fda492dbee04a9 100644 (file)
@@ -35,6 +35,7 @@
 #include "terminal-util.h"
 #include "util.h"
 #include "path-util.h"
+#include "cgroup-util.h"
 
 static int parse_argv(
                 pam_handle_t *handle,
@@ -198,13 +199,93 @@ error:
         return r;
 }
 
+static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
+        uint64_t val;
+        int r;
+
+        if (isempty(limit))
+                return 0;
+
+        if (streq(limit, "infinity")) {
+                r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", (uint64_t)-1);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+                        return r;
+                }
+        } else {
+                r = parse_percent(limit);
+                if (r >= 0) {
+                        r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
+                        if (r < 0) {
+                                pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+                                return r;
+                        }
+                } else {
+                        r = parse_size(limit, 1024, &val);
+                        if (r >= 0) {
+                                r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", val);
+                                if (r < 0) {
+                                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+                                        return r;
+                                }
+                        } else
+                                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.limit: %s, ignoring.", limit);
+                }
+        }
+
+        return 0;
+}
+
+static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit)
+{
+        uint64_t val;
+        int r;
+
+        /* No need to parse "infinity" here, it will be set unconditionally later in manager_start_scope() */
+        if (isempty(limit) || streq(limit, "infinity"))
+                return 0;
+
+        r = safe_atou64(limit, &val);
+        if (r >= 0) {
+                r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", val);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+                        return r;
+                }
+        } else
+                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.limit: %s, ignoring.", limit);
+
+        return 0;
+}
+
+static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit, const char *field) {
+        uint64_t val;
+        int r;
+
+        if (!isempty(limit)) {
+                r = cg_weight_parse(limit, &val);
+                if (r >= 0) {
+                        r = sd_bus_message_append(m, "(sv)", field, "t", val);
+                        if (r < 0) {
+                                pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+                                return r;
+                        }
+                } else if (streq(field, "CPUWeight"))
+                        pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight: %s, ignoring.", limit);
+                else
+                        pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight: %s, ignoring.", limit);
+        }
+
+        return 0;
+}
+
 _public_ PAM_EXTERN int pam_sm_open_session(
                 pam_handle_t *handle,
                 int flags,
                 int argc, const char **argv) {
 
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
         const char
                 *username, *id, *object_path, *runtime_path,
                 *service = NULL,
@@ -212,7 +293,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 *remote_user = NULL, *remote_host = NULL,
                 *seat = NULL,
                 *type = NULL, *class = NULL,
-                *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL;
+                *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL,
+                *memory_max = NULL, *tasks_max = NULL, *cpu_weight = NULL, *io_weight = NULL;
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int session_fd = -1, existing, r;
         bool debug = false, remote;
@@ -353,6 +435,11 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         remote = !isempty(remote_host) && !is_localhost(remote_host);
 
+        (void) pam_get_data(handle, "systemd.memory_max", (const void **)&memory_max);
+        (void) pam_get_data(handle, "systemd.tasks_max",  (const void **)&tasks_max);
+        (void) pam_get_data(handle, "systemd.cpu_weight", (const void **)&cpu_weight);
+        (void) pam_get_data(handle, "systemd.io_weight",  (const void **)&io_weight);
+
         /* Talk to logind over the message bus */
 
         r = sd_bus_open_system(&bus);
@@ -361,7 +448,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 return PAM_SESSION_ERR;
         }
 
-        if (debug)
+        if (debug) {
                 pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
                            "uid="UID_FMT" pid="PID_FMT" service=%s type=%s class=%s desktop=%s seat=%s vtnr=%"PRIu32" tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
                            pw->pw_uid, getpid_cached(),
@@ -369,29 +456,71 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                            type, class, strempty(desktop),
                            strempty(seat), vtnr, strempty(tty), strempty(display),
                            yes_no(remote), strempty(remote_user), strempty(remote_host));
+                pam_syslog(handle, LOG_DEBUG, "Session limits: "
+                           "memory_max=%s tasks_max=%s cpu_weight=%s io_weight=%s",
+                           strna(memory_max), strna(tasks_max), strna(cpu_weight), strna(io_weight));
+        }
+
+        r = sd_bus_message_new_method_call(
+                        bus,
+                        &m,
+                        "org.freedesktop.login1",
+                        "/org/freedesktop/login1",
+                        "org.freedesktop.login1.Manager",
+                        "CreateSession");
+        if (r < 0) {
+                pam_syslog(handle, LOG_ERR, "Failed to create CreateSession method call: %s", strerror(-r));
+                return PAM_SESSION_ERR;
+        }
+
+        r = sd_bus_message_append(m, "uusssssussbss",
+                        (uint32_t) pw->pw_uid,
+                        (uint32_t) getpid_cached(),
+                        service,
+                        type,
+                        class,
+                        desktop,
+                        seat,
+                        vtnr,
+                        tty,
+                        display,
+                        remote,
+                        remote_user,
+                        remote_host);
+        if (r < 0) {
+                pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+                return PAM_SESSION_ERR;
+        }
+
+        r = sd_bus_message_open_container(m, 'a', "(sv)");
+        if (r < 0) {
+                pam_syslog(handle, LOG_ERR, "Failed to open message container: %s", strerror(-r));
+                return PAM_SYSTEM_ERR;
+        }
+
+        r = append_session_memory_max(handle, m, memory_max);
+        if (r < 0)
+                return PAM_SESSION_ERR;
+
+        r = append_session_tasks_max(handle, m, tasks_max);
+        if (r < 0)
+                return PAM_SESSION_ERR;
+
+        r = append_session_cg_weight(handle, m, cpu_weight, "CPUWeight");
+        if (r < 0)
+                return PAM_SESSION_ERR;
+
+        r = append_session_cg_weight(handle, m, io_weight, "IOWeight");
+        if (r < 0)
+                return PAM_SESSION_ERR;
+
+        r = sd_bus_message_close_container(m);
+        if (r < 0) {
+                pam_syslog(handle, LOG_ERR, "Failed to close message container: %s", strerror(-r));
+                return PAM_SYSTEM_ERR;
+        }
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.login1",
-                               "/org/freedesktop/login1",
-                               "org.freedesktop.login1.Manager",
-                               "CreateSession",
-                               &error,
-                               &reply,
-                               "uusssssussbssa(sv)",
-                               (uint32_t) pw->pw_uid,
-                               (uint32_t) getpid_cached(),
-                               service,
-                               type,
-                               class,
-                               desktop,
-                               seat,
-                               vtnr,
-                               tty,
-                               display,
-                               remote,
-                               remote_user,
-                               remote_host,
-                               0);
+        r = sd_bus_call(bus, m, 0, &error, &reply);
         if (r < 0) {
                 if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) {
                         pam_syslog(handle, LOG_DEBUG, "Cannot create session: %s", bus_error_message(&error, r));