chiark / gitweb /
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)
committerSven Eden <yamakuzure@gmx.net>
Fri, 24 Aug 2018 14:47:08 +0000 (16:47 +0200)
man/pam_elogind.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_elogind.c

index 7278f477408df53df8574569fcdaddaf65bc45bf..1942ce2d42dab9c92bfc9292e8ef63fa9884ad5b 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_elogind.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>elogind --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>elogind.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>elogind.memory_max</varname></term>
+
+        <listitem><para>Sets unit <varname>MemoryMax=</varname>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>elogind.tasks_max</varname></term>
+
+        <listitem><para>Sets unit <varname>TasksMax=</varname>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>elogind.cpu_weight</varname></term>
+
+        <listitem><para>Sets unit <varname>CPUWeight=</varname>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>elogind.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, "elogind.memory_max", (void *)"200M", cleanup);
+pam_set_data(handle, "elogind.tasks_max",  (void *)"50",   cleanup);
+pam_set_data(handle, "elogind.cpu_weight", (void *)"100",  cleanup);
+pam_set_data(handle, "elogind.io_weight",  (void *)"340",  cleanup);
+      </programlisting>
+    </para>
+
+  </refsect1>
+
   <refsect1>
     <title>Example</title>
 
index 6bc76464c33b4c367e751b4ef130024ab2ea93e4..e185c26ffed12002991cc655795237acb9efc218 100644 (file)
@@ -3,19 +3,6 @@
   This file is part of systemd.
 
   Copyright 2011 Lennart Poettering
-
-  elogind is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  elogind is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with elogind; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
 #include <errno.h>
@@ -760,10 +747,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;
@@ -919,7 +902,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;
 
@@ -3228,7 +3219,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) {
 
@@ -3292,9 +3283,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 5967127e6f1d93803bdd2254ef38639ef5519f32..39d7a366fdb4ae370c2c539217bd8f081633ddbf 100644 (file)
@@ -555,7 +555,7 @@ int session_activate(Session *s) {
 }
 
 #if 0 /// UNNEEDED by elogind
-static int session_start_scope(Session *s) {
+static int session_start_scope(Session *s, sd_bus_message *properties) {
         int r;
 
         assert(s);
@@ -580,7 +580,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) {
@@ -621,7 +621,7 @@ static int session_start_cgroup(Session *s) {
 }
 #endif // 0
 
-int session_start(Session *s) {
+int session_start(Session *s, sd_bus_message *properties) {
         int r;
 
         assert(s);
@@ -638,7 +638,7 @@ int session_start(Session *s) {
 
         /* Create cgroup */
 #if 0 /// elogind does its own session management
-        r = session_start_scope(s);
+        r = session_start_scope(s, properties);
 #else
         r = session_start_cgroup(s);
 #endif // 0
index 1eef439aecabc9edf32f1992ccdaf082a090adf1..04a37475eedc93d0dbdbd30ff1378ec58b79dda5 100644 (file)
@@ -129,7 +129,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 5407c3c33d39d8c6ce1e6c0bebfbdaf131280271..cad8da502c67a74e06e42c93589d5c2a542ebeed 100644 (file)
@@ -1256,7 +1256,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 b51cea2fb69e8ca39ad172f5aba8cfbf0046ff18..1221b377c5c4a0ac6497177bcd01f21da59fac1c 100644 (file)
@@ -215,7 +215,7 @@ int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_
 
 #if 0 /// UNNEEDED by elogind
 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 abfecb05f5f31167569c0afe199f84e82646778a..bf5fcf4d400d7e501d0cb7dde3356f82ab3f88fd 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 elogind.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 elogind.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 elogind.cpu_weight: %s, ignoring.", limit);
+                else
+                        pam_syslog(handle, LOG_WARNING, "Failed to parse elogind.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;
@@ -355,6 +437,11 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         remote = !isempty(remote_host) && !is_localhost(remote_host);
 
+        (void) pam_get_data(handle, "elogind.memory_max", (const void **)&memory_max);
+        (void) pam_get_data(handle, "elogind.tasks_max",  (const void **)&tasks_max);
+        (void) pam_get_data(handle, "elogind.cpu_weight", (const void **)&cpu_weight);
+        (void) pam_get_data(handle, "elogind.io_weight",  (const void **)&io_weight);
+
         /* Talk to logind over the message bus */
 
         r = sd_bus_open_system(&bus);
@@ -363,7 +450,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(),
@@ -371,29 +458,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));