summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Synacek <jan.synacek@gmail.com>2018-04-17 16:42:44 +0200
committerSven Eden <yamakuzure@gmx.net>2018-08-24 16:47:08 +0200
commit8fcb4101a9f969bcfc188a787ad86a0e252c5b9f (patch)
tree6027e10984d850cc470aabc9226303c5f1b76e77
parenta37ab83d0da89ef55905edfe838a5c05891a989f (diff)
logind: enable limiting of user session scopes using pam context objects (#8397)
-rw-r--r--man/pam_elogind.xml54
-rw-r--r--src/login/logind-dbus.c41
-rw-r--r--src/login/logind-session.c8
-rw-r--r--src/login/logind-session.h2
-rw-r--r--src/login/logind.c2
-rw-r--r--src/login/logind.h2
-rw-r--r--src/login/pam_elogind.c179
7 files changed, 235 insertions, 53 deletions
diff --git a/man/pam_elogind.xml b/man/pam_elogind.xml
index 7278f4774..1942ce2d4 100644
--- a/man/pam_elogind.xml
+++ b/man/pam_elogind.xml
@@ -250,6 +250,60 @@
</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>
<programlisting>#%PAM-1.0
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 6bc76464c..e185c26ff 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -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)
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index 5967127e6..39d7a366f 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -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
diff --git a/src/login/logind-session.h b/src/login/logind-session.h
index 1eef439ae..04a37475e 100644
--- a/src/login/logind-session.h
+++ b/src/login/logind-session.h
@@ -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);
diff --git a/src/login/logind.c b/src/login/logind.c
index 5407c3c33..cad8da502 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -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);
diff --git a/src/login/logind.h b/src/login/logind.h
index b51cea2fb..1221b377c 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -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);
diff --git a/src/login/pam_elogind.c b/src/login/pam_elogind.c
index abfecb05f..bf5fcf4d4 100644
--- a/src/login/pam_elogind.c
+++ b/src/login/pam_elogind.c
@@ -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));