Skip to content

Commit d28e629

Browse files
committed
fix: prepare for removal of transient session vars from activation environment
In future releases the following variables will be omitted: XDG_VTNR, XDG_SESSION_ID, XDG_SESSION_PATH, XDG_SEAT, XDG_SEAT_PATH. These are transient variables that make sense only for a specific session among others (graphical or otherwise) and should not be placed in activation environment. This is in contrast to vars like XDG_CURERNT_DESKTOP which are relevant to one and only graphical session and may inform other sessions and environments on what's going on there. The problem though is that those vars are useful to have at hand. This commit creates alternative way of propagaitng them to compositor and further: * compositor's service `EnvironmentFile=` directive and a file for it. * `uwsm app` now transmits them via `--setenv` arg to service units it launches. Currently overall behavior is unchanged by default, the new mechanism is redundant. In future releases these vars will be moved from `always_export` set to `never_export` and `always_unset` sets. This behavior can be tested by adding `UWSM_NO_SESSION_SPECIFIC_VARS=true` directly to activation environment before `uwsm start`. Reference for var exclusion in GNOME and MATE session managers: https://gitlab.gnome.org/GNOME/gnome-session/-/issues/86 mate-desktop/mate-session-manager#282
1 parent 323a8ae commit d28e629

5 files changed

Lines changed: 83 additions & 20 deletions

File tree

