Merge pull request #7599 from keszybz/slice-templates

Make user@.service independent of logind
This commit is contained in:
Lennart Poettering 2018-04-26 21:39:05 +02:00 committed by GitHub
commit 385f3a0d8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 513 additions and 374 deletions

View File

@ -325,17 +325,6 @@
<filename>systemd-logind</filename>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>UserTasksMax=</varname></term>
<listitem><para>Sets the maximum number of OS tasks each user may run concurrently. This controls the
<varname>TasksMax=</varname> setting of the per-user slice unit, see
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details. If assigned the special value <literal>infinity</literal>, no tasks limit is applied.
Defaults to 33%, which equals 10813 with the kernel's defaults on the host, but might be smaller in
OS containers.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>RemoveIPC=</varname></term>

View File

@ -1508,7 +1508,7 @@
</variablelist>
<para>The following specifiers are interpreted in the Install
section: %n, %N, %p, %i, %U, %u, %m, %H, %b, %v. For their meaning
section: %n, %N, %p, %i, %j, %U, %u, %m, %H, %b, %v. For their meaning
see the next section.
</para>
</refsect1>
@ -1543,28 +1543,38 @@
</row>
<row>
<entry><literal>%N</literal></entry>
<entry>Unescaped full unit name</entry>
<entry>Same as <literal>%n</literal>, but with escaping undone. This undoes the escaping used when generating unit names from arbitrary strings (see above). </entry>
<entry>Full unit name</entry>
<entry>Same as <literal>%n</literal>, but with the type suffix removed.</entry>
</row>
<row>
<entry><literal>%p</literal></entry>
<entry>Prefix name</entry>
<entry>For instantiated units, this refers to the string before the <literal>@</literal> character of the unit name. For non-instantiated units, this refers to the name of the unit with the type suffix removed.</entry>
<entry>For instantiated units, this refers to the string before the first <literal>@</literal> character of the unit name. For non-instantiated units, same as <literal>%N</literal>.</entry>
</row>
<row>
<entry><literal>%P</literal></entry>
<entry>Unescaped prefix name</entry>
<entry>Same as <literal>%p</literal>, but with escaping undone</entry>
<entry>Same as <literal>%p</literal>, but with escaping undone.</entry>
</row>
<row>
<entry><literal>%i</literal></entry>
<entry>Instance name</entry>
<entry>For instantiated units: this is the string between the <literal>@</literal> character and the suffix of the unit name.</entry>
<entry>For instantiated units this is the string between the first <literal>@</literal> character and the type suffix. Empty for non-instantiated units.</entry>
</row>
<row>
<entry><literal>%I</literal></entry>
<entry>Unescaped instance name</entry>
<entry>Same as <literal>%i</literal>, but with escaping undone</entry>
<entry>Same as <literal>%i</literal>, but with escaping undone.</entry>
</row>
<row>
<entry><literal>%j</literal></entry>
<entry>Final component of the prefix</entry>
<entry>This is the string between the last <literal>-</literal> and the end of the prefix name. If there is no <literal>-</literal>, this is the same as <literal>%p</literal>.</entry>
</row>
<row>
<entry><literal>%J</literal></entry>
<entry>Unescaped final component of the prefix</entry>
<entry>Same as <literal>%j</literal>, but with escaping undone.</entry>
</row>
<row>
<entry><literal>%f</literal></entry>

View File

@ -1672,6 +1672,14 @@ if conf.get('ENABLE_LOGIND') == 1
endif
endif
executable('systemd-user-runtime-dir',
user_runtime_dir_sources,
include_directories : includes,
link_with : [libshared, liblogind_core],
install_rpath : rootlibexecdir,
install : true,
install_dir : rootlibexecdir)
if conf.get('HAVE_PAM') == 1
executable('systemd-user-sessions',
'src/user-sessions/user-sessions.c',

View File

@ -710,7 +710,7 @@ int slice_build_parent_slice(const char *slice, char **ret) {
return 1;
}
int slice_build_subslice(const char *slice, const char*name, char **ret) {
int slice_build_subslice(const char *slice, const char *name, char **ret) {
char *subslice;
assert(slice);

View File

@ -55,6 +55,37 @@ static int specifier_instance_unescaped(char specifier, void *data, void *userda
return unit_name_unescape(strempty(u->instance), ret);
}
static int specifier_last_component(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
_cleanup_free_ char *prefix = NULL;
char *dash;
int r;
assert(u);
r = unit_name_to_prefix(u->id, &prefix);
if (r < 0)
return r;
dash = strrchr(prefix, '-');
if (dash)
return specifier_string(specifier, dash + 1, userdata, ret);
*ret = TAKE_PTR(prefix);
return 0;
}
static int specifier_last_component_unescaped(char specifier, void *data, void *userdata, char **ret) {
_cleanup_free_ char *p = NULL;
int r;
r = specifier_last_component(specifier, data, userdata, &p);
if (r < 0)
return r;
return unit_name_unescape(p, ret);
}
static int specifier_filename(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
@ -213,31 +244,33 @@ int unit_full_printf(Unit *u, const char *format, char **ret) {
*/
const Specifier table[] = {
{ 'n', specifier_string, u->id },
{ 'N', specifier_prefix_and_instance, NULL },
{ 'p', specifier_prefix, NULL },
{ 'P', specifier_prefix_unescaped, NULL },
{ 'i', specifier_string, u->instance },
{ 'I', specifier_instance_unescaped, NULL },
{ 'n', specifier_string, u->id },
{ 'N', specifier_prefix_and_instance, NULL },
{ 'p', specifier_prefix, NULL },
{ 'P', specifier_prefix_unescaped, NULL },
{ 'i', specifier_string, u->instance },
{ 'I', specifier_instance_unescaped, NULL },
{ 'j', specifier_last_component, NULL },
{ 'J', specifier_last_component_unescaped, NULL },
{ 'f', specifier_filename, NULL },
{ 'c', specifier_cgroup, NULL },
{ 'r', specifier_cgroup_slice, NULL },
{ 'R', specifier_cgroup_root, NULL },
{ 't', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_RUNTIME) },
{ 'S', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_STATE) },
{ 'C', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_CACHE) },
{ 'L', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_LOGS) },
{ 'f', specifier_filename, NULL },
{ 'c', specifier_cgroup, NULL },
{ 'r', specifier_cgroup_slice, NULL },
{ 'R', specifier_cgroup_root, NULL },
{ 't', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_RUNTIME) },
{ 'S', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_STATE) },
{ 'C', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_CACHE) },
{ 'L', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_LOGS) },
{ 'U', specifier_user_id, NULL },
{ 'u', specifier_user_name, NULL },
{ 'h', specifier_user_home, NULL },
{ 's', specifier_user_shell, NULL },
{ 'U', specifier_user_id, NULL },
{ 'u', specifier_user_name, NULL },
{ 'h', specifier_user_home, NULL },
{ 's', specifier_user_shell, NULL },
{ 'm', specifier_machine_id, NULL },
{ 'H', specifier_host_name, NULL },
{ 'b', specifier_boot_id, NULL },
{ 'v', specifier_kernel_release, NULL },
{ 'm', specifier_machine_id, NULL },
{ 'H', specifier_host_name, NULL },
{ 'b', specifier_boot_id, NULL },
{ 'v', specifier_kernel_release, NULL },
{}
};

