Sample Header Ad - 728x90

Run display dependent command in a system setting event

0 votes
1 answer
172 views
I am working on a systemd/udev setting that I would like to share upstream, however I can't get it to work in a non-hacky way. Essentially, I have this script as the exec of a systemd service:
ICON="somepath/dslr-camera-white.png"

      function on-display() {
          local sdisplay=$(echo $XDG_SESSION_TYPE)
          if [ "$sdisplay" == "wayland" ]; then
          local display=":$(echo $WAYLAND_DISPLAY)"
          else
          local display=":$(ls /tmp/.X11-unix/* | sed 's#/tmp/.X11-unix/X##' | head -n 1)"
          fi
          
          local user=$(who | grep '('$display')' | awk '{print $1}' | head -n 1)

          local uid=$(id -u $user)

          sudo -u $user DISPLAY=$display DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$uid/bus "$@"
      }

      cleanup() {
          on-display notify-send -i $ICON "Disconnected" "The DSLR Camera has been turned off." --app-name="DSLR Webcam"
          trap - SIGTERM && kill -- -$$
      }

      trap cleanup SIGINT SIGTERM EXIT

      on-display notify-send -i $ICON "Connected" "The DSLR Camera has been turned on and it is ready to use." --app-name="DSLR Webcam"
      on-display yad --window-icon=$ICON --image=$ICON --no-buttons --title="DSLR Webcam" --notification --listen &

      output=$(v4l2-ctl --list-devices)
      line=$(echo "$output" | grep "Virtual Camera")
      vdevice=$(echo "$output" | sed -n "/$line/{n;s/^\t\+//p;}")
      gphoto2 --stdout --capture-movie | ffmpeg -i - -vcodec rawvideo -pix_fmt yuv420p -threads 0 -f v4l2 $vdevice
The point of the service is to be started by an udev rule when an specific gphoto2 supported camera is plugged in, therefor I have these udev rules:
ACTION=="add", ATTR{idVendor}=="04a9", ATTR{idProduct}=="3218", RUN+="systemctl start dslr-webcam.service"
ACTION=="remove", ATTR{idVendor}=="04a9", ATTR{idProduct}=="3218", RUN+="systemctl stop dslr-webcam.service"
So far so "ok", because I have read on alternatives to using RUN for a systemd service, but regardless... The issue here is specifically the call to yad. yad is a program that allows you to display dialogue boxes from the CLI, I am leveraging it for its ability to make a systrey icon, as I would like for one to show up when the camera is active. ## The problem Unlike notify-send, which probably works off some common socket, yad requires the appropriate XAUTHORITY set in order for it to work, otherwise you will get cannot open display: :0. The hacky solution in my case is to simply set it the right Xauthority, since I am using SDDM (display manager), it resides in the /tmp directory, so I can add this to the script:
XAUTHORITY=$(ls /tmp/xauth*)
And then it works... But this is terrible, it makes a lot of assumptions, in fact the whole on-display function also seems like a bad idea. If I took this to a different system, it probably wouldn't work, because the proper Xauthority could be in a myriad of places, and I haven't even tried Wayland yet (I will leave that for later). ## What about xauth I thought I could somehow use xauth to retrieve the right Xauthority, but that doesn't seem to be the case... This script is a system service, so my xauth info returns Authority file: /root/.xauthWV7OfU, and running it as the right user sudo -u $user xauth info gives me Authority file: /home/myuser/.Xauthority, none of which work when given to yad. The correct XAUTHORITY is set by the display manager, so I think I could only get it from child processes of it. I have also tried all the approaches give by this [answer](https://unix.stackexchange.com/a/429106/528934) , but the first one would not work, as XAUTHORITY isn't actually in the system env, and the second one (aside from the mentioned pitfalls), doesn't work, it says the xauth file is at /run/sddm/xauth_KvyuHd, but trying to use it doesn't work, so it is not the same xauth file as /tmp/xauth_FzoQqz From the same question as the previous one, [this](https://unix.stackexchange.com/a/429162/528934) approach seems to work, but I don't know how portable it is. And still seems hacky. ---------- ## Run as user service Perhaps this is the most promising one, as nothing in that script prevents it from being run as a user (it also gets rid of on-display), I did try this approach, and while if the camera was plugged-in and I ran systemctl --user start dslr-webcam.service it would work as expected (including yad), now I have a problem with the udev rule. I searched many places, including [here](https://unix.stackexchange.com/questions/550279/udev-rule-to-trigger-systemd-service) , but I cannot find how to run a systemd user service from an udev rule, and to me that doesn't really make any sense either, how would udev know what user to use?
Asked by Mathias Sven (273 rep)
Oct 28, 2023, 05:43 PM
Last activity: Oct 28, 2023, 06:48 PM