Skip to content

Instantly share code, notes, and snippets.

@dpwrussell
Last active April 22, 2021 15:29
Show Gist options
  • Save dpwrussell/9d8fede004a5576e1a55bd81b2e97fc9 to your computer and use it in GitHub Desktop.
Save dpwrussell/9d8fede004a5576e1a55bd81b2e97fc9 to your computer and use it in GitHub Desktop.
Passing file descriptors to Xvfb to retrieve allocated DISPLAY

Problem

Passing a file descriptor to the command used in create_subprocess_exec would result in Xvfb complaining it could not write to the file descriptor.

Explanation

File descriptors are closed by default when passed to a subprocess, plus file descriptors do not inherit by default.

Solution

I have in this example shown how to use a pipe to pass in a file descriptor which we then read from to get the result. I set file descriptors to not be automatically closed and I set the pipe to allow inheritance.

docker build -t xfd .
docker run -it --rm xfd

Example result. DISPLAY 0 was allocated to one of the executions, DISPLAY 1 to the other.

$ docker run -it --rm xfd
_XSERVTransSocketUNIXCreateListener: ...SocketCreateListener() failed
_XSERVTransMakeAllCOTSServerListeners: server already running
DISPLAY: 0
Await xvfb
DISPLAY: 1
Await xvfb
FROM ubuntu:20.10
RUN apt-get -y update
RUN apt-get install -y python3 xvfb
ADD main.py main.py
CMD python3 main.py & python3 main.py
import os
import asyncio
async def create_xvfb():
dpipe = os.pipe()
os.set_inheritable(dpipe[1], True)
xvfb_cmd = [
"Xvfb",
"-displayfd",
str(dpipe[1]),
]
xvfb = await asyncio.create_subprocess_exec(*xvfb_cmd, close_fds=False)
while True:
s = os.read(dpipe[0], 1).decode("utf-8")
if s == "\n":
break
print(f"DISPLAY: {s}")
print("Await xvfb")
print(await xvfb.wait())
os.close(dpipe[1])
os.close(dpipe[0])
def main():
loop = asyncio.get_event_loop()
loop.run_until_complete(create_xvfb())
loop.close()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment