Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Steam input in unprivileged containers #81

Open
ABeltramo opened this issue Jun 27, 2024 · 11 comments · May be fixed by #88
Open

Fix Steam input in unprivileged containers #81

ABeltramo opened this issue Jun 27, 2024 · 11 comments · May be fixed by #88
Labels
bug Something isn't working enhancement New feature or request

Comments

@ABeltramo
Copy link
Member

Steam input works by creating virtual devices using uinput (similar to how we do it in inputtino). So the first step is obviously to allow the Steam container to access it:

devices = ["/dev/uinput:/dev/uinput"]
env = [
  "PROTON_LOG=1",
  "RUN_GAMESCOPE=true",
  "GOW_REQUIRED_DEVICES=/dev/uinput /dev/input/* /dev/dri/* /dev/nvidia*"
]

When you start a game with steam input enabled the following uevents will be generated:

➜ docker exec -it -u retro WolfSteam_4135727842959053255 udevadm monitor -p
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent

KERNEL[13943.544155] add      /devices/virtual/input/input165 (input)
ACTION=add
DEVPATH=/devices/virtual/input/input165
SUBSYSTEM=input
PRODUCT=3/28de/11ff/1
NAME="Microsoft X-Box 360 pad 0"
PROP=0
EV=20000b
KEY=7cdb0000 0 0 0 0 0 0 0 0 0
ABS=3003f
FF=10000 0 0
MODALIAS=input:b0003v28DEp11FFe0001-e0,1,3,15,k130,131,133,134,136,137,13A,13B,13C,13D,13E,ra0,1,2,3,4,5,10,11,mlsf50,w
SEQNUM=23561

KERNEL[13943.544214] add      /devices/virtual/input/input165/event26 (input)
ACTION=add
DEVPATH=/devices/virtual/input/input165/event26
SUBSYSTEM=input
DEVNAME=/dev/input/event26
SEQNUM=23562
MAJOR=13
MINOR=90

KERNEL[13943.544244] add      /devices/virtual/input/input165/js3 (input)
ACTION=add
DEVPATH=/devices/virtual/input/input165/js3
SUBSYSTEM=input
DEVNAME=/dev/input/js3
SEQNUM=23563
MAJOR=13
MINOR=3

As you can see it's missing the corresponding udev events (network isolation for the win); we'll have to pick those kernel events up, mknod the devices and relay the correspondent udev event using fake-udev.

The only bit that I'm not sure is how to avoid mounting other containers devices. Unfortunately kernel events aren't isolated, for example I can see the event for when a device is plugged in the host.. How can we mknod only the devices that are actually generated by this steam instance? Intercept /dev/uinput ioctl calls?

Quick fix

This will break isolation, but for a single user this should work:

[[apps]]
title = "Steam"
start_virtual_compositor = true

[apps.runner]
type = "docker"
name = "WolfSteam"
image = "ghcr.io/games-on-whales/steam:edge"
mounts = ["/dev/input:/dev/input:rw", "/run/udev:/run/udev:ro"]
env = [
  "PROTON_LOG=1",
  "RUN_GAMESCOPE=true",
  "GOW_REQUIRED_DEVICES=/dev/uinput /dev/input/* /dev/dri/* /dev/nvidia*",
]
devices = ["/dev/uinput:/dev/uinput"]
ports = []
base_create_json = """
{
  "HostConfig": {
    "NetworkMode": "host",
    "IpcMode": "host",
    "CapAdd": ["SYS_ADMIN", "SYS_NICE", "SYS_PTRACE", "NET_RAW", "MKNOD", "NET_ADMIN"],
    "SecurityOpt": ["seccomp=unconfined", "apparmor=unconfined"],
    "Ulimits": [{"Name":"nofile", "Hard":10240, "Soft":10240}],
    "Privileged": false,
    "DeviceCgroupRules": ["c 13:* rmw", "c 244:* rmw"]
  }
}
\
"""

And set the env variable WOLF_DOCKER_FAKE_UDEV_PATH="" to disable our fake_udev implementation.

@ABeltramo ABeltramo added bug Something isn't working enhancement New feature or request labels Jun 27, 2024
@Drakulix
Copy link
Contributor

