Skip to content

Commit a012133

Browse files
Hook25LiaoU3
andauthored
Inherit envvars more aggressively (bugfix) (#2067)
* Add $XDG_RUNTIME_DIR for alsa tests * Inherit envvars more aggressively The point of inheriting them from only DISPLAY having processes was to target user processes. This prioritizes them (as we do know the username) but still proceeds till we run out of options. Additionally this also calculates XDG_RUNTIME_DIR and DBUS_SESSION_BUS_ADDRESS always if it wasn't found anywhere to (try) to make the job start anyway with a sane-ish value * Python 3.5 :( * Also test the bailout mechanism --------- Co-authored-by: liaou3 <[email protected]>
1 parent 853686b commit a012133

File tree

2 files changed

+102
-31
lines changed

2 files changed

+102
-31
lines changed

checkbox-ng/plainbox/impl/session/remote_assistant.py

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import os
2424
import pwd
2525
import time
26+
import itertools
2627
from collections import namedtuple
2728
from contextlib import suppress
2829
from tempfile import SpooledTemporaryFile
@@ -268,39 +269,62 @@ def _prepare_display_without_psutil(self):
268269
}
269270

270271
def prepare_extra_env(self):
272+
"""
273+
Try to inherit user environment variables from other processes
274+
"""
275+
# target envvars are the one we are looking for in this function.
276+
# If we find them we can stop iterating
277+
target_envvars = {
278+
"DISPLAY",
279+
"XAUTHORITY",
280+
"XDG_SESSION_TYPE",
281+
"XDG_RUNTIME_DIR",
282+
"DBUS_SESSION_BUS_ADDRESS",
283+
"WAYLAND_DISPLAY",
284+
}
271285
extra_env = {}
272-
# If possible also set the DISPLAY env var
273-
# i.e when a user desktop session is running
274-
for p in psutil.pids():
275-
try:
276-
p_environ = psutil.Process(p).environ()
277-
p_user = psutil.Process(p).username()
278-
except psutil.AccessDenied:
279-
continue
280-
except AttributeError:
281-
# psutil < 4.0.0 doesn't provide Process.environ()
282-
return self._prepare_display_without_psutil()
283-
except psutil.NoSuchProcess:
284-
# quietly ignore the process that died before we had a chance
285-
# to read the environment from them
286-
continue
287-
if (
288-
"DISPLAY" in p_environ
289-
and "XAUTHORITY" in p_environ
290-
and "XDG_SESSION_TYPE" in p_environ
291-
and p_user != "gdm"
292-
): # gdm uses :1024
293-
uid = pwd.getpwnam(self._normal_user).pw_uid
294-
extra_env["DISPLAY"] = p_environ["DISPLAY"]
295-
extra_env["XAUTHORITY"] = p_environ["XAUTHORITY"]
296-
extra_env["XDG_SESSION_TYPE"] = p_environ["XDG_SESSION_TYPE"]
297-
extra_env["XDG_RUNTIME_DIR"] = "/run/user/{}".format(uid)
298-
extra_env["DBUS_SESSION_BUS_ADDRESS"] = (
299-
"unix:path=/run/user/{}/bus".format(uid)
300-
)
301-
if "WAYLAND_DISPLAY" in p_environ:
302-
extra_env["WAYLAND_DISPLAY"] = p_environ["WAYLAND_DISPLAY"]
286+
try:
287+
processes = psutil.process_iter(
288+
attrs=["pid", "environ", "username"]
289+
)
290+
infos = [
291+
p.info
292+
for p in processes
293+
if p.info["username"] != "gdm" and p.info["environ"]
294+
]
295+
except (TypeError, ValueError):
296+
# TypeError is raised on very old psutil versions (missing attrs)
297+
# ValueError is raised on old psutil (missing environ support)
298+
return self._prepare_display_without_psutil()
299+
300+
def envvar_priority(info):
301+
if info["username"] == self._normal_user:
302+
# prioritize normal users in reversed order (heuristic,
303+
# lower pids -> late spawned processes, most likely to be
304+
# "normal" user processes)
305+
return -info["pid"]
306+
# de-prioritize root users still in reverse order as above
307+
return 10000000 - info["pid"]
308+
309+
infos = sorted(infos, key=envvar_priority)
310+
311+
for info in infos:
312+
missing_keys = target_envvars - extra_env.keys()
313+
if not missing_keys:
314+
break
315+
i_env = info["environ"]
316+
present_keys = i_env.keys() & missing_keys
317+
extra_env.update({k: i_env[k] for k in present_keys})
303318

319+
uid = pwd.getpwnam(self._normal_user).pw_uid
320+
# we may be starting before a user session, lets try to assign a
321+
# default value to the runtime dir and dbus session
322+
if "XDG_RUNTIME_DIR" not in extra_env:
323+
extra_env["XDG_RUNTIME_DIR"] = "/run/user/{}".format(uid)
324+
if "DBUS_SESSION_BUS_ADDRESS" not in extra_env:
325+
extra_env["DBUS_SESSION_BUS_ADDRESS"] = (
326+
"unix:path=/run/user/{}/bus".format(uid)
327+
)
304328
return extra_env
305329

306330
@allowed_when(Idle)

checkbox-ng/plainbox/impl/session/test_remote_assistant.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,53 @@
3838

3939

4040
class RemoteAssistantTests(TestCase):
41+
@mock.patch("pwd.getpwnam")
42+
@mock.patch("psutil.process_iter")
43+
def test_prepare_extra_env_priority(
44+
self, process_iter_mock, getpwnam_mock
45+
):
46+
self_mock = mock.MagicMock()
47+
self_mock._normal_user = "ubuntu"
48+
49+
info_mocks = [
50+
# this should not be used as root processes are our last resort
51+
{
52+
"username": "root",
53+
"pid": 1,
54+
"environ": {"DISPLAY": "root_display"},
55+
},
56+
{
57+
"username": "ubuntu",
58+
"pid": 999,
59+
"environ": {"WAYLAND_DISPLAY": "wrong_display"},
60+
},
61+
{
62+
"username": "ubuntu",
63+
"pid": 1000,
64+
"environ": {"DISPLAY": ":0", "WAYLAND_DISPLAY": "wayland-1"},
65+
},
66+
]
67+
process_iter_mock.return_value = [
68+
mock.MagicMock(info=info) for info in info_mocks
69+
]
70+
71+
extra_env = RemoteSessionAssistant.prepare_extra_env(self_mock)
72+
73+
self.assertEqual(extra_env["DISPLAY"], ":0")
74+
self.assertEqual(extra_env["WAYLAND_DISPLAY"], "wayland-1")
75+
self.assertIn("XDG_RUNTIME_DIR", extra_env)
76+
self.assertIn("DBUS_SESSION_BUS_ADDRESS", extra_env)
77+
78+
@mock.patch("psutil.process_iter")
79+
def test_prepare_extra_env_fallback(self, process_iter_mock):
80+
process_iter_mock.side_effect = TypeError
81+
82+
self_mock = mock.MagicMock()
83+
84+
_ = RemoteSessionAssistant.prepare_extra_env(self_mock)
85+
86+
self.assertTrue(self_mock._prepare_display_without_psutil.called)
87+
4188
def test_allowed_when_ok(self):
4289
self_mock = mock.MagicMock()
4390
allowed_when = RemoteSessionAssistant.allowed_when

0 commit comments

Comments
 (0)