I want all audio streams to pause when my bluetooth headphones disconnects. I am using pipewire-pulse which provides pulse-audio replacement daenon, as far as I understand. Problem is pipewire-pulse does not support the same set of modules as pulse-audio, such as module-bluetooth-policy
listed here. I suspect that this module would not do exactly what I want anyhow.
Instead, I tried to make a udev rule that called playerctl --all-players pause
whenever my headphones disconnects. The command does not work when run as root as my pipewire-pulse process runs as non-root. However, I can not seem to find any way to call playerctl
from udev as non-root. I have tried running a script with SUID, calling su
directly from udev rule, and su
from script, but cannot call anything as non-root from udev.
So how could I make this work?
Here is one way you can do it: using the trap
command and basic IPC signals.
Simply write a shell script like this and launch it as a normal user at boot (put it in your start up scripts or whatever):
#!/bin/sh
# You can use any signal you want; SIGINT is just an example
trap "playerctl --all-players pause" SIGINT
while true
do
sleep 1
done
Your udev
rule will not run the playerctl
command directly. It will send a signal to this shell script (via pkill
), which will then run the playerctl
command. Since the script is launched as a normal user, the command will also be run as a normal user.
I hope this helps.
You can find a list of available IPC signals here:
https://man7.org/linux/man-pages/man7/signal.7.html
Thanx! This will surely work, although i am not a huge fan of the overhead. In addition, I do want the pause to be instant, since I’m trying to do this for my colleges who will hear my music loudly when I go to the coffee machine. However, it will do for now. In addition, Do you have an idea of why udev can’t even indirectly run commands as non-root?
I’m not entirely sure about systemd
’s design philosophy, but I believe this is due to the fact that the systemd-udevd
service was started at root when you boot up your system. Generally speaking, a child process runs as the same user as its parent process.
It’s possible to implement the same idea with less overhead, though the code will be slightly more complicated. For example, you can write a python program that listens to a unix domain socket - because waiting for input from a unix socket is a blocking operation, you don’t have to run sleep
commands in an infinite loop. If you do this, you might need two scripts instead of one. One to send bytes to the unix socket and another to receive bytes from the socket. The former will be triggered by the udev rule and the latter (launched as a normal user) will be responsible for running the playerctl
command every time it receives a “go” signal from the unix socket.