Intercept /dev/uinput ioctl calls?

Yes. A valve engineer confirm to me, that LD_PRELOAD-ing steam to do this should give us all the infos we need.

Basically we should intercept UI_DEV_CREATE and then internally call UI_GET_SYSNAME, matching the name to our container and returning from the intercepted ioctl.

Everything that is not UI_DEV_CREATE we can just passthrough and ignore.

@ABeltramo
Copy link
Member Author

Thank you so much for confirming it! It should be fairly straightforward then, just need to plug together fake-udev and mknod in a simple library that I'll preload.
Damn I wish I could bother a Valve engineer too 😅

@pahaze
Copy link

pahaze commented Dec 16, 2024

Hi! Just wondering if there's any update on this. I tried the quick fix, as I just want to connect to my TV and phone (separate times, home / work respectively), but I get the following in my logs: [DOCKER] error 400 - {"message":"Duplicate mount point: /run/udev"}

I set the runner to what you put in the quick fix in config.toml, and I also set the environment variable to not use fake-udev. I'm using Fedora 41, fully updated, and I'm using the Docker compose file. Here's my config:

version: "3"
services:
  wolf:
    image: ghcr.io/games-on-whales/wolf:stable
    environment:
      - XDG_RUNTIME_DIR=/tmp/sockets
      - HOST_APPS_STATE_FOLDER=/etc/wolf
      - WOLF_DOCKER_FAKE_UDEV_PATH=""
    volumes:
      - /etc/wolf/:/etc/wolf
      - /tmp/sockets:/tmp/sockets:rw
      - /var/run/docker.sock:/var/run/docker.sock:rw
      - /dev/:/dev/:rw
      - /run/udev:/run/udev:rw
    device_cgroup_rules:
      - 'c 13:* rmw'
    devices:
      - /dev/dri
      - /dev/uinput
      - /dev/uhid
    network_mode: host
    restart: unless-stopped

And here's my full log output if it's needed:

