logind: enable limiting of user session scopes using pam context objects (#8397)

This commit is contained in:
Jan Synacek 2018-04-17 16:42:44 +02:00 committed by Lennart Poettering
parent 2cb36f7c1e
commit 22f9331412
7 changed files with 235 additions and 40 deletions

View file

@ -249,6 +249,60 @@
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>

View 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)

View 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;

View 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);

View 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);

View 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);

View 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_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_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(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));