View File

@ -15,6 +15,7 @@
#include "bus-error.h"
#include "bus-util.h"
#include "cgroup-util.h"
#include "conf-parser.h"
#include "fd-util.h"
#include "logind.h"
#include "parse-util.h"
@ -24,6 +25,48 @@
#include "udev-util.h"
#include "user-util.h"
void manager_reset_config(Manager *m) {
m->n_autovts = 6;
m->reserve_vt = 6;
m->remove_ipc = true;
m->inhibit_delay_max = 5 * USEC_PER_SEC;
m->handle_power_key = HANDLE_POWEROFF;
m->handle_suspend_key = HANDLE_SUSPEND;
m->handle_hibernate_key = HANDLE_HIBERNATE;
m->handle_lid_switch = HANDLE_SUSPEND;
m->handle_lid_switch_ep = _HANDLE_ACTION_INVALID;
m->handle_lid_switch_docked = HANDLE_IGNORE;
m->power_key_ignore_inhibited = false;
m->suspend_key_ignore_inhibited = false;
m->hibernate_key_ignore_inhibited = false;
m->lid_switch_ignore_inhibited = true;
m->holdoff_timeout_usec = 30 * USEC_PER_SEC;
m->idle_action_usec = 30 * USEC_PER_MINUTE;
m->idle_action = HANDLE_IGNORE;
m->runtime_dir_size = physical_memory_scale(10U, 100U); /* 10% */
m->user_tasks_max = system_tasks_max_scale(DEFAULT_USER_TASKS_MAX_PERCENTAGE, 100U); /* 33% */
m->sessions_max = 8192;
m->inhibitors_max = 8192;
m->kill_user_processes = KILL_USER_PROCESSES;
m->kill_only_users = strv_free(m->kill_only_users);
m->kill_exclude_users = strv_free(m->kill_exclude_users);
}
int manager_parse_config_file(Manager *m) {
assert(m);
return config_parse_many_nulstr(PKGSYSCONFDIR "/logind.conf",
CONF_PATHS_NULSTR("systemd/logind.conf.d"),
"Login\0",
config_item_perf_lookup, logind_gperf_lookup,
CONFIG_PARSE_WARN, m);
}
int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) {
Device *d;

View File

@ -332,6 +332,20 @@ static int property_get_current_inhibitors(
return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->inhibitors));
}
static int property_get_compat_user_tasks_max(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
assert(reply);
return sd_bus_message_append(reply, "t", CGROUP_LIMIT_MAX);
}
static int method_get_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *p = NULL;
Manager *m = userdata;
@ -2720,7 +2734,7 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_current_inhibitors, 0, 0),
SD_BUS_PROPERTY("SessionsMax", "t", NULL, offsetof(Manager, sessions_max), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_current_sessions, 0, 0),
SD_BUS_PROPERTY("UserTasksMax", "t", NULL, offsetof(Manager, user_tasks_max), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("UserTasksMax", "t", property_get_compat_user_tasks_max, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
@ -2980,78 +2994,6 @@ static int strdup_job(sd_bus_message *reply, char **job) {
return 1;
}
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_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
int r;
assert(manager);
assert(slice);
assert(job);
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;
return strdup_job(reply, job);
}
int manager_start_scope(
Manager *manager,
const char *scope,

View File

@ -17,27 +17,27 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
Login.NAutoVTs, config_parse_n_autovts, 0, offsetof(Manager, n_autovts)
Login.ReserveVT, config_parse_unsigned, 0, offsetof(Manager, reserve_vt)
Login.KillUserProcesses, config_parse_bool, 0, offsetof(Manager, kill_user_processes)
Login.KillOnlyUsers, config_parse_strv, 0, offsetof(Manager, kill_only_users)
Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users)
Login.InhibitDelayMaxSec, config_parse_sec, 0, offsetof(Manager, inhibit_delay_max)
Login.HandlePowerKey, config_parse_handle_action, 0, offsetof(Manager, handle_power_key)
Login.HandleSuspendKey, config_parse_handle_action, 0, offsetof(Manager, handle_suspend_key)
Login.HandleHibernateKey, config_parse_handle_action, 0, offsetof(Manager, handle_hibernate_key)
Login.HandleLidSwitch, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch)
Login.HandleLidSwitchExternalPower,config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch_ep)
Login.HandleLidSwitchDocked, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch_docked)
Login.PowerKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, power_key_ignore_inhibited)
Login.SuspendKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, suspend_key_ignore_inhibited)
Login.HibernateKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, hibernate_key_ignore_inhibited)
Login.LidSwitchIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, lid_switch_ignore_inhibited)
Login.HoldoffTimeoutSec, config_parse_sec, 0, offsetof(Manager, holdoff_timeout_usec)
Login.IdleAction, config_parse_handle_action, 0, offsetof(Manager, idle_action)
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.InhibitorsMax, config_parse_uint64, 0, offsetof(Manager, inhibitors_max)
Login.SessionsMax, config_parse_uint64, 0, offsetof(Manager, sessions_max)
Login.UserTasksMax, config_parse_user_tasks_max,0, offsetof(Manager, user_tasks_max)
Login.NAutoVTs, config_parse_n_autovts, 0, offsetof(Manager, n_autovts)
Login.ReserveVT, config_parse_unsigned, 0, offsetof(Manager, reserve_vt)
Login.KillUserProcesses, config_parse_bool, 0, offsetof(Manager, kill_user_processes)
Login.KillOnlyUsers, config_parse_strv, 0, offsetof(Manager, kill_only_users)
Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users)
Login.InhibitDelayMaxSec, config_parse_sec, 0, offsetof(Manager, inhibit_delay_max)
Login.HandlePowerKey, config_parse_handle_action, 0, offsetof(Manager, handle_power_key)
Login.HandleSuspendKey, config_parse_handle_action, 0, offsetof(Manager, handle_suspend_key)
Login.HandleHibernateKey, config_parse_handle_action, 0, offsetof(Manager, handle_hibernate_key)
Login.HandleLidSwitch, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch)
Login.HandleLidSwitchExternalPower, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch_ep)
Login.HandleLidSwitchDocked, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch_docked)
Login.PowerKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, power_key_ignore_inhibited)
Login.SuspendKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, suspend_key_ignore_inhibited)
Login.HibernateKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, hibernate_key_ignore_inhibited)
Login.LidSwitchIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, lid_switch_ignore_inhibited)
Login.HoldoffTimeoutSec, config_parse_sec, 0, offsetof(Manager, holdoff_timeout_usec)
Login.IdleAction, config_parse_handle_action, 0, offsetof(Manager, idle_action)
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.InhibitorsMax, config_parse_uint64, 0, offsetof(Manager, inhibitors_max)
Login.SessionsMax, config_parse_uint64, 0, offsetof(Manager, sessions_max)
Login.UserTasksMax, config_parse_compat_user_tasks_max, 0, offsetof(Manager, user_tasks_max)