man/uwsm.1.scd

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,18 @@ be used for distro level defaults.
9292
| *UWSM_SILENT_START*
9393
: (int or boolean value)
9494
|
95-
: _True_ or *1* to inhibit stdout messages from *uwsm start*. *2* to also
95+
: _True_ or *1* to inhibit stdout messages from "*uwsm start*". *2* to also
9696
inhibit warnings.
97+
| *UWSM_NO_SESSION_SPECIFIC_VARS*
98+
: (boolean value)
99+
|
100+
: Set _True_ to test future behavior: omit transient session vars:
101+
*XDG_SEAT*, *XDG_SEAT_PATH*, *XDG_SESSION_ID*, *XDG_SESSION_PATH*, *XDG_VTNR*
102+
from activation environment and load them via env file.
103+
(!) Should be set in systemd activation environment before starting uwsm
104+
to be seen by environment preloader
105+
("*systemctl --user set-environment UWSM_NO_SESSION_SPECIFIC_VARS=true*"
106+
or add to user's *environment.d*).
97107
| *DEBUG*
98108
: (int or boolean value)
99109
|

systemd/user/wayland-wm-app-daemon.service.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ Type=exec
1111
ExecStart=@BIN_PATH@ aux app-daemon
1212
Restart=on-failure
1313
RestartMode=direct
14+
EnvironmentFile=-%t/@BIN_NAME@/session_vars.env
1415
SyslogIdentifier=@BIN_NAME@_app-daemon
1516
Slice=session.slice

systemd/user/wayland-wm-env@.service.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ RemainAfterExit=yes
2020
ExecStart=@BIN_PATH@ aux prepare-env -- "%I"
2121
ExecStopPost=@BIN_PATH@ aux cleanup-env
2222
Restart=no
23+
EnvironmentFile=-%t/@BIN_NAME@/session_vars.env
2324
SyslogIdentifier=@BIN_NAME@_env-preloader
2425
Slice=session.slice

systemd/user/wayland-wm@.service.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Type=notify
2020
NotifyAccess=all
2121
ExecStart=@BIN_PATH@ aux exec -- %I
2222
Restart=no
23+
EnvironmentFile=-%t/@BIN_NAME@/session_vars.env
2324
TimeoutStartSec=30
2425
TimeoutStopSec=10
2526
SyslogIdentifier=@BIN_NAME@_%I

uwsm/main.py

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -127,20 +127,38 @@ def _split_lines(self, text, width):
127127
class Varnames:
128128
"Sets of varnames"
129129

130-
always_export = {
131-
"PATH",
132-
"XDG_CURRENT_DESKTOP",
133-
"XDG_MENU_PREFIX",
130+
# TODO: remove this in future release
131+
session_separate = str2bool_plus(
132+
os.environ.get("UWSM_NO_SESSION_SPECIFIC_VARS", "0")
133+
)
134+
135+
session_specific = {
134136
"XDG_SEAT",
135137
"XDG_SEAT_PATH",
136-
"XDG_SESSION_DESKTOP",
137138
"XDG_SESSION_ID",
138139
"XDG_SESSION_PATH",
139-
"XDG_SESSION_TYPE",
140140
"XDG_VTNR",
141141
}
142-
never_export = {"PWD", "LS_COLORS", "INVOCATION_ID", "SHLVL", "SHELL", "TERM"}
143-
always_unset = {"DISPLAY", "WAYLAND_DISPLAY"}
142+
always_export = {
143+
"PATH",
144+
"XDG_CURRENT_DESKTOP",
145+
"XDG_MENU_PREFIX",
146+
"XDG_SESSION_DESKTOP",
147+
"XDG_SESSION_CLASS",
148+
"XDG_SESSION_TYPE",
149+
} | (set() if session_separate else session_specific)
150+
never_export = {
151+
"PWD",
152+
"LS_COLORS",
153+
"INVOCATION_ID",
154+
"SHLVL",
155+
"SHELL",
156+
"TERM",
157+
"NOTIFY_SOCKET",
158+
} | (set() if not session_separate else session_specific)
159+
always_unset = {"DISPLAY", "WAYLAND_DISPLAY"} | (
160+
set() if not session_separate else session_specific
161+
)
144162
always_cleanup = {
145163
"DISPLAY",
146164
"LANG",
@@ -150,14 +168,11 @@ class Varnames:
150168
"XCURSOR_THEME",
151169
"XDG_CURRENT_DESKTOP",
152170
"XDG_MENU_PREFIX",
153-
"XDG_SEAT",
154-
"XDG_SEAT_PATH",
155171
"XDG_SESSION_DESKTOP",
156-
"XDG_SESSION_ID",
157-
"XDG_SESSION_PATH",
172+
"XDG_SESSION_CLASS",
158173
"XDG_SESSION_TYPE",
159-
"XDG_VTNR",
160-
}
174+
"NOTIFY_SOCKET",
175+
} | session_specific
161176
never_cleanup = {"SSH_AGENT_LAUNCHER", "SSH_AUTH_SOCK", "SSH_AGENT_PID"}
162177

163178

@@ -1483,6 +1498,7 @@ def generate_units(rung: str = "run"):
14831498
ExecStart={BIN_PATH} aux prepare-env -- "%I"
14841499
ExecStopPost={BIN_PATH} aux cleanup-env
14851500
Restart=no
1501+
EnvironmentFile=-%t/{BIN_NAME}/session_vars.env
14861502
SyslogIdentifier={BIN_NAME}_env-preloader
14871503
Slice=session.slice
14881504
"""
@@ -1517,6 +1533,7 @@ def generate_units(rung: str = "run"):
15171533
NotifyAccess=all
15181534
ExecStart={BIN_PATH} aux exec -- %I
15191535
Restart=no
1536+
EnvironmentFile=-%t/{BIN_NAME}/session_vars.env
15201537
TimeoutStartSec=30
15211538
TimeoutStopSec=10
15221539
SyslogIdentifier={BIN_NAME}_%I
@@ -1573,6 +1590,7 @@ def generate_units(rung: str = "run"):
15731590
ExecStart={BIN_PATH} aux app-daemon
15741591
Restart=on-failure
15751592
RestartMode=direct
1593+
EnvironmentFile=-%t/{BIN_NAME}/session_vars.env
15761594
SyslogIdentifier={BIN_NAME}_app-daemon
15771595
Slice=session.slice
15781596
"""
@@ -1845,6 +1863,7 @@ def generate_tweaks(rung: str = "run"):
18451863
[Service]
18461864
# also put them in special graphical app slice
18471865
Slice=app-graphical.slice
1866+
EnvironmentFile=-%t/{BIN_NAME}/session_vars.env
18481867
"""
18491868
),
18501869
rung=rung,
@@ -2775,8 +2794,8 @@ def finalize(additional_vars=None):
27752794
sys.exit(1)
27762795

27772796

2778-
def save_env(filename: str, env: dict = None):
2779-
"Saves environment to a null-separated runtime file"
2797+
def save_env(filename: str, env: dict = None, separator: str = "\0"):
2798+
"Saves environment to a null-separated (or value of 'separator') runtime file"
27802799
if env is None:
27812800
env = dict(os.environ)
27822801
env = filter_varnames(env)
@@ -2786,7 +2805,14 @@ def save_env(filename: str, env: dict = None):
27862805
env_file = os.path.join(BaseDirectory.get_runtime_dir(), BIN_NAME, filename)
27872806
os.makedirs(os.path.dirname(env_file), exist_ok=True)
27882807
with open(env_file, "w") as env_file_data:
2789-
env_file_data.write("\0".join((f"{key}={value}" for key, value in env.items())))
2808+
# No processing or escaping is done here.
2809+
# The intended use of alternative separator is for writing systemd
2810+
# EnvironmentFile= with session XDG_ vars which should not
2811+
# contain anything requiring escaping
2812+
env_file_data.write(
2813+
separator.join((f"{key}={value}" for key, value in env.items()))
2814+
+ ("\n" if separator == "\n" else "")
2815+
)
27902816
print_debug(f"written {env_file}", env)
27912817

27922818

@@ -3372,7 +3398,12 @@ def cleanup_env():
33723398
print_normal("Restoring initial systemd vars.")
33733399
set_systemd_vars(env_pre, verbose=False, bus_session=bus_session)
33743400

3375-
for drop_file in [cleanup_file, env_pre_file]:
3401+
# also remove session-specific env file
3402+
session_env_file = os.path.join(
3403+
BaseDirectory.get_runtime_dir(strict=True), BIN_NAME, "session_vars.env"
3404+
)
3405+
3406+
for drop_file in [cleanup_file, env_pre_file, session_env_file]:
33763407
if not os.path.exists(drop_file):
33773408
continue
33783409
os.remove(drop_file)
@@ -4086,7 +4117,14 @@ def is_int(checkvar):
40864117
if app_unit_type == "scope":
40874118
final_args.append("--scope")
40884119
else:
4089-
final_args.extend(["--property=Type=exec", "--property=ExitType=cgroup"])
4120+
final_args.extend(
4121+
["--property=Type=exec", "--property=ExitType=cgroup"]
4122+
+ [
4123+
f"--setenv={var}={os.environ.get(var, "")}"
4124+
for var in sorted(Varnames.session_specific)
4125+
if os.environ.get(var, "")
4126+
]
4127+
)
40904128
# silence service via unit properties
40914129
if silent:
40924130
if silent == "out":
@@ -5035,8 +5073,20 @@ def main():
50355073
)
50365074
print_debug(sprc)
50375075

5076+
# save login environment
50385077
save_env("env_login")
50395078

5079+
# save session vars for unit EnvironmentFile=
5080+
save_env(
5081+
"session_vars.env",
5082+
env={
5083+
var: os.environ.get(var, "")
5084+
for var in sorted(Varnames.session_specific)
5085+
if os.environ.get(var, "")
5086+
},
5087+
separator="\n",
5088+
)
5089+
50405090
# fork out a process that will hold session scope open
50415091
# until compositor unit is stopped
50425092
mainpid = os.getpid()

0 commit comments

Comments
 (0)