proton: Don't crash if sys.stderr is not usable

If a Steam user runs Steam from a terminal, puts it in the background
and then exits from that terminal, or if they restart their desktop
session from a terminal (as in ValveSoftware/Proton#6277) and then exit
from that terminal, then we can inherit a stdout and/or stderr file
descriptor pointing to an invalid file descriptor. Writing to such a
file descriptor fails with EIO. Similarly, we could get write errors
as a result of OS state, such as ENOSPC if we are writing to a disk
that is full, or EPIPE if a stream to a logging framework such as the
systemd journal has been shut down.

In sufficiently pathological situations, the file descriptor could even
become invalid while the `proton` script is running, so even checking
for validity on startup would not be enough to prevent this.

The ability to log to stderr is important but not functionally critical,
and it's not like there is anything we can usefully do about a write
failure here (or even anywhere we can usefully put a warning message),
so just ignore write errors. This is similar to the behaviour of the
`logging` framework in the Python standard library (which writes to
`stderr` if a user-defined handler fails, but takes no other action)
and also similar to the approach taken to solve
ValveSoftware/steam-for-linux#8069.

Signed-off-by: Simon McVittie <smcv@collabora.com>
Link: https://github.com/ValveSoftware/Proton/pull/6341
This commit is contained in:
Simon McVittie 2022-11-25 14:23:15 +00:00 committed by Arkadiusz Hiler
parent 07ef25e563
commit 11336323c1

10
proton
View file

@ -68,8 +68,14 @@ def append_to_env_str(env, variable, append_str, separator):
env[variable] = env[variable] + separator + append_str
def log(msg):
sys.stderr.write(PFX + msg + os.linesep)
sys.stderr.flush()
try:
sys.stderr.write(PFX + msg + os.linesep)
sys.stderr.flush()
except OSError:
# e.g. see https://github.com/ValveSoftware/Proton/issues/6277
# There's not much we can usefully do about this: printing a
# warning to stderr isn't going to work any better the second time
pass
def file_is_wine_builtin_dll(path):
if os.path.islink(path):