View File

@ -319,85 +319,6 @@ int user_load(User *u) {
return r;
}
static int user_mkdir_runtime_path(User *u) {
int r;
assert(u);
r = mkdir_safe_label("/run/user", 0755, 0, 0, MKDIR_WARN_MODE);
if (r < 0)
return log_error_errno(r, "Failed to create /run/user: %m");
if (path_is_mount_point(u->runtime_path, NULL, 0) <= 0) {
_cleanup_free_ char *t = NULL;
r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu%s",
u->uid, u->gid, u->manager->runtime_dir_size,
mac_smack_use() ? ",smackfsroot=*" : "");
if (r < 0)
return log_oom();
(void) mkdir_label(u->runtime_path, 0700);
r = mount("tmpfs", u->runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, t);
if (r < 0) {
if (!IN_SET(errno, EPERM, EACCES)) {
r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", u->runtime_path);
goto fail;
}
log_debug_errno(errno, "Failed to mount per-user tmpfs directory %s, assuming containerized execution, ignoring: %m", u->runtime_path);
r = chmod_and_chown(u->runtime_path, 0700, u->uid, u->gid);
if (r < 0) {
log_error_errno(r, "Failed to change runtime directory ownership and mode: %m");
goto fail;
}
}
r = label_fix(u->runtime_path, 0);
if (r < 0)
log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", u->runtime_path);
}
return 0;
fail:
/* Try to clean up, but ignore errors */
(void) rmdir(u->runtime_path);
return r;
}
static int user_start_slice(User *u) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *description;
char *job;
int r;
assert(u);
u->slice_job = mfree(u->slice_job);
description = strjoina("User Slice of ", u->name);
r = manager_start_slice(
u->manager,
u->slice,
description,
"systemd-logind.service",
"systemd-user-sessions.service",
u->manager->user_tasks_max,
&error,
&job);
if (r >= 0)
u->slice_job = job;
else if (!sd_bus_error_has_name(&error, BUS_ERROR_UNIT_EXISTS))
/* we don't fail due to this, let's try to continue */
log_error_errno(r, "Failed to start user slice %s, ignoring: %s (%s)",
u->slice, bus_error_message(&error, r), error.name);
return 0;
}
static int user_start_service(User *u) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *job;
@ -444,20 +365,9 @@ int user_start(User *u) {
*/
u->stopping = false;
if (!u->started) {
if (!u->started)
log_debug("Starting services for new user %s.", u->name);
/* Make XDG_RUNTIME_DIR */
r = user_mkdir_runtime_path(u);
if (r < 0)
return r;
}
/* Create cgroup */
r = user_start_slice(u);
if (r < 0)
return r;
/* Save the user data so far, because pam_systemd will read the
* XDG_RUNTIME_DIR out of it while starting up systemd --user.
* We need to do user_save_internal() because we have not
@ -518,29 +428,6 @@ static int user_stop_service(User *u) {
return r;
}
static int user_remove_runtime_path(User *u) {
int r;
assert(u);
r = rm_rf(u->runtime_path, 0);
if (r < 0)
log_error_errno(r, "Failed to remove runtime directory %s (before unmounting): %m", u->runtime_path);
/* Ignore cases where the directory isn't mounted, as that's
* quite possible, if we lacked the permissions to mount
* something */
r = umount2(u->runtime_path, MNT_DETACH);
if (r < 0 && !IN_SET(errno, EINVAL, ENOENT))
log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
r = rm_rf(u->runtime_path, REMOVE_ROOT);
if (r < 0)
log_error_errno(r, "Failed to remove runtime directory %s (after unmounting): %m", u->runtime_path);
return r;
}
int user_stop(User *u, bool force) {
Session *s;
int r = 0, k;
@ -590,11 +477,6 @@ int user_finalize(User *u) {
r = k;
}
/* Kill XDG_RUNTIME_DIR */
k = user_remove_runtime_path(u);
if (k < 0)
r = k;
/* Clean SysV + POSIX IPC objects, but only if this is not a system user. Background: in many setups cronjobs
* are run in full PAM and thus logind sessions, even if the code run doesn't belong to actual users but to
* system components. Since enable RemoveIPC= globally for all users, we need to be a bit careful with such
@ -858,8 +740,8 @@ int config_parse_tmpfs_size(
return 0;
}
int config_parse_user_tasks_max(
const char* unit,
int config_parse_compat_user_tasks_max(
const char *unit,
const char *filename,
unsigned line,
const char *section,
@ -870,45 +752,17 @@ int config_parse_user_tasks_max(
void *data,
void *userdata) {
uint64_t *m = data;
uint64_t k;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
if (isempty(rvalue)) {
*m = system_tasks_max_scale(DEFAULT_USER_TASKS_MAX_PERCENTAGE, 100U);
return 0;
}
if (streq(rvalue, "infinity")) {
*m = CGROUP_LIMIT_MAX;
return 0;
}
/* Try to parse as percentage */
r = parse_percent(rvalue);
if (r >= 0)
k = system_tasks_max_scale(r, 100U);
else {
/* If the passed argument was not a percentage, or out of range, parse as byte size */
r = safe_atou64(rvalue, &k);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse tasks maximum, ignoring: %s", rvalue);
return 0;
}
}
if (k <= 0 || k >= UINT64_MAX) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Tasks maximum out of range, ignoring: %s", rvalue);
return 0;
}
*m = k;
log_syntax(unit, LOG_NOTICE, filename, line, 0,
"Support for option %s= has been removed.",
lvalue);
log_info("Hint: try creating /etc/systemd/system/user-.slice/50-limits.conf with:\n"
" [Slice]\n"
" TasksMax=%s",
rvalue);
return 0;
}

View File

@ -79,3 +79,5 @@ UserState user_state_from_string(const char *s) _pure_;
int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
int config_parse_compat_user_tasks_max(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);

View File

@ -33,38 +33,6 @@
static void manager_free(Manager *m);
static void manager_reset_config(Manager *m) {
m->n_autovts = 6;
m->reserve_vt = 6;
m->remove_ipc = true;
m->inhibit_delay_max = 5 * USEC_PER_SEC;
m->handle_power_key = HANDLE_POWEROFF;
m->handle_suspend_key = HANDLE_SUSPEND;
m->handle_hibernate_key = HANDLE_HIBERNATE;
m->handle_lid_switch = HANDLE_SUSPEND;
m->handle_lid_switch_ep = _HANDLE_ACTION_INVALID;
m->handle_lid_switch_docked = HANDLE_IGNORE;
m->power_key_ignore_inhibited = false;
m->suspend_key_ignore_inhibited = false;
m->hibernate_key_ignore_inhibited = false;
m->lid_switch_ignore_inhibited = true;
m->holdoff_timeout_usec = 30 * USEC_PER_SEC;
m->idle_action_usec = 30 * USEC_PER_MINUTE;
m->idle_action = HANDLE_IGNORE;
m->runtime_dir_size = physical_memory_scale(10U, 100U); /* 10% */
m->user_tasks_max = system_tasks_max_scale(DEFAULT_USER_TASKS_MAX_PERCENTAGE, 100U); /* 33% */
m->sessions_max = 8192;
m->inhibitors_max = 8192;
m->kill_user_processes = KILL_USER_PROCESSES;
m->kill_only_users = strv_free(m->kill_only_users);
m->kill_exclude_users = strv_free(m->kill_exclude_users);
}
static Manager *manager_new(void) {
Manager *m;
int r;
@ -1091,16 +1059,6 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us
return 0;
}
static int manager_parse_config_file(Manager *m) {
assert(m);
return config_parse_many_nulstr(PKGSYSCONFDIR "/logind.conf",
CONF_PATHS_NULSTR("systemd/logind.conf.d"),
"Login\0",
config_item_perf_lookup, logind_gperf_lookup,
CONFIG_PARSE_WARN, m);
}
static int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
Manager *m = userdata;
int r;

