Discussion:
[mpd-devel] Receive command from Named Pipe and treat it as a client PlayerCommand
ed mcmurray
2017-08-10 15:35:39 UTC
Permalink
Hi,

From within the MPD application, I would like to access a Linux Named
Pipe FIFO that has been created in a second application. The second
application would place a STOP command into the Name Pipe, when
appropriate. I would like to insert code in MPD to then monitor this
Named Pipe for the STOP command and act on the command as though it was
sent from an mpd client.

I believe that I understand the basic operation of command handling in
MPD, so that I have an idea of how I might accomplish this. But I don't
understand it fully and could use some guidance.

_MY UNDERSTANDING_:
player_task() is the PlayerCommand handler. When a client command has
been received, the player_task runs to allow the command to be acted
on. When command is fully acted on , the PlayerCommand is set back to
NONE. The player_task then blocks and waits for receipt of next client
command, before unblocking and repeating the same command handling process.

If this is correct, I believe I would like to place the additional code
to monitor the Name Pipe, in the part of the MPD code that is waiting
for a client command. At that place in the code, I would read the
Named Pipe for STOP command and upon receipt, set PlayerCommand to STOP,
just a though it was received from an MPD client. *My problem is that I
can't find where in the MPD code it is waiting for the client command*.

Any advice on my understanding/approach, or where to look in the MPD
code would be much appreciated.

thanks,
Ed
MPD version: mpd-0.20.9


---
This email has been checked for viruses by Avast antivirus software.
https://www.avast.com/antivirus
Max Kellermann
2017-08-11 07:31:31 UTC
Permalink
From within the MPD application, I would like to access a Linux Named Pipe
FIFO that has been created in a second application. The second application
would place a STOP command into the Name Pipe, when appropriate. I would
like to insert code in MPD to then monitor this Named Pipe for the STOP
command and act on the command as though it was sent from an mpd
client.
But ... why?

You can already do that with sockets, which is the core idea of MPD.
Why pipes? Pipes have serious disadvantages (such as being
unidirectional), but you did not describe any advantages of using them
instead of sockets.
Kurt Van Dijck
2017-08-11 11:54:48 UTC
Permalink
Post by Max Kellermann
From within the MPD application, I would like to access a Linux Named Pipe
FIFO that has been created in a second application. The second application
would place a STOP command into the Name Pipe, when appropriate. I would
like to insert code in MPD to then monitor this Named Pipe for the STOP
command and act on the command as though it was sent from an mpd
client.
But ... why?
You can already do that with sockets, which is the core idea of MPD.
Why pipes? Pipes have serious disadvantages (such as being
unidirectional), but you did not describe any advantages of using them
instead of sockets.
The overhead of unix sockets during setup (you need socket()+bind()
system calls, against open() in case of a FIFO) is little compared
to the overhead of using them, as in order to successfully write()
to a fifo, the read() must complete.
Unix sockets, as any other sockets, will buffer in kernel.

I agree that unix sockets are superior to fifo's in this matter.

Kind regards,
Kurt
Max Kellermann
2017-08-11 15:07:23 UTC
Permalink
in order to successfully write() to a fifo, the read() must
complete.
Nitpicking: that part is not true. Linux has a pipe buffer as well,
which defaults to 64 kB (configurable). Maybe other kernels don't
have one, but Linux does.
Kurt Van Dijck
2017-08-11 18:30:35 UTC
Permalink
Post by Max Kellermann
in order to successfully write() to a fifo, the read() must
complete.
Nitpicking: that part is not true. Linux has a pipe buffer as well,
which defaults to 64 kB (configurable). Maybe other kernels don't
have one, but Linux does.
Even more nitpicking:
mkfifo testfifo
echo hello world > testfifo
... wait ...
cat testfifo
now echo has completed.
I know that internal buffers exist. I didn't say it wasn't.
I said writing does not complete before reading starts :-)
And that is IMO a pain to deal with as user.
For the reference: I didn't perform the above test with unix sockets
but I never experienced problems neither.

So far for the gory details :-)
I'm getting back to my mpd-to-mqtt gateway ...

