On Sun, Apr 26, 2020 at 08:40:08PM +0100, Stuart Henderson wrote:
> 
> But....
> 
> The problem with this is, on a multiuser system where you want to share
> audio between mpd (or audio/squeezelite, which has the same problem) and
> more than one user, you have to share a cookie between _mpd, user1 and
> user2, so sndiod's privacy mechanism is nullified.
> 

If mpd was allowed to bypass checks and play while either user1 or
user2 are using the device, the privacy mechanism would be affected
anyway. For instance user1 would be allowed to start music during
phone-calls of user2.

> As an alternative, perhaps the mechanism sndiod now uses for root could be
> extended for users specified on sndiod's command line. Stupid hardcoded
> example for mpd, clearly not committable as-is, but demonstrating the concept.
> 

I'll try implement something like this, it's the safer.

It would be nice if _mpd could start as root, open the device, drop
privileges then. I just tried it, but this fails because mpd calls
setuid() too early, probably not designed for this.

The hack below makes mpd open the default device before main() is
called, then it reuses the same handle all the time. It works for me,
but if the device fails, mpd needs to be restarted; I don't know if
this is a problem, I don't use mpd very often.

--- SndioOutputPlugin.hxx.orig  Sun Apr 26 22:11:07 2020
+++ SndioOutputPlugin.hxx       Mon Apr 27 07:29:20 2020
@@ -22,10 +22,38 @@
 
 #include "../OutputAPI.hxx"
 
+#include <sndio.h>
+#include <unistd.h>
+#include <stdio.h>
+
 class Mixer;
 class MixerListener;
 
 extern const struct AudioOutputPlugin sndio_output_plugin;
+
+class SndioHandle {
+       struct sio_hdl *static_hdl;
+public:
+       SndioHandle()
+       {
+               static_hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0);
+       }
+
+       ~SndioHandle()
+       {
+               if (static_hdl != NULL)
+                       sio_close(static_hdl);
+       }
+
+       struct sio_hdl *open()
+       {
+               return static_hdl;
+       }
+
+       void close(struct sio_hdl *hdl)
+       {
+       }
+};
 
 class SndioOutput final : AudioOutput {
        Mixer *mixer = nullptr;
--- SndioOutputPlugin.cxx.orig  Sun Apr 26 22:02:27 2020
+++ SndioOutputPlugin.cxx       Mon Apr 27 07:29:09 2020
@@ -24,6 +24,7 @@
 #include "Log.hxx"
 
 #include <sndio.h>
+#include <unistd.h>
 
 #include <stdexcept>
 
@@ -36,6 +37,8 @@ static constexpr unsigned MPD_SNDIO_BUFFER_TIME_MS = 2
 
 static constexpr Domain sndio_output_domain("sndio_output");
 
+static SndioHandle sndio_handle;
+
 SndioOutput::SndioOutput(const ConfigBlock &block)
        :AudioOutput(0),
         device(block.GetBlockValue("device", SIO_DEVANY)),
@@ -58,14 +61,14 @@ SndioOutput::Create(EventLoop &, const ConfigBlock &bl
 static bool
 sndio_test_default_device()
 {
-       auto *hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0);
+       auto hdl = sndio_handle.open();
        if (!hdl) {
                FormatError(sndio_output_domain,
                            "Error opening default sndio device");
                return false;
        }
 
-       sio_close(hdl);
+       sndio_handle.close(hdl);
        return true;
 }
 
@@ -75,7 +78,7 @@ SndioOutput::Open(AudioFormat &audio_format)
        struct sio_par par;
        unsigned bits, rate, chans;
 
-       hdl = sio_open(device, SIO_PLAY, 0);
+       hdl = sndio_handle.open();
        if (!hdl)
                throw std::runtime_error("Failed to open default sndio device");
 
@@ -108,7 +111,7 @@ SndioOutput::Open(AudioFormat &audio_format)
 
        if (!sio_setpar(hdl, &par) ||
            !sio_getpar(hdl, &par)) {
-               sio_close(hdl);
+               sndio_handle.close(hdl);
                throw std::runtime_error("Failed to set/get audio params");
        }
 
@@ -118,7 +121,7 @@ SndioOutput::Open(AudioFormat &audio_format)
            par.pchan != chans ||
            par.sig != 1 ||
            par.le != SIO_LE_NATIVE) {
-               sio_close(hdl);
+               sndio_handle.close(hdl);
                throw std::runtime_error("Requested audio params cannot be 
satisfied");
        }
 
@@ -132,7 +135,7 @@ SndioOutput::Open(AudioFormat &audio_format)
                raw_volume = -1;
 
        if (!sio_start(hdl)) {
-               sio_close(hdl);
+               sndio_handle.close(hdl);
                throw std::runtime_error("Failed to start audio device");
        }
 }
@@ -140,7 +143,8 @@ SndioOutput::Open(AudioFormat &audio_format)
 void
 SndioOutput::Close()  noexcept
 {
-       sio_close(hdl);
+       sio_stop(hdl);
+       sndio_handle.close(hdl);
 }
 
 size_t

Reply via email to