From 8fcb4101a9f969bcfc188a787ad86a0e252c5b9f Mon Sep 17 00:00:00 2001 From: Jan Synacek Date: Tue, 17 Apr 2018 16:42:44 +0200 Subject: [PATCH] logind: enable limiting of user session scopes using pam context objects (#8397) --- man/pam_elogind.xml | 54 +++++++++++ src/login/logind-dbus.c | 41 +++++---- src/login/logind-session.c | 8 +- src/login/logind-session.h | 2 +- src/login/logind.c | 2 +- src/login/logind.h | 2 +- src/login/pam_elogind.c | 179 +++++++++++++++++++++++++++++++------ 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 @@ -249,6 +249,60 @@ based on the $DISPLAY variable. + + Session limits + + PAM modules earlier in the stack, that is those that come before pam_elogind.so, + 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 user@.service unit instance, + which runs the elogind --user 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. + + + See + elogind.resource-control5 for more information about the resources. + Also, see pam_set_data3 for additional information about how to set + the context objects. + + + + + elogind.memory_max + + Sets unit MemoryMax=. + + + + elogind.tasks_max + + Sets unit TasksMax=. + + + + elogind.cpu_weight + + Sets unit CPUWeight=. + + + + elogind.io_weight + + Sets unit IOWeight=. + + + + Example data as can be provided from an another PAM module: + +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); + + + + + Example 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 . ***/ #include @@ -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)); -- 2.30.2