Kurt
Max Kellermann
2017-08-11 21:11:44 UTC
Permalink
Post by Kurt Van Dijck
mkfifo testfifo
echo hello world > testfifo
... wait ...
cat testfifo
now echo has completed.
This has nothing to do with buffering or writes, because a write
hasn't happened at this point. It doesn't block at write(), it blocks
at open(). It blocks until the "connection" has been established.
After that, the usual pipe buffering applies, and writes do NOT block
(until the pipe buffer is full).
Kurt Van Dijck
2017-08-11 21:16:49 UTC
Permalink
Post by Max Kellermann
Post by Kurt Van Dijck
mkfifo testfifo
echo hello world > testfifo
... wait ...
cat testfifo
now echo has completed.
This has nothing to do with buffering or writes, because a write
hasn't happened at this point. It doesn't block at write(), it blocks
at open(). It blocks until the "connection" has been established.
After that, the usual pipe buffering applies, and writes do NOT block
(until the pipe buffer is full).
indeed, I jumped to conclusion too soon.
I had problems once with fifo's, during the setup.
That's why I mentioned it here...
Kind regards,
Kurt
Max Kellermann
2017-08-11 15:05:04 UTC
Permalink
On the MPD side I'm not so sure where in the code that I should create,
bind, listen and accept connections, for the socket.
That code already exists. Why do you believe a change to MPD is
necessary?
I'm sure this should be more obvious, but any help would be appreciated.
Still nothing here is obvious to me. I have no idea what you're
trying and why.
ed mcmurray
2017-08-11 16:57:54 UTC
Permalink
See Response below
Post by Max Kellermann
On the MPD side I'm not so sure where in the code that I should create,
bind, listen and accept connections, for the socket.
That code already exists. Why do you believe a change to MPD is
Okay, I now understand that no changes are required to MPD. I believe I
can just attach to the existing local socket created by MPD.
Post by Max Kellermann
necessary?
I'm sure this should be more obvious, but any help would be appreciated.
Still nothing here is obvious to me. I have no idea what you're
trying and why.
I am trying to allow a second application to send a STOP command to MPD,
using the existing local socket created by MPD. I would like to allow a
form of Convenience Switching. So if music is playing through MPD from
an MPD client (JRiver). The second application could then STOP MPD play
safely(as though command came from MPD client (Jriver). I have attached
a terminal screen shot below, showing mpd socket assignment.
Post by Max Kellermann
u_str ESTAB 0 0 * 5147 * 5249 users:(("upmpdcli",pid=212,fd=2),("upmpdcli",pid=212,fd=1))
u_str ESTAB 0 0 * 6115 * 6116 users:(("mpd",pid=236,fd=10))
tcp ESTAB 0 0 127.0.0.1:48337 127.0.0.1:6600 users:(("upmpdcli",pid=212,fd=5))
tcp ESTAB 0 0 ::ffff:127.0.0.1:6600 ::ffff:127.0.0.1:48337 users:(("mpd",pid=236,fd=11))
---
This email has been checked for viruses by Avast antivirus software.
https://www.avast.com/antivirus
Kurt Van Dijck
2017-08-11 21:19:22 UTC
Permalink
Post by ed mcmurray
I am trying to allow a second application to send a STOP command to
MPD, using the existing local socket created by MPD. I would like to
allow a form of Convenience Switching. So if music is playing through
MPD from an MPD client (JRiver). The second application could then
STOP MPD play safely(as though command came from MPD client (Jriver).
I have attached a terminal screen shot below, showing mpd socket
assignment.
A snippet I wrote recently:

static int connect_uri(const char *host, int port, int preferred_type)
{
int sock;

if (*host == '@' || *host == '/') {
/* unix socket */
struct sockaddr_un addr = {
.sun_family = AF_UNIX,
};
int socklen;

strcpy(addr.sun_path, host);
socklen = strlen(host) + offsetof(struct sockaddr_un, sun_path);

sock = socket(AF_UNIX, preferred_type, 0);
if (sock < 0)
mylog(LOG_ERR, "socket AF_UNIX ...: %s", ESTR(errno));
if (connect(sock, (void *)&addr, socklen) < 0)
mylog(LOG_ERR, "bind %s: %s", host, ESTR(errno));
return sock;
}

The remainder goes via ip sockets, which is not necessarily what you
want.

Is this what you were looking for?

Kurt
ed mcmurray
2017-08-11 23:53:25 UTC
Permalink
Kurt,

I am new to socket programming. I will review and get back to you.

Thanks,
Ed
Post by Kurt Van Dijck
Post by ed mcmurray
I am trying to allow a second application to send a STOP command to
MPD, using the existing local socket created by MPD. I would like to
allow a form of Convenience Switching. So if music is playing through
MPD from an MPD client (JRiver). The second application could then
STOP MPD play safely(as though command came from MPD client (Jriver).
I have attached a terminal screen shot below, showing mpd socket
assignment.
static int connect_uri(const char *host, int port, int preferred_type)
{
int sock;
/* unix socket */
struct sockaddr_un addr = {
.sun_family = AF_UNIX,
};
int socklen;
strcpy(addr.sun_path, host);
socklen = strlen(host) + offsetof(struct sockaddr_un, sun_path);
sock = socket(AF_UNIX, preferred_type, 0);
if (sock < 0)
mylog(LOG_ERR, "socket AF_UNIX ...: %s", ESTR(errno));
if (connect(sock, (void *)&addr, socklen) < 0)
mylog(LOG_ERR, "bind %s: %s", host, ESTR(errno));
return sock;
}
The remainder goes via ip sockets, which is not necessarily what you
want.
Is this what you were looking for?
Kurt
---
This email has been checked for viruses by Avast antivirus software.
https://www.avast.com/antivirus
ed mcmurray
2017-08-12 21:52:44 UTC
Permalink
Yes thanks, that is what I was looking for. I have copied some code and
pasted it below, to show what I am going to try. I’ve also highlighted
the two areas that I could still use some help with. •
*MPD_LOCAL_SOCKET_ADDRESS: *I believe linux SS command should show this.
$ ss -x -a // local socket address already setup between
MPD and upmdpcli
• *STOP_COMMAND_SEQUENCE: *any advice on string(s) to write to socket to
mimic command sequence that would be received by MPD when JRiver is
playing and the STOP button is pressed.

Note: We are using upmpdci, the UPnP Media Renderer front-end for MPD.

// Includes
...

// File scope
int socket_fd;

// Generic function to show example calls
void foo(void)
{


// call to setup socket
socket_setup( *MPD_LOCAL_SOCKET_ADDRESSS*); // Use local socket
address already setup between MPD and upmdpcli
...
// call to write command sequence
write_cmd_to_socket(*STOP_COMMAND_SEQUENCE);*// command sequence
that would be received by MPD when JRiver is playing and STOP button is
pressed

.
}

void write_cmd_to_socket (const char* cmd)
{
int length = strlen(cmd) + 1;
write(socket_fd, cmd, length);
}

void socket_setup (const char* socket_address)
{
struct sockaddr_un name;

/* Create socket. */
socket_fd = socket (PF_LOCAL, SOCK_STREAM, 0);

/* Store the server’s name in the socket address. */
name.sun_family = AF_LOCAL;
strcpy (name.sun_path, socket_address);

/* Connect the socket. */
connect (socket_fd, &name, SUN_LEN (&name));

close (socket_fd);
return 0;
}

Ed

-------------------------------------------------------------------------------------------------------------------------
Post by Kurt Van Dijck
Post by ed mcmurray
I am trying to allow a second application to send a STOP command to
MPD, using the existing local socket created by MPD. I would like to
allow a form of Convenience Switching. So if music is playing through
MPD from an MPD client (JRiver). The second application could then
STOP MPD play safely(as though command came from MPD client (Jriver).
I have attached a terminal screen shot below, showing mpd socket
assignment.
static int connect_uri(const char *host, int port, int preferred_type)
{
int sock;
/* unix socket */
struct sockaddr_un addr = {
.sun_family = AF_UNIX,
};
int socklen;
strcpy(addr.sun_path, host);
socklen = strlen(host) + offsetof(struct sockaddr_un, sun_path);
sock = socket(AF_UNIX, preferred_type, 0);
if (sock < 0)
mylog(LOG_ERR, "socket AF_UNIX ...: %s", ESTR(errno));
if (connect(sock, (void *)&addr, socklen) < 0)
mylog(LOG_ERR, "bind %s: %s", host, ESTR(errno));
return sock;
}
The remainder goes via ip sockets, which is not necessarily what you
want.
Is this what you were looking for?
Kurt
---
This email has been checked for viruses by Avast antivirus software.
https://www.avast.com/antivirus

Loading...