1

I'm writing a plugin for my statusbar to print MPD state, currently using the libmpdclient library. It has to be robust to properly handle lost connections in case MPD is restarted, but simple checking with mpd_connection_get_error on existing mpd_connection object does not work – it can detect the error only when the initial mpd_connection_new fails.

This is a simplified code I'm working with:

#include <stdio.h>
#include <unistd.h>
#include <mpd/client.h>

int main(void) {
    struct mpd_connection* m_connection = NULL;
    struct mpd_status* m_status = NULL;
    char* m_state_str;

    m_connection = mpd_connection_new(NULL, 0, 30000);

    while (1) {
        // this check works only on start up (i.e. when mpd_connection_new failed),
        // not when the connection is lost later
        if (mpd_connection_get_error(m_connection) != MPD_ERROR_SUCCESS) {
            fprintf(stderr, "Could not connect to MPD: %s\n", mpd_connection_get_error_message(m_connection));
            mpd_connection_free(m_connection);
            m_connection = NULL;
        }

        m_status = mpd_run_status(m_connection);
        if (mpd_status_get_state(m_status) == MPD_STATE_PLAY) {
            m_state_str = "playing";
        } else if (mpd_status_get_state(m_status) == MPD_STATE_STOP) {
            m_state_str = "stopped";
        } else if (mpd_status_get_state(m_status) == MPD_STATE_PAUSE) {
            m_state_str = "paused";
        } else {
            m_state_str = "unknown";
        }
        printf("MPD state: %s\n", m_state_str);
        sleep(1);
    }
}

When MPD is stopped during the execution of the above program, it segfaults with:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00007fb2fd9557e0 in mpd_status_get_state () from /usr/lib/libmpdclient.so.2

The only way I can think of to make the program safe is to establish a new connection in every iteration, which I was hoping to avoid. But then what if the connection is lost between individual calls to libmpdclient functions? How often, and more importantly how exactly, should I check if the connection is still alive?

Jakub Klinkovský
  • 1,248
  • 1
  • 12
  • 33

2 Answers2

2

The only way I could find that really works (beyond reestablishing a connection with each run) is using the idle command. If mpd_recv_idle (or mpd_run_idle) returns 0, it is an error condition, and you can take that as a cue to free your connection and run from there. It's not a perfect solution, but it does let you keep a live connection between runs, and it helps you avoid segfaults (though I don't think you can completely avoid them, because if you send a command and mpd is killed before you recv it, I'm pretty sure the library still segfaults). I'm not sure if there is a better solution. It would be fantastic if there was a reliable way to detect if your connection was still alive via the API, but I can't find anything of the sort. It doesn't seem like libmpdclient is well-built for very long-lived connections that have to deal with mpd instances that go up and down over time.

Another lower-level option is to use sockets to interact with MPD through its protocol directly, though in doing that you'd likely reimplement much of libmpdclient itself anyway.

EDIT: Unfortunately, the idle command does block until something happens, and can sit blocking for as long as a single audio track will last, so if you need your program to do other things in the interim, you have to find a way to implement it asynchronously or in another thread.

Taywee
  • 1,313
  • 11
  • 17
0

Assuming "conn" is a connection created with "mpd_connection_new":

if (mpd_connection_get_error(conn) == MPD_ERROR_CLOSED) {
    // mpd_connection_get_error_message(conn)
    // will return "Connection closed by the server"
}

You can run this check after almost any libmpdclient call, including "mpd_recv_idle" or (as per your example) "mpd_run_status".

I'm using libmpdclient 2.18, and this certainly works for me.

user240515
  • 3,056
  • 1
  • 27
  • 34