wolf-1  | [2024-12-16 16:09:06] 
wolf-1  | [2024-12-16 16:09:06] [ /etc/cont-init.d/10-setup_user.sh: executing... ]
wolf-1  | [2024-12-16 16:09:06] **** Configure default user ****
wolf-1  | [2024-12-16 16:09:06] Container running as root. Nothing to do.
wolf-1  | [2024-12-16 16:09:06] DONE
wolf-1  | [2024-12-16 16:09:06] 
wolf-1  | [2024-12-16 16:09:06] [ /etc/cont-init.d/15-setup_devices.sh: executing... ]
wolf-1  | [2024-12-16 16:09:06] **** Configure devices ****
wolf-1  | [2024-12-16 16:09:06] Exec device groups
wolf-1  | [2024-12-16 16:09:06] Adding user 'root' to groups: gow-gid-104
wolf-1  | [2024-12-16 16:09:06] DONE
wolf-1  | [2024-12-16 16:09:06] 
wolf-1  | [2024-12-16 16:09:06] [ /etc/cont-init.d/30-nvidia.sh: executing... ]
wolf-1  | [2024-12-16 16:09:06] 
wolf-1  | [2024-12-16 16:09:06] [ /etc/cont-init.d/init-gamescope.sh: executing... ]
wolf-1  | [2024-12-16 16:09:06] Launching the container's startup script as user 'root'
wolf-1  | 0:00:00.048611408   193 0x55ca3806e460 WARN          adaptivedemux2 gstadaptivedemuxelement.c:41:adaptivedemux2_base_element_init: Failed to load libsoup library
wolf-1  | 0:00:00.048715749   193 0x55ca3806e460 WARN          adaptivedemux2 gstadaptivedemuxelement.c:41:adaptivedemux2_base_element_init: Failed to load libsoup library
wolf-1  | 0:00:00.048803249   193 0x55ca3806e460 WARN          adaptivedemux2 gstadaptivedemuxelement.c:41:adaptivedemux2_base_element_init: Failed to load libsoup library
wolf-1  | 0:00:00.219949922   193 0x55ca3806e460 WARN                 default ges-meta-container.c:236:_set_value:<GESAsset@0x55ca38474f80> Could not set value on item: format-version
wolf-1  | 0:00:00.219970062   193 0x55ca3806e460 WARN                 default ges-meta-container.c:236:_set_value:<GESAsset@0x55ca384756f0> Could not set value on item: format-version
wolf-1  | 0:00:00.219985432   193 0x55ca3806e460 WARN                 default ges-meta-container.c:236:_set_value:<GESAsset@0x55ca38475e40> Could not set value on item: format-version
wolf-1  | 0:00:00.220246443   193 0x55ca3806e460 WARN               structure gststructure.c:2375:priv_gst_structure_parse_fields: Failed to find delimiter, r=mimetype
wolf-1  | 0:00:00.266264172   193 0x55ca3806e460 WARN              cudaloader gstcudaloader.cpp:260:gst_cuda_load_library_once_func: Could not open library libcuda.so.1, libcuda.so.1: cannot open shared object file: No such file or directory
wolf-1  | amdgpu: os_same_file_description couldn't determine if two DRM fds reference the same file description.
wolf-1  | If they do, bad things may happen!
wolf-1  | 0:00:00.414302906   193 0x55ca3806e460 WARN                vafilter gstvafilter.c:1743:gst_va_filter_has_compose:<vafilter0> VPP does not support alpha blending
wolf-1  | 0:00:00.443903880   193 0x55ca3806e460 WARN                 default gstvaapi.c:229:plugin_init: Cannot create a VA display
wolf-1  | 16:09:07.005177572 INFO  | Gstreamer version: 1.25.0-1
wolf-1  | 16:09:07.005829074 INFO  | Reading config file from: /etc/wolf/cfg/config.toml
wolf-1  | 0:00:00.535793416     1 0x5592820ea520 WARN     GST_ELEMENT_FACTORY gstelementfactory.c:712:gst_element_factory_make_with_properties: no such element factory "vah264lpenc"!
wolf-1  | amdgpu: os_same_file_description couldn't determine if two DRM fds reference the same file description.
wolf-1  | If they do, bad things may happen!
wolf-1  | 0:00:00.567071538     1 0x5592820ea520 WARN                vafilter gstvafilter.c:1743:gst_va_filter_has_compose:<vafilter0> VPP does not support alpha blending
wolf-1  | 16:09:07.057857466 INFO  | Using H264 encoder: vaapi
wolf-1  | 0:00:00.580524700     1 0x5592820ea520 WARN     GST_ELEMENT_FACTORY gstelementfactory.c:712:gst_element_factory_make_with_properties: no such element factory "vah265lpenc"!
wolf-1  | 16:09:07.066066547 INFO  | Using HEVC encoder: vaapi
wolf-1  | 0:00:00.588726952     1 0x5592820ea520 WARN     GST_ELEMENT_FACTORY gstelementfactory.c:712:gst_element_factory_make_with_properties: no such element factory "vaav1enc"!
wolf-1  | 0:00:00.588739162     1 0x5592820ea520 WARN     GST_ELEMENT_FACTORY gstelementfactory.c:712:gst_element_factory_make_with_properties: no such element factory "vaav1lpenc"!
wolf-1  | 16:09:07.066501009 INFO  | Using AV1 encoder: aom
wolf-1  | 16:09:07.066510259 WARN  | Software AV1 encoder detected
wolf-1  | 16:09:07.067504743 INFO  | Starting API server on /tmp/wolf.sock
wolf-1  | 16:09:07.067520973 INFO  | Starting mDNS service
wolf-1  | 16:09:07.067540773 INFO  | RTSP server started on port: 48010
wolf-1  | 16:09:07.067568653 INFO  | HTTP server listening on port: 47989 
wolf-1  | 16:09:07.067575263 INFO  | Control server started on port: 47999
wolf-1  | XDG_RUNTIME_DIR (/tmp/sockets) is not owned by us (uid 0), but by uid 1000! (This could e.g. happen if you try to connect to a non-root PulseAudio as a root user, over the native protocol. Don't do that.)
wolf-1  | 16:09:07.067732404 WARN  | [PULSE] Unable to connect, Access denied
wolf-1  | 16:09:07.067776524 INFO  | Starting PulseAudio docker container
wolf-1  | 16:09:07.068444977 WARN  | [DOCKER] Container WolfPulseAudio already present, removing first
wolf-1  | 16:09:07.068904469 INFO  | HTTPS server listening on port: 47984 
wolf-1  | 16:09:10.839454254 WARN  | HTTPS error during request at  error code: 2 - End of file
wolf-1  | 16:09:11.103662469 WARN  | [HTTP] Can't find icon_png_path for app with id: 1
wolf-1  | 16:09:11.177125904 WARN  | [HTTP] Can't find icon_png_path for app with id: 1
wolf-1  | 16:09:11.212713281 WARN  | [HTTP] Can't find icon_png_path for app with id: 4
wolf-1  | 16:09:11.213254394 WARN  | [HTTP] Can't find icon_png_path for app with id: 2
wolf-1  | 16:09:12.289008784 WARN  | [HTTP] Can't find icon_png_path for app with id: 1
wolf-1  | 16:09:12.329672681 WARN  | [HTTP] Can't find icon_png_path for app with id: 1
wolf-1  | 16:09:12.697998049 WARN  | [HTTP] Can't find icon_png_path for app with id: 4
wolf-1  | 16:09:12.728541217 WARN  | [HTTP] Can't find icon_png_path for app with id: 2
wolf-1  | 16:09:13.004705398 WARN  | Moonlight requested an impossible number of channels: 2
wolf-1  | 16:09:13.005892782 INFO  | RTP server started on port: 48100
wolf-1  | 16:09:13.005917523 INFO  | RTP server started on port: 48200
wolf-1  | 16:09:13.068433505 WARN  | [DOCKER] error 400 - {"message":"Duplicate mount point: /run/udev"}
wolf-1  | 
wolf-1  | 0:00:06.591309000     1 0x7f527c000e60 WARN                audiosrc gstaudiosrc.c:227:audioringbuffer_thread_func:<pulsesrc0> error reading data -1 (reason: Success), skipping segment
wolf-1  | 16:09:13.076542667 WARN  | [RTSP] received packet from unrecognised client: 192.168.42.58
wolf-1  | 16:09:13.658596813 WARN  | [HTTP] Can't find icon_png_path for app with id: 1
wolf-1  | 16:09:13.744568276 WARN  | [HTTP] Can't find icon_png_path for app with id: 1
wolf-1  | 16:09:13.842027864 WARN  | [HTTP] Can't find icon_png_path for app with id: 4
wolf-1  | 16:09:14.037075340 WARN  | [HTTP] Can't find icon_png_path for app with id: 2
wolf-1  | 16:09:16.239103116 WARN  | Moonlight requested an impossible number of channels: 2
wolf-1  | 16:09:16.239630198 WARN  | [RTP] Unable to start RTP server on 48200: bind: Address already in use [system:98 at /usr/include/boost/asio/detail/reactive_socket_service.hpp:161:33 in function 'boost::system::error_code boost::asio::detail::reactive_socket_service<boost::asio::ip::udp>::bind(implementation_type &, const endpoint_type &, boost::system::error_code &) [Protocol = boost::asio::ip::udp]']
wolf-1  | 16:09:16.239654218 WARN  | [RTP] Unable to start RTP server on 48100: bind: Address already in use [system:98 at /usr/include/boost/asio/detail/reactive_socket_service.hpp:161:33 in function 'boost::system::error_code boost::asio::detail::reactive_socket_service<boost::asio::ip::udp>::bind(implementation_type &, const endpoint_type &, boost::system::error_code &) [Protocol = boost::asio::ip::udp]']
wolf-1  | 16:09:16.296417958 WARN  | [DOCKER] error 400 - {"message":"Duplicate mount point: /run/udev"}
wolf-1  | 
wolf-1  | 0:00:09.819306723     1 0x7f5318067650 WARN                audiosrc gstaudiosrc.c:227:audioringbuffer_thread_func:<pulsesrc1> error reading data -1 (reason: Success), skipping segment
wolf-1  | 16:09:16.298561876 WARN  | [RTSP] received packet from unrecognised client: 192.168.42.58
wolf-1  | 16:09:19.369840202 WARN  | Moonlight requested an impossible number of channels: 2
wolf-1  | 16:09:19.370088533 INFO  | RTP server started on port: 48100
wolf-1  | 16:09:19.370114353 INFO  | RTP server started on port: 48200
wolf-1  | 16:09:19.430283216 WARN  | [DOCKER] error 400 - {"message":"Duplicate mount point: /run/udev"}
wolf-1  | 
wolf-1  | 0:00:12.953280521     1 0x7f52d8001380 WARN                audiosrc gstaudiosrc.c:227:audioringbuffer_thread_func:<pulsesrc2> error reading data -1 (reason: Success), skipping segment
wolf-1  | 16:09:19.436972322 WARN  | [RTSP] received packet from unrecognised client: 192.168.42.58
wolf-1  | 16:09:21.931747033 WARN  | HTTPS error during request at  error code: 2 - End of file
wolf-1  | 16:09:26.593724064 WARN  | Moonlight requested an impossible number of channels: 2
wolf-1  | 16:09:26.594006025 INFO  | RTP server started on port: 48100
wolf-1  | 16:09:26.594018655 INFO  | RTP server started on port: 48200
wolf-1  | 16:09:26.648927938 WARN  | [DOCKER] error 400 - {"message":"Duplicate mount point: /run/udev"}
wolf-1  | 
wolf-1  | 0:00:20.171786163     1 0x7f52cc000a10 WARN                audiosrc gstaudiosrc.c:227:audioringbuffer_thread_func:<pulsesrc3> error reading data -1 (reason: Success), skipping segment
wolf-1  | 16:09:26.660396783 WARN  | [RTSP] received packet from unrecognised client: 192.168.42.58

@ABeltramo
Copy link
Member Author

@pahaze is there a specific reason why you are trying to use this?
Joypads should be already fully working, you just have to disable Steam input from the settings.

@pahaze
Copy link

pahaze commented Dec 16, 2024

@pahaze is there a specific reason why you are trying to use this?
Joypads should be already fully working, you just have to disable Steam input from the settings.

There is, sadly. I use a DualSense/DualShock 4 to play my games, and a lot of the games I own only work with Xbox controllers or need to map the controller to keyboard keys using Steam Input

@ABeltramo
Copy link
Member Author

Right, I see what you mean.
You can override the joypad mapping by editing the config.toml, that should allow you to get around this.

@pahaze
Copy link

pahaze commented Dec 16, 2024

Right, I see what you mean.
You can override the joypad mapping by editing the config.toml, that should allow you to get around this.

That should do the job! Minus the keyboard/mouse games, but I can get a wireless combo later on for those. Thanks!

@ABeltramo
Copy link
Member Author

Nice, unfortunately that's just a workaround.. Hopefully I'll get around to properly support Steam input in Wolf!

@pahaze
Copy link

pahaze commented Dec 16, 2024

Nice, unfortunately that's just a workaround.. Hopefully I'll get around to properly support Steam input in Wolf!

If possible, I might try to look into it some myself too. I'm not too experienced with Docker, or Linux to that level, and I'd like to start learning! I've not been doing much programming as is, too :). But, just an idea, and please tell me if this isn't possible or I'm wrong, but is it possible to set up seats between the Wayland sessions to know which Steam Input device is being used for each Wolf session, rather than having to rely on kernel events and potentially accessing other containers?

@ABeltramo
Copy link
Member Author

Unfortunately no, historically compositors just ignored joypads and apps had to direcly access them so everything relies on accessing stuff directly under /dev/input.

If you want to take a look into this I suggest reading how we achieved hotplug in Docker and the rationale behind this issue and the status of the corresponding stale PR.

Feel free to jump in our Discord server if you want to ask anything in more detail!

@pahaze
Copy link

pahaze commented Dec 16, 2024

I see.. I'll definitely try to look into it some if I can, at the very least to try to get a better understanding. I did change my config, and I got this at first:

Screenshot_20241216-180609.png

Which, scared me, until I went past the screen and saw it was actually working as an Xbox controller (hello from work!) 😆

Screenshot_20241216-180653.png

Thanks again for the help and all info!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working enhancement New feature or request
Projects
None yet
3 participants