diff --git a/man/logind.conf.xml b/man/logind.conf.xml index 43d1ffbd3cf..3cde2671198 100644 --- a/man/logind.conf.xml +++ b/man/logind.conf.xml @@ -1,4 +1,4 @@ - + @@ -277,7 +277,18 @@ limit relative to the amount of physical RAM. Defaults to 10%. Note that this size is a safety limit only. As each runtime directory is a tmpfs file system, it will only consume as much - memory as is needed. + memory as is needed. + + + + UserTasksMax= + + Sets the maximum number of OS tasks each user + may run concurrently. This controls the + TasksMax= setting of the per-user slice + unit, see + systemd.resource-control5 + for details. diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 7890d68aa0f..1b460b3bb04 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -2740,13 +2740,101 @@ int manager_send_changed(Manager *manager, const char *property, ...) { l); } +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) { + + _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL; + int r; + + assert(manager); + assert(slice); + + r = sd_bus_message_new_method_call( + manager->bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "ss", strempty(slice), "fail"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return r; + + if (!isempty(description)) { + r = sd_bus_message_append(m, "(sv)", "Description", "s", description); + if (r < 0) + return r; + } + + if (!isempty(after)) { + r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after); + if (r < 0) + return r; + } + + if (!isempty(after2)) { + r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2); + if (r < 0) + return r; + } + + r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "a(sa(sv))", 0); + if (r < 0) + return r; + + r = sd_bus_call(manager->bus, m, 0, error, &reply); + if (r < 0) + return r; + + if (job) { + const char *j; + char *copy; + + r = sd_bus_message_read(reply, "o", &j); + if (r < 0) + return r; + + copy = strdup(j); + if (!copy) + return -ENOMEM; + + *job = copy; + } + + return 1; +} + int manager_start_scope( Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, - const char *after, const char *after2, + const char *after, + const char *after2, + uint64_t tasks_max, sd_bus_error *error, char **job) { @@ -2814,6 +2902,10 @@ int manager_start_scope( if (r < 0) return r; + r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max); + if (r < 0) + return r; + r = sd_bus_message_close_container(m); if (r < 0) return r; diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf index 9218d098e02..8552c464cca 100644 --- a/src/login/logind-gperf.gperf +++ b/src/login/logind-gperf.gperf @@ -34,3 +34,4 @@ Login.IdleAction, config_parse_handle_action, 0, offsetof(Manag Login.IdleActionSec, config_parse_sec, 0, offsetof(Manager, idle_action_usec) Login.RuntimeDirectorySize, config_parse_tmpfs_size, 0, offsetof(Manager, runtime_dir_size) Login.RemoveIPC, config_parse_bool, 0, offsetof(Manager, remove_ipc) +Login.UserTasksMax, config_parse_uint64, 0, offsetof(Manager, user_tasks_max) diff --git a/src/login/logind-session.c b/src/login/logind-session.c index ef3d46f3fb5..6c4ada29fbe 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -516,21 +516,28 @@ static int session_start_scope(Session *s) { if (!s->scope) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_free_ char *description = NULL; char *scope, *job = NULL; - - description = strjoin("Session ", s->id, " of user ", s->user->name, NULL); - if (!description) - return log_oom(); + const char *description; scope = strjoin("session-", s->id, ".scope", NULL); if (!scope) return log_oom(); - r = manager_start_scope(s->manager, scope, s->leader, s->user->slice, description, "systemd-logind.service", "systemd-user-sessions.service", &error, &job); + description = strjoina("Session ", s->id, " of user ", s->user->name, NULL); + + r = manager_start_scope( + s->manager, + scope, + s->leader, + s->user->slice, + description, + "systemd-logind.service", + "systemd-user-sessions.service", + (uint64_t) -1, /* disable TasksMax= for the scope, rely on the slice setting for it */ + &error, + &job); if (r < 0) { - log_error("Failed to start session scope %s: %s %s", - scope, bus_error_message(&error, r), error.name); + log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(&error, r)); free(scope); return r; } else { @@ -542,7 +549,7 @@ static int session_start_scope(Session *s) { } if (s->scope) - hashmap_put(s->manager->session_units, s->scope, s); + (void) hashmap_put(s->manager->session_units, s->scope, s); return 0; } diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 56bc5a010c6..7bdbe6583c2 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -25,6 +25,7 @@ #include #include "alloc-util.h" +#include "bus-common-errors.h" #include "bus-error.h" #include "bus-util.h" #include "clean-ipc.h" @@ -44,6 +45,7 @@ #include "rm-rf.h" #include "smack-util.h" #include "special.h" +#include "stdio-util.h" #include "string-table.h" #include "unit-name.h" #include "user-util.h" @@ -392,34 +394,51 @@ fail: } static int user_start_slice(User *u) { - char *job; int r; assert(u); if (!u->slice) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice; - sprintf(lu, UID_FMT, u->uid); + char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice, *job; + const char *description; + u->slice_job = mfree(u->slice_job); + + xsprintf(lu, UID_FMT, u->uid); r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &slice); if (r < 0) - return r; + return log_error_errno(r, "Failed to build slice name: %m"); - r = manager_start_unit(u->manager, slice, &error, &job); + description = strjoina("User Slice of ", u->name); + + r = manager_start_slice( + u->manager, + slice, + description, + "systemd-logind.service", + "systemd-user-sessions.service", + u->manager->user_tasks_max, + &error, + &job); if (r < 0) { - log_error("Failed to start user slice: %s", bus_error_message(&error, r)); - free(slice); + + if (sd_bus_error_has_name(&error, BUS_ERROR_UNIT_EXISTS)) + /* The slice already exists? If so, that's fine, let's just reuse it */ + u->slice = slice; + else { + log_error_errno(r, "Failed to start user slice %s, ignoring: %s (%s)", slice, bus_error_message(&error, r), error.name); + free(slice); + /* we don't fail due to this, let's try to continue */ + } } else { u->slice = slice; - - free(u->slice_job); u->slice_job = job; } } if (u->slice) - hashmap_put(u->manager->user_units, u->slice, u); + (void) hashmap_put(u->manager->user_units, u->slice, u); return 0; } @@ -433,16 +452,21 @@ static int user_start_service(User *u) { if (!u->service) { char lu[DECIMAL_STR_MAX(uid_t) + 1], *service; - sprintf(lu, UID_FMT, u->uid); + xsprintf(lu, UID_FMT, u->uid); r = unit_name_build("user", lu, ".service", &service); if (r < 0) return log_error_errno(r, "Failed to build service name: %m"); - r = manager_start_unit(u->manager, service, &error, &job); + r = manager_start_unit( + u->manager, + service, + &error, + &job); if (r < 0) { - log_error("Failed to start user service: %s", bus_error_message(&error, r)); + log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r)); free(service); + /* we don't fail due to this, let's try to continue */ } else { u->service = service; @@ -452,7 +476,7 @@ static int user_start_service(User *u) { } if (u->service) - hashmap_put(u->manager->user_units, u->service, u); + (void) hashmap_put(u->manager->user_units, u->service, u); return 0; } diff --git a/src/login/logind.c b/src/login/logind.c index be6bbe5b5cd..c3a2fb5acd8 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -70,6 +70,7 @@ static Manager *manager_new(void) { m->idle_action_not_before_usec = now(CLOCK_MONOTONIC); m->runtime_dir_size = PAGE_ALIGN((size_t) (physical_memory() / 10)); /* 10% */ + m->user_tasks_max = (uint64_t) -1; m->devices = hashmap_new(&string_hash_ops); m->seats = hashmap_new(&string_hash_ops); diff --git a/src/login/logind.conf b/src/login/logind.conf index 6df6f04c775..e8f14877347 100644 --- a/src/login/logind.conf +++ b/src/login/logind.conf @@ -32,3 +32,4 @@ #IdleActionSec=30min #RuntimeDirectorySize=10% #RemoveIPC=yes +#UserTasksMax= diff --git a/src/login/logind.h b/src/login/logind.h index 44e05d8b01a..f34544e64ce 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -134,6 +134,7 @@ struct Manager { sd_event_source *lid_switch_ignore_event_source; size_t runtime_dir_size; + uint64_t user_tasks_max; }; int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device); @@ -171,7 +172,8 @@ 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_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, sd_bus_error *error, char **job); +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_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);