Compare commits
8 Commits
4047a411f4
...
31c68e0277
Author | SHA1 | Date |
---|---|---|
Benjamin Berg | 31c68e0277 | |
Sean-StarLabs | b642dfcdc2 | |
Tom | 90c40df0a6 | |
Balint Reczey | 93c23c9297 | |
Wen Yang | acd1987a18 | |
Zhu Li | 9520a0308d | |
Franck Bui | e730db6c90 | |
Pieter Lexis | 72e1c0b308 |
|
@ -0,0 +1,102 @@
|
||||||
|
---
|
||||||
|
title: Desktop Environment Integration
|
||||||
|
category: Concepts
|
||||||
|
layout: default
|
||||||
|
---
|
||||||
|
|
||||||
|
# Desktop Environments
|
||||||
|
|
||||||
|
NOTE: This document is a work-in-progress.
|
||||||
|
|
||||||
|
## Single Graphical Session
|
||||||
|
|
||||||
|
systemd only supports running one graphical session per user at a time.
|
||||||
|
While this might not have always been the case historically, having multiple
|
||||||
|
sessions for one user running at the same time is problematic.
|
||||||
|
The DBus session bus is shared between all the logins, and services that are
|
||||||
|
started must be implicitly assigned to the user's current graphical session.
|
||||||
|
|
||||||
|
In principle it is possible to run a single graphical session across multiple
|
||||||
|
logind seats, and this could be a way to use more than one display per user.
|
||||||
|
When a user logs in to a second seat, the seat resources could be assigned
|
||||||
|
to the existing session, allowing the graphical environment to present it
|
||||||
|
is a single seat.
|
||||||
|
Currently nothing like this is supported or even planned.
|
||||||
|
|
||||||
|
## Pre-defined systemd units
|
||||||
|
|
||||||
|
[`systemd.special(7)`](https://www.freedesktop.org/software/systemd/man/systemd.special.html)
|
||||||
|
defines the `graphical-session.target` and `graphical-session-pre.target` to
|
||||||
|
allow cross-desktop integration. Furthermore, systemd defines the three base
|
||||||
|
slices `background`, `apps` and `session`.
|
||||||
|
All units should be placed into one of these slices depending on their purposes:
|
||||||
|
|
||||||
|
* `session.slice`: Contains only processes essential to run the user's graphical session
|
||||||
|
* `apps.slice`: Contains all normal applications that the user is running
|
||||||
|
* `background.slice`: Useful for low-priority background tasks
|
||||||
|
|
||||||
|
The purpose of this grouping is to assign different priorities to the
|
||||||
|
applications.
|
||||||
|
This could e.g. mean reserving memory to session processes,
|
||||||
|
preferentially killing background tasks in out-of-memory situations
|
||||||
|
or assinging different memory/CPU/IO priorities to ensure that the session
|
||||||
|
runs smoothly under load.
|
||||||
|
|
||||||
|
TODO: Will there be a default to place units into e.g. `apps.slice` by default
|
||||||
|
rather than the root slice?
|
||||||
|
|
||||||
|
## XDG standardization for applications
|
||||||
|
|
||||||
|
To ensure cross-desktop compatibility and encourage sharing of good practices,
|
||||||
|
desktop environments should adhere to the following conventions:
|
||||||
|
|
||||||
|
* Application units should follow the scheme `apps-<launcher>-<ApplicationID>-<RANDOM>.service`,
|
||||||
|
e.g. `apps-gnome-org.gnome.Evince-12345.service`,
|
||||||
|
`apps-flatpak-org.telegram.desktop-12345.service` or `apps-KDE-org.kde.okular-12345.service`.
|
||||||
|
* Using `.service` units instead of `.scope` units, i.e. allowing systemd to
|
||||||
|
start the process on behalf of the caller,
|
||||||
|
instead of the caller starting the process and letting systemd know about it,
|
||||||
|
is encouraged.
|
||||||
|
* If no application ID is available, the launcher should generate a reasonable
|
||||||
|
name when possible (e.g. using `basename(argv[0])`). This name must not
|
||||||
|
contain a `-` character.
|
||||||
|
|
||||||
|
This has the following advantages:
|
||||||
|
* Using the `apps-<launcher>-` prefix means that the unit defaults can be
|
||||||
|
adjusted using desktop environment specific drop-in files.
|
||||||
|
* The application ID can be retrieved by stripping the prefix and postfix.
|
||||||
|
This in turn should map to the corresponding `.desktop` file when available
|
||||||
|
|
||||||
|
TODO: Define the name of slices that should be used.
|
||||||
|
This could be `apps-<launcher>-<ApplicationID>-<RANDOM>.slice`.
|
||||||
|
|
||||||
|
TODO: Does it really make sense to insert the `<launcher>`? In GNOME I am
|
||||||
|
currently using a drop-in to configure `BindTo=graphical-session.target`,
|
||||||
|
`CollectMode=inactive-or-failed` and `TimeoutSec=5s`. I feel that such a
|
||||||
|
policy makes sense, but it may make much more sense to just define a
|
||||||
|
global default for all (graphical) applications.
|
||||||
|
|
||||||
|
* Should application lifetime be bound to the session?
|
||||||
|
* May the user have applications that do not belong to the graphical session (e.g. launched from SSH)?
|
||||||
|
* Could we maybe add a default `apps-.service.d` drop-in configuration?
|
||||||
|
|
||||||
|
## XDG autostart integration
|
||||||
|
|
||||||
|
To allow XDG autostart integration, systemd will ship a cross-desktop generator
|
||||||
|
to create appropriate units for the autostart directory.
|
||||||
|
Desktop Environments will be able to make use of this simply by starting the
|
||||||
|
appropriate XDG related targets (representing e.g. content of the
|
||||||
|
`$XDG_CURRENT_DESKTOP` environment variable to handle `OnlyShowIn/NotShowIn`).
|
||||||
|
The names and ordering rules for these targets are to be defined.
|
||||||
|
|
||||||
|
This generator will likely never support certain desktop specific extensions.
|
||||||
|
One such example is the GNOME specific feature to bind a service to a settings
|
||||||
|
variable.
|
||||||
|
|
||||||
|
## Startup and shutdown best practices
|
||||||
|
|
||||||
|
Question here are:
|
||||||
|
|
||||||
|
* Are there strong opinions on how the session-leader process should watch the user's session units?
|
||||||
|
* Should systemd/logind/… provide an integrated way to define a session in terms of a running *user* unit?
|
||||||
|
* Is having `gnome-session-shutdown.target` that is run with `replace-irreversibly` considered a good practice?
|
|
@ -571,6 +571,24 @@ evdev:name:ETPS/2 Elantech Touchpad:dmi:*svnSAMSUNGELECTRONICSCO.,LTD.:pn870Z5E/
|
||||||
EVDEV_ABS_35=::30
|
EVDEV_ABS_35=::30
|
||||||
EVDEV_ABS_36=::29
|
EVDEV_ABS_36=::29
|
||||||
|
|
||||||
|
#########################################
|
||||||
|
# Star Labs
|
||||||
|
#########################################
|
||||||
|
|
||||||
|
# Star LabTop Mk III
|
||||||
|
evdev:name:ALPS0001:00 0911:5288 Touchpad:dmi:*svnStarLabs:pnLabTop*
|
||||||
|
EVDEV_ABS_00=0:2627:25
|
||||||
|
EVDEV_ABS_01=0:1331:20
|
||||||
|
EVDEV_ABS_35=0:2627:25
|
||||||
|
EVDEV_ABS_36=0:1331:20
|
||||||
|
|
||||||
|
# Star Lite Mk II
|
||||||
|
evdev:name:ALPS0001:00 0911:5288 Touchpad:dmi:*svnStarLabs:pnLite:*
|
||||||
|
EVDEV_ABS_00=55:1750:16
|
||||||
|
EVDEV_ABS_01=51:950:15
|
||||||
|
EVDEV_ABS_35=55:1750:16
|
||||||
|
EVDEV_ABS_36=51:950:15
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
# System76
|
# System76
|
||||||
#########################################
|
#########################################
|
||||||
|
|
|
@ -407,6 +407,10 @@ sensor:modalias:acpi:SMO8500*:dmi:bvnLENOVO:*:pvrLenovoMIIX3-830:*
|
||||||
sensor:modalias:acpi:BOSC0200*:dmi:*:svnLENOVO:pn81H3:*
|
sensor:modalias:acpi:BOSC0200*:dmi:*:svnLENOVO:pn81H3:*
|
||||||
ACCEL_MOUNT_MATRIX=0, 1, 0; -1, 0, 0; 0, 0, 1
|
ACCEL_MOUNT_MATRIX=0, 1, 0; -1, 0, 0; 0, 0, 1
|
||||||
|
|
||||||
|
# IdeaPad Miix 300
|
||||||
|
sensor:modalias:acpi:SMO8500*:dmi:bvnLENOVO:*:pvrMIIX300-*
|
||||||
|
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
|
||||||
|
|
||||||
# IdeaPad Miix 310 note this only is for BIOS version (bvr) 1HCN4?WW and 1HCN2?WW, which has
|
# IdeaPad Miix 310 note this only is for BIOS version (bvr) 1HCN4?WW and 1HCN2?WW, which has
|
||||||
# a portrait LCD panel, versions with bvr 1HCN3?WW have a landscape panel
|
# a portrait LCD panel, versions with bvr 1HCN3?WW have a landscape panel
|
||||||
sensor:modalias:acpi:KIOX000A*:dmi:bvnLENOVO:bvr1HCN4?WW:*:svnLENOVO:pn80SG:*
|
sensor:modalias:acpi:KIOX000A*:dmi:bvnLENOVO:bvr1HCN4?WW:*:svnLENOVO:pn80SG:*
|
||||||
|
|
|
@ -217,8 +217,7 @@
|
||||||
this notification message has been sent. If this option is used, <varname>NotifyAccess=</varname> (see
|
this notification message has been sent. If this option is used, <varname>NotifyAccess=</varname> (see
|
||||||
below) should be set to open access to the notification socket provided by systemd. If
|
below) should be set to open access to the notification socket provided by systemd. If
|
||||||
<varname>NotifyAccess=</varname> is missing or set to <option>none</option>, it will be forcibly set to
|
<varname>NotifyAccess=</varname> is missing or set to <option>none</option>, it will be forcibly set to
|
||||||
<option>main</option>. Note that currently <varname>Type=</varname><option>notify</option> will not work if
|
<option>main</option></para></listitem>.
|
||||||
used in combination with <varname>PrivateNetwork=</varname><option>yes</option>.</para></listitem>
|
|
||||||
|
|
||||||
<listitem><para>Behavior of <option>idle</option> is very similar to <option>simple</option>; however,
|
<listitem><para>Behavior of <option>idle</option> is very similar to <option>simple</option>; however,
|
||||||
actual execution of the service program is delayed until all active jobs are dispatched. This may be used
|
actual execution of the service program is delayed until all active jobs are dispatched. This may be used
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
"start:Start container as a service"
|
"start:Start container as a service"
|
||||||
"stop:Stop container (equal to poweroff)"
|
"stop:Stop container (equal to poweroff)"
|
||||||
"login:Get a login prompt on a VM/container"
|
"login:Get a login prompt on a VM/container"
|
||||||
|
"shell:Invoke a shell (or other command) in a container"
|
||||||
"enable:Enable automatic container start at boot"
|
"enable:Enable automatic container start at boot"
|
||||||
"disable:Disable automatic container start at boot"
|
"disable:Disable automatic container start at boot"
|
||||||
"poweroff:Power off one or more VMs/containers"
|
"poweroff:Power off one or more VMs/containers"
|
||||||
|
|
|
@ -701,16 +701,18 @@ int take_etc_passwd_lock(const char *root) {
|
||||||
bool valid_user_group_name_full(const char *u, bool strict) {
|
bool valid_user_group_name_full(const char *u, bool strict) {
|
||||||
const char *i;
|
const char *i;
|
||||||
long sz;
|
long sz;
|
||||||
|
bool warned = false;
|
||||||
|
|
||||||
/* Checks if the specified name is a valid user/group name. Also see POSIX IEEE Std 1003.1-2008, 2016 Edition,
|
/* Checks if the specified name is a valid user/group name. Also see POSIX IEEE Std 1003.1-2008, 2016 Edition,
|
||||||
* 3.437. We are a bit stricter here however. Specifically we deviate from POSIX rules:
|
* 3.437. We are a bit stricter here however. Specifically we deviate from POSIX rules:
|
||||||
*
|
*
|
||||||
* - We require that names fit into the appropriate utmp field
|
* - We require that names fit into the appropriate utmp field
|
||||||
* - We don't allow empty user names
|
* - We don't allow empty user names
|
||||||
* - No dots or digits in the first character
|
* - No dots in the first character
|
||||||
*
|
*
|
||||||
* If strict==true, additionally:
|
* If strict==true, additionally:
|
||||||
* - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator)
|
* - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator)
|
||||||
|
* - We don't allow a digit as the first character
|
||||||
*
|
*
|
||||||
* Note that other systems are even more restrictive, and don't permit underscores or uppercase characters.
|
* Note that other systems are even more restrictive, and don't permit underscores or uppercase characters.
|
||||||
*/
|
*/
|
||||||
|
@ -720,17 +722,26 @@ bool valid_user_group_name_full(const char *u, bool strict) {
|
||||||
|
|
||||||
if (!(u[0] >= 'a' && u[0] <= 'z') &&
|
if (!(u[0] >= 'a' && u[0] <= 'z') &&
|
||||||
!(u[0] >= 'A' && u[0] <= 'Z') &&
|
!(u[0] >= 'A' && u[0] <= 'Z') &&
|
||||||
|
!(u[0] >= '0' && u[0] <= '9' && !strict) &&
|
||||||
u[0] != '_')
|
u[0] != '_')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool warned = false;
|
bool only_digits_seen = u[0] >= '0' && u[0] <= '9';
|
||||||
|
|
||||||
|
if (only_digits_seen) {
|
||||||
|
log_warning("User or group name \"%s\" starts with a digit, accepting for compatibility.", u);
|
||||||
|
warned = true;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = u+1; *i; i++) {
|
for (i = u+1; *i; i++) {
|
||||||
if (((*i >= 'a' && *i <= 'z') ||
|
if (((*i >= 'a' && *i <= 'z') ||
|
||||||
(*i >= 'A' && *i <= 'Z') ||
|
(*i >= 'A' && *i <= 'Z') ||
|
||||||
(*i >= '0' && *i <= '9') ||
|
(*i >= '0' && *i <= '9') ||
|
||||||
IN_SET(*i, '_', '-')))
|
IN_SET(*i, '_', '-'))) {
|
||||||
|
if (!(*i >= '0' && *i <= '9'))
|
||||||
|
only_digits_seen = false;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (*i == '.' && !strict) {
|
if (*i == '.' && !strict) {
|
||||||
if (!warned) {
|
if (!warned) {
|
||||||
|
@ -744,6 +755,9 @@ bool valid_user_group_name_full(const char *u, bool strict) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (only_digits_seen)
|
||||||
|
return false;
|
||||||
|
|
||||||
sz = sysconf(_SC_LOGIN_NAME_MAX);
|
sz = sysconf(_SC_LOGIN_NAME_MAX);
|
||||||
assert_se(sz > 0);
|
assert_se(sz > 0);
|
||||||
|
|
||||||
|
|
|
@ -210,11 +210,13 @@ int unit_add_name(Unit *u, const char *text) {
|
||||||
if (unit_name_is_valid(text, UNIT_NAME_TEMPLATE)) {
|
if (unit_name_is_valid(text, UNIT_NAME_TEMPLATE)) {
|
||||||
|
|
||||||
if (!u->instance)
|
if (!u->instance)
|
||||||
return -EINVAL;
|
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"instance is not set when adding name '%s': %m", text);
|
||||||
|
|
||||||
r = unit_name_replace_instance(text, u->instance, &s);
|
r = unit_name_replace_instance(text, u->instance, &s);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return log_unit_debug_errno(u, r,
|
||||||
|
"failed to build instance name from '%s': %m", text);
|
||||||
} else {
|
} else {
|
||||||
s = strdup(text);
|
s = strdup(text);
|
||||||
if (!s)
|
if (!s)
|
||||||
|
@ -224,36 +226,43 @@ int unit_add_name(Unit *u, const char *text) {
|
||||||
if (set_contains(u->names, s))
|
if (set_contains(u->names, s))
|
||||||
return 0;
|
return 0;
|
||||||
if (hashmap_contains(u->manager->units, s))
|
if (hashmap_contains(u->manager->units, s))
|
||||||
return -EEXIST;
|
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EEXIST),
|
||||||
|
"unit already exist when adding name '%s': %m", text);
|
||||||
|
|
||||||
if (!unit_name_is_valid(s, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
|
if (!unit_name_is_valid(s, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
|
||||||
return -EINVAL;
|
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"name '%s' is invalid: %m", text);
|
||||||
|
|
||||||
t = unit_name_to_type(s);
|
t = unit_name_to_type(s);
|
||||||
if (t < 0)
|
if (t < 0)
|
||||||
return -EINVAL;
|
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"failed to to derive unit type from name '%s': %m", text);
|
||||||
|
|
||||||
if (u->type != _UNIT_TYPE_INVALID && t != u->type)
|
if (u->type != _UNIT_TYPE_INVALID && t != u->type)
|
||||||
return -EINVAL;
|
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"unit type is illegal: u->type(%d) and t(%d) for name '%s': %m",
|
||||||
|
u->type, t, text);
|
||||||
|
|
||||||
r = unit_name_to_instance(s, &i);
|
r = unit_name_to_instance(s, &i);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return log_unit_debug_errno(u, r, "failed to extract instance from name '%s': %m", text);
|
||||||
|
|
||||||
if (i && !unit_type_may_template(t))
|
if (i && !unit_type_may_template(t))
|
||||||
return -EINVAL;
|
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL), "templates are not allowed for name '%s': %m", text);
|
||||||
|
|
||||||
/* Ensure that this unit is either instanced or not instanced,
|
/* Ensure that this unit is either instanced or not instanced,
|
||||||
* but not both. Note that we do allow names with different
|
* but not both. Note that we do allow names with different
|
||||||
* instance names however! */
|
* instance names however! */
|
||||||
if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i)
|
if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i)
|
||||||
return -EINVAL;
|
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"instance is illegal: u->type(%d), u->instance(%s) and i(%s) for name '%s': %m",
|
||||||
|
u->type, u->instance, i, text);
|
||||||
|
|
||||||
if (!unit_type_may_alias(t) && !set_isempty(u->names))
|
if (!unit_type_may_alias(t) && !set_isempty(u->names))
|
||||||
return -EEXIST;
|
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EEXIST), "symlinks are not allowed for name '%s': %m", text);
|
||||||
|
|
||||||
if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES)
|
if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES)
|
||||||
return -E2BIG;
|
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(E2BIG), "too many units: %m");
|
||||||
|
|
||||||
r = set_put(u->names, s);
|
r = set_put(u->names, s);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -263,7 +272,7 @@ int unit_add_name(Unit *u, const char *text) {
|
||||||
r = hashmap_put(u->manager->units, s, u);
|
r = hashmap_put(u->manager->units, s, u);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
(void) set_remove(u->names, s);
|
(void) set_remove(u->names, s);
|
||||||
return r;
|
return log_unit_debug_errno(u, r, "add unit to hashmap failed for name '%s': %m", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (u->type == _UNIT_TYPE_INVALID) {
|
if (u->type == _UNIT_TYPE_INVALID) {
|
||||||
|
|
|
@ -96,7 +96,7 @@ static void test_valid_user_group_name_compat(void) {
|
||||||
assert_se(valid_user_group_name_compat("eff."));
|
assert_se(valid_user_group_name_compat("eff."));
|
||||||
|
|
||||||
assert_se(valid_user_group_name_compat("some5"));
|
assert_se(valid_user_group_name_compat("some5"));
|
||||||
assert_se(!valid_user_group_name_compat("5some"));
|
assert_se(valid_user_group_name_compat("5some"));
|
||||||
assert_se(valid_user_group_name_compat("INNER5NUMBER"));
|
assert_se(valid_user_group_name_compat("INNER5NUMBER"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +166,7 @@ static void test_valid_user_group_name_or_id_compat(void) {
|
||||||
assert_se(valid_user_group_name_or_id_compat("kk-k"));
|
assert_se(valid_user_group_name_or_id_compat("kk-k"));
|
||||||
|
|
||||||
assert_se(valid_user_group_name_or_id_compat("some5"));
|
assert_se(valid_user_group_name_or_id_compat("some5"));
|
||||||
assert_se(!valid_user_group_name_or_id_compat("5some"));
|
assert_se(valid_user_group_name_or_id_compat("5some"));
|
||||||
assert_se(valid_user_group_name_or_id_compat("INNER5NUMBER"));
|
assert_se(valid_user_group_name_or_id_compat("INNER5NUMBER"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ Description=Generate network units from Kernel command line
|
||||||
Documentation=man:systemd-network-generator.service(8)
|
Documentation=man:systemd-network-generator.service(8)
|
||||||
DefaultDependencies=no
|
DefaultDependencies=no
|
||||||
Before=network-pre.target
|
Before=network-pre.target
|
||||||
|
Wants=network-pre.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -19,4 +20,4 @@ RemainAfterExit=yes
|
||||||
ExecStart=@rootlibexecdir@/systemd-network-generator
|
ExecStart=@rootlibexecdir@/systemd-network-generator
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=network-pre.target
|
WantedBy=sysinit.target
|
||||||
|
|
Loading…
Reference in New Issue