View File

@ -35,4 +35,3 @@
#RemoveIPC=yes
#InhibitorsMax=8192
#SessionsMax=8192
#UserTasksMax=33%

View File

@ -126,6 +126,9 @@ struct Manager {
uint64_t inhibitors_max;
};
void manager_reset_config(Manager *m);
int manager_parse_config_file(Manager *m);
int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device);
int manager_add_button(Manager *m, const char *name, Button **_button);
int manager_add_seat(Manager *m, const char *id, Seat **_seat);
@ -163,7 +166,6 @@ 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, 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);
@ -179,7 +181,6 @@ int manager_set_lid_switch_ignore(Manager *m, usec_t until);
int config_parse_n_autovts(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_tmpfs_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_user_tasks_max(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret);
int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret);

View File

@ -13,9 +13,6 @@ logind_gperf_c = custom_target(
output : 'logind-gperf.c',
command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@'])
systemd_logind_sources += [logind_gperf_c]
liblogind_core_sources = files('''
logind-core.c
logind-device.c
@ -42,6 +39,8 @@ liblogind_core_sources = files('''
logind-acl.h
'''.split())
liblogind_core_sources += [logind_gperf_c]
logind_acl_c = files('logind-acl.c')
if conf.get('HAVE_ACL') == 1
liblogind_core_sources += logind_acl_c
@ -59,6 +58,11 @@ loginctl_sources = files('''
sysfs-show.c
'''.split())
user_runtime_dir_sources = files('''
user-runtime-dir.c
logind.h
'''.split())
if conf.get('ENABLE_LOGIND') == 1
logind_conf = configure_file(
input : 'logind.conf.in',

View File

@ -0,0 +1,170 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <stdint.h>
#include <sys/mount.h>
#include "fs-util.h"
#include "label.h"
#include "logind.h"
#include "mkdir.h"
#include "mount-util.h"
#include "path-util.h"
#include "rm-rf.h"
#include "smack-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "user-util.h"
static int gather_configuration(size_t *runtime_dir_size) {
Manager m = {};
int r;
manager_reset_config(&m);
r = manager_parse_config_file(&m);
if (r < 0)
log_warning_errno(r, "Failed to parse logind.conf: %m");
*runtime_dir_size = m.runtime_dir_size;
return 0;
}
static int user_mkdir_runtime_path(const char *runtime_path, uid_t uid, gid_t gid, size_t runtime_dir_size) {
int r;
assert(runtime_path);
assert(path_is_absolute(runtime_path));
assert(uid_is_valid(uid));
assert(gid_is_valid(gid));
r = mkdir_safe_label("/run/user", 0755, 0, 0, MKDIR_WARN_MODE);
if (r < 0)
return log_error_errno(r, "Failed to create /run/user: %m");
if (path_is_mount_point(runtime_path, NULL, 0) >= 0)
log_debug("%s is already a mount point", runtime_path);
else {
char options[sizeof("mode=0700,uid=,gid=,size=,smackfsroot=*")
+ DECIMAL_STR_MAX(uid_t)
+ DECIMAL_STR_MAX(gid_t)
+ DECIMAL_STR_MAX(size_t)];
xsprintf(options,
"mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu%s",
uid, gid, runtime_dir_size,
mac_smack_use() ? ",smackfsroot=*" : "");
(void) mkdir_label(runtime_path, 0700);
r = mount("tmpfs", runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, options);
if (r < 0) {
if (!IN_SET(errno, EPERM, EACCES)) {
r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", runtime_path);
goto fail;
}
log_debug_errno(errno, "Failed to mount per-user tmpfs directory %s.\n"
"Assuming containerized execution, ignoring: %m", runtime_path);
r = chmod_and_chown(runtime_path, 0700, uid, gid);
if (r < 0) {
log_error_errno(r, "Failed to change ownership and mode of \"%s\": %m", runtime_path);
goto fail;
}
}
r = label_fix(runtime_path, 0);
if (r < 0)
log_warning_errno(r, "Failed to fix label of \"%s\", ignoring: %m", runtime_path);
}
return 0;
fail:
/* Try to clean up, but ignore errors */
(void) rmdir(runtime_path);
return r;
}
static int user_remove_runtime_path(const char *runtime_path) {
int r;
assert(runtime_path);
assert(path_is_absolute(runtime_path));
r = rm_rf(runtime_path, 0);
if (r < 0)
log_error_errno(r, "Failed to remove runtime directory %s (before unmounting): %m", runtime_path);
/* Ignore cases where the directory isn't mounted, as that's
* quite possible, if we lacked the permissions to mount
* something */
r = umount2(runtime_path, MNT_DETACH);
if (r < 0 && !IN_SET(errno, EINVAL, ENOENT))
log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", runtime_path);
r = rm_rf(runtime_path, REMOVE_ROOT);
if (r < 0)
log_error_errno(r, "Failed to remove runtime directory %s (after unmounting): %m", runtime_path);
return r;
}
static int do_mount(const char *runtime_path, uid_t uid, gid_t gid) {
size_t runtime_dir_size;
assert_se(gather_configuration(&runtime_dir_size) == 0);
log_debug("Will mount %s owned by "UID_FMT":"GID_FMT, runtime_path, uid, gid);
return user_mkdir_runtime_path(runtime_path, uid, gid, runtime_dir_size);
}
static int do_umount(const char *runtime_path) {
log_debug("Will remove %s", runtime_path);
return user_remove_runtime_path(runtime_path);
}
int main(int argc, char *argv[]) {
const char *user;
uid_t uid;
gid_t gid;
char runtime_path[sizeof("/run/user") + DECIMAL_STR_MAX(uid_t)];
int r;
log_parse_environment();
log_open();
if (argc != 3) {
log_error("This program takes two arguments.");
return EXIT_FAILURE;
}
if (!STR_IN_SET(argv[1], "start", "stop")) {
log_error("First argument must be either \"start\" or \"stop\".");
return EXIT_FAILURE;
}
umask(0022);
user = argv[2];
r = get_user_creds(&user, &uid, &gid, NULL, NULL);
if (r < 0) {
log_error_errno(r,
r == -ESRCH ? "No such user \"%s\"" :
r == -ENOMSG ? "UID \"%s\" is invalid or has an invalid main group"
: "Failed to look up user \"%s\": %m",
user);
return EXIT_FAILURE;
}
xsprintf(runtime_path, "/run/user/" UID_FMT, uid);
if (streq(argv[1], "start"))
r = do_mount(runtime_path, uid, gid);
else if (streq(argv[1], "stop"))
r = do_umount(runtime_path);
else
assert_not_reached("Unknown verb!");
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}

View File

@ -88,6 +88,27 @@ static int specifier_instance(char specifier, void *data, void *userdata, char *
return 0;
}
static int specifier_last_component(char specifier, void *data, void *userdata, char **ret) {
_cleanup_free_ char *prefix = NULL;
char *dash;
int r;
r = specifier_prefix(specifier, data, userdata, &prefix);
if (r < 0)
return r;
dash = strrchr(prefix, '-');
if (dash) {
dash = strdup(dash + 1);
if (!dash)
return -ENOMEM;
*ret = dash;
} else
*ret = TAKE_PTR(prefix);
return 0;
}
int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) {
/* This is similar to unit_full_printf() but does not support
@ -111,6 +132,7 @@ int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret)
{ 'N', specifier_prefix_and_instance, NULL },
{ 'p', specifier_prefix, NULL },
{ 'i', specifier_instance, NULL },
{ 'j', specifier_last_component, NULL },
{ 'U', specifier_user_id, NULL },
{ 'u', specifier_user_name, NULL },

View File

@ -253,13 +253,18 @@ static void test_controller_is_valid(void) {
static void test_slice_to_path_one(const char *unit, const char *path, int error) {
_cleanup_free_ char *ret = NULL;
int r;
assert_se(cg_slice_to_path(unit, &ret) == error);
log_info("unit: %s", unit);
r = cg_slice_to_path(unit, &ret);
log_info("actual: %s / %d", strnull(ret), r);
log_info("expect: %s / %d", strnull(path), error);
assert_se(r == error);
assert_se(streq_ptr(ret, path));
}
static void test_slice_to_path(void) {
test_slice_to_path_one("foobar.slice", "foobar.slice", 0);
test_slice_to_path_one("foobar-waldo.slice", "foobar.slice/foobar-waldo.slice", 0);
test_slice_to_path_one("foobar-waldo.service", NULL, -EINVAL);
@ -273,6 +278,15 @@ static void test_slice_to_path(void) {
test_slice_to_path_one("foo.slice/foo--bar.slice", NULL, -EINVAL);
test_slice_to_path_one("a-b.slice", "a.slice/a-b.slice", 0);
test_slice_to_path_one("a-b-c-d-e.slice", "a.slice/a-b.slice/a-b-c.slice/a-b-c-d.slice/a-b-c-d-e.slice", 0);
test_slice_to_path_one("foobar@.slice", NULL, -EINVAL);
test_slice_to_path_one("foobar@waldo.slice", NULL, -EINVAL);
test_slice_to_path_one("foobar@waldo.service", NULL, -EINVAL);
test_slice_to_path_one("-foo@-.slice", NULL, -EINVAL);
test_slice_to_path_one("-foo@.slice", NULL, -EINVAL);
test_slice_to_path_one("foo@-.slice", NULL, -EINVAL);
test_slice_to_path_one("foo@@bar.slice", NULL, -EINVAL);
test_slice_to_path_one("foo.slice/foo@@bar.slice", NULL, -EINVAL);
}
static void test_shift_path_one(const char *raw, const char *root, const char *shifted) {

View File

@ -662,6 +662,7 @@ static void test_install_printf(void) {
expect(i, "%N", "name");
expect(i, "%p", "name");
expect(i, "%i", "");
expect(i, "%j", "name");
expect(i, "%u", user);
expect(i, "%U", uid);

View File

@ -31,29 +31,39 @@
#include "util.h"
static void test_unit_name_is_valid(void) {
assert_se(unit_name_is_valid("foo.service", UNIT_NAME_ANY));
assert_se(unit_name_is_valid("foo.service", UNIT_NAME_PLAIN));
assert_se( unit_name_is_valid("foo.service", UNIT_NAME_ANY));
assert_se( unit_name_is_valid("foo.service", UNIT_NAME_PLAIN));
assert_se(!unit_name_is_valid("foo.service", UNIT_NAME_INSTANCE));
assert_se(!unit_name_is_valid("foo.service", UNIT_NAME_TEMPLATE));
assert_se(!unit_name_is_valid("foo.service", UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE));
assert_se(unit_name_is_valid("foo@bar.service", UNIT_NAME_ANY));
assert_se( unit_name_is_valid("foo@bar.service", UNIT_NAME_ANY));
assert_se(!unit_name_is_valid("foo@bar.service", UNIT_NAME_PLAIN));
assert_se(unit_name_is_valid("foo@bar.service", UNIT_NAME_INSTANCE));
assert_se( unit_name_is_valid("foo@bar.service", UNIT_NAME_INSTANCE));
assert_se(!unit_name_is_valid("foo@bar.service", UNIT_NAME_TEMPLATE));
assert_se(unit_name_is_valid("foo@bar.service", UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE));
assert_se( unit_name_is_valid("foo@bar.service", UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE));
assert_se(unit_name_is_valid("foo@.service", UNIT_NAME_ANY));
assert_se( unit_name_is_valid("foo@bar@bar.service", UNIT_NAME_ANY));
assert_se(!unit_name_is_valid("foo@bar@bar.service", UNIT_NAME_PLAIN));
assert_se( unit_name_is_valid("foo@bar@bar.service", UNIT_NAME_INSTANCE));
assert_se(!unit_name_is_valid("foo@bar@bar.service", UNIT_NAME_TEMPLATE));
assert_se( unit_name_is_valid("foo@bar@bar.service", UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE));
assert_se( unit_name_is_valid("foo@.service", UNIT_NAME_ANY));
assert_se(!unit_name_is_valid("foo@.service", UNIT_NAME_PLAIN));
assert_se(!unit_name_is_valid("foo@.service", UNIT_NAME_INSTANCE));
assert_se(unit_name_is_valid("foo@.service", UNIT_NAME_TEMPLATE));
assert_se(unit_name_is_valid("foo@.service", UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE));
assert_se( unit_name_is_valid("foo@.service", UNIT_NAME_TEMPLATE));
assert_se( unit_name_is_valid("foo@.service", UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE));
assert_se(!unit_name_is_valid(".service", UNIT_NAME_ANY));
assert_se(!unit_name_is_valid("", UNIT_NAME_ANY));
assert_se(!unit_name_is_valid("foo.waldo", UNIT_NAME_ANY));
assert_se(!unit_name_is_valid("@.service", UNIT_NAME_ANY));
assert_se(!unit_name_is_valid("@piep.service", UNIT_NAME_ANY));
assert_se( unit_name_is_valid("user@1000.slice", UNIT_NAME_ANY));
assert_se( unit_name_is_valid("user@1000.slice", UNIT_NAME_INSTANCE));
assert_se(!unit_name_is_valid("user@1000.slice", UNIT_NAME_TEMPLATE));
}
static void test_unit_name_replace_instance_one(const char *pattern, const char *repl, const char *expected, int ret) {
@ -188,7 +198,7 @@ static void test_unit_name_mangle(void) {
static int test_unit_printf(void) {
_cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *uid = NULL, *user = NULL, *shell = NULL, *home = NULL;
_cleanup_(manager_freep) Manager *m = NULL;
Unit *u, *u2;
Unit *u;
int r;
assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
@ -235,6 +245,9 @@ static int test_unit_printf(void) {
expect(u, "%p", "blah");
expect(u, "%P", "blah");
expect(u, "%i", "");
expect(u, "%I", "");
expect(u, "%j", "blah");
expect(u, "%J", "blah");
expect(u, "%u", user);
expect(u, "%U", uid);
expect(u, "%h", home);
@ -244,24 +257,41 @@ static int test_unit_printf(void) {
expect(u, "%t", "/run/user/*");
/* templated */
assert_se(u2 = unit_new(m, sizeof(Service)));
assert_se(unit_add_name(u2, "blah@foo-foo.service") == 0);
assert_se(unit_add_name(u2, "blah@foo-foo.service") == 0);
assert_se(u = unit_new(m, sizeof(Service)));
assert_se(unit_add_name(u, "blah@foo-foo.service") == 0);
assert_se(unit_add_name(u, "blah@foo-foo.service") == 0);
expect(u, "%n", "blah@foo-foo.service");
expect(u, "%N", "blah@foo-foo");
expect(u, "%f", "/foo/foo");
expect(u, "%p", "blah");
expect(u, "%P", "blah");
expect(u, "%i", "foo-foo");
expect(u, "%I", "foo/foo");
expect(u, "%j", "blah");
expect(u, "%J", "blah");
expect(u, "%u", user);
expect(u, "%U", uid);
expect(u, "%h", home);
expect(u, "%m", mid);
expect(u, "%b", bid);
expect(u, "%H", host);
expect(u, "%t", "/run/user/*");
/* templated with components */
assert_se(u = unit_new(m, sizeof(Slice)));
assert_se(unit_add_name(u, "blah-blah\\x2d.slice") == 0);
expect(u, "%n", "blah-blah\\x2d.slice");
expect(u, "%N", "blah-blah\\x2d");
expect(u, "%f", "/blah/blah-");
expect(u, "%p", "blah-blah\\x2d");
expect(u, "%P", "blah/blah-");
expect(u, "%i", "");
expect(u, "%I", "");
expect(u, "%j", "blah\\x2d");
expect(u, "%J", "blah-");
expect(u2, "%n", "blah@foo-foo.service");
expect(u2, "%N", "blah@foo-foo");
expect(u2, "%f", "/foo/foo");
expect(u2, "%p", "blah");
expect(u2, "%P", "blah");
expect(u2, "%i", "foo-foo");
expect(u2, "%I", "foo/foo");
expect(u2, "%u", user);
expect(u2, "%U", uid);
expect(u2, "%h", home);
expect(u2, "%m", mid);
expect(u2, "%b", bid);
expect(u2, "%H", host);
expect(u2, "%t", "/run/user/*");
#undef expect
return 0;
@ -323,10 +353,10 @@ static void test_unit_name_build(void) {
}
static void test_slice_name_is_valid(void) {
assert_se(slice_name_is_valid(SPECIAL_ROOT_SLICE));
assert_se(slice_name_is_valid("foo.slice"));
assert_se(slice_name_is_valid("foo-bar.slice"));
assert_se(slice_name_is_valid("foo-bar-baz.slice"));
assert_se( slice_name_is_valid(SPECIAL_ROOT_SLICE));
assert_se( slice_name_is_valid("foo.slice"));
assert_se( slice_name_is_valid("foo-bar.slice"));
assert_se( slice_name_is_valid("foo-bar-baz.slice"));
assert_se(!slice_name_is_valid("-foo-bar-baz.slice"));
assert_se(!slice_name_is_valid("foo-bar-baz-.slice"));
assert_se(!slice_name_is_valid("-foo-bar-baz-.slice"));
@ -335,6 +365,20 @@ static void test_slice_name_is_valid(void) {
assert_se(!slice_name_is_valid(".slice"));
assert_se(!slice_name_is_valid(""));
assert_se(!slice_name_is_valid("foo.service"));
assert_se(!slice_name_is_valid("foo@.slice"));
assert_se(!slice_name_is_valid("foo@bar.slice"));
assert_se(!slice_name_is_valid("foo-bar@baz.slice"));
assert_se(!slice_name_is_valid("foo@bar@baz.slice"));
assert_se(!slice_name_is_valid("foo@bar-baz.slice"));
assert_se(!slice_name_is_valid("-foo-bar-baz@.slice"));
assert_se(!slice_name_is_valid("foo-bar-baz@-.slice"));
assert_se(!slice_name_is_valid("foo-bar-baz@a--b.slice"));
assert_se(!slice_name_is_valid("-foo-bar-baz@-.slice"));
assert_se(!slice_name_is_valid("foo-bar--baz@.slice"));
assert_se(!slice_name_is_valid("foo--bar--baz@.slice"));
assert_se(!slice_name_is_valid("@.slice"));
assert_se(!slice_name_is_valid("foo@bar.service"));
}
static void test_build_subslice(void) {
@ -372,6 +416,13 @@ static void test_build_parent_slice(void) {
test_build_parent_slice_one("foo-bar-.slice", NULL, -EINVAL);
test_build_parent_slice_one("foo-bar.service", NULL, -EINVAL);
test_build_parent_slice_one(".slice", NULL, -EINVAL);
test_build_parent_slice_one("foo@bar.slice", NULL, -EINVAL);
test_build_parent_slice_one("foo-bar@baz.slice", NULL, -EINVAL);
test_build_parent_slice_one("foo-bar--@baz.slice", NULL, -EINVAL);
test_build_parent_slice_one("-foo-bar@bar.slice", NULL, -EINVAL);
test_build_parent_slice_one("foo-bar@-.slice", NULL, -EINVAL);
test_build_parent_slice_one("foo@bar.service", NULL, -EINVAL);
test_build_parent_slice_one("@.slice", NULL, -EINVAL);
}
static void test_unit_name_to_instance(void) {

View File

@ -218,6 +218,7 @@ in_units = [
'multi-user.target.wants/'],
['systemd-vconsole-setup.service', 'ENABLE_VCONSOLE'],
['systemd-volatile-root.service', ''],
['user-runtime-dir@.service', ''],
['user@.service', ''],
]
@ -304,6 +305,9 @@ foreach tuple : units
endif
endforeach
install_data('user-.slice.d/10-defaults.conf',
install_dir : systemunitdir + '/user-.slice.d')
############################################################
meson.add_install_script(meson_make_symlink,

View File

@ -0,0 +1,15 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# This file is part of systemd.
#
# systemd 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.
[Unit]
Description=User Slice of UID %j
After=systemd-user-sessions.service
[Slice]
TasksMax=33%

View File

@ -0,0 +1,17 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# This file is part of systemd.
#
# systemd 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.
[Unit]
Description=/run/user/%i mount wrapper
StopWhenUnneeded=yes
[Service]
ExecStart=@rootlibexecdir@/systemd-user-runtime-dir start %i
ExecStop=@rootlibexecdir@/systemd-user-runtime-dir stop %i
RemainAfterExit=true

View File

@ -10,6 +10,8 @@
[Unit]
Description=User Manager for UID %i
After=systemd-user-sessions.service
After=user-runtime-dir@%i.service
Requires=user-runtime-dir@%i.service
[Service]
User=%i