From 88ed51edb90f886d10277e73366b97e689371811 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Wed, 6 Aug 2025 18:12:56 +0200 Subject: [PATCH] Fix a segfault if JACK libs are missing (#8026) Fix a segmentation fault that occurs if the JACK libraries are not installed. In that case `jack_client_open` which is called through `lib_weakjack` will return a `nullptr` for the client. Subsequent calls to `jack_client_open` do not check for `nullptr` in the library so we have to do this ourselves to prevent the segmentation fault. The check is added to `AudioJack::setupWidget::getAudioPortNames`. Extract the printing of the JACK status into the function `printJackStatus` as its functionality is needed several times. Print a warning and the status in `AudioJack::setupWidget::setupWidget`. ## Code review changes Use `std::printf` and `std::fprintf`and print to `stderr` whenever fitting. --------- Co-authored-by: Andrew Wiltshire <62200778+AW1534@users.noreply.github.com> --- src/core/audio/AudioJack.cpp | 55 ++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index 543349c97..ac4e34365 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -40,6 +40,8 @@ #include "MainWindow.h" #include "MidiJack.h" +#include + namespace { @@ -57,6 +59,21 @@ QString getInputKeyByChannel(size_t channel) return "input" + QString::number(channel + 1); } +void printJackStatus(jack_status_t status) +{ + std::fprintf(stderr, "Status: 0x%2.0x\n", static_cast(status)); + + if (status & JackFailure) + { + std::fprintf(stderr, "Overall operation failed. JACK dependencies might need to be installed.\n"); + } + + if (status & JackServerFailed) + { + std::fprintf(stderr, "Could not connect to JACK server.\n"); + } +} + } namespace lmms @@ -172,15 +189,16 @@ bool AudioJack::initJackClient() m_client = jack_client_open(clientName.toLatin1().constData(), JackNullOption, &status, serverName); if (m_client == nullptr) { - printf("jack_client_open() failed, status 0x%2.0x\n", status); - if (status & JackServerFailed) { printf("Could not connect to JACK server.\n"); } + std::fprintf(stderr, "jack_client_open() failed, "); + printJackStatus(status); + return false; } if (status & JackNameNotUnique) { - printf( "there's already a client with name '%s', so unique " - "name '%s' was assigned\n", - clientName.toLatin1().constData(), jack_get_client_name(m_client)); + std::printf("there's already a client with name '%s', so unique " + "name '%s' was assigned\n", + clientName.toLatin1().constData(), jack_get_client_name(m_client)); } resizeInputBuffer(jack_get_buffer_size(m_client)); @@ -212,7 +230,7 @@ bool AudioJack::initJackClient() if (m_outputPorts.back() == nullptr) { - printf("no more JACK-ports available!\n"); + std::fprintf(stderr, "no more JACK-ports available!\n"); return false; } } @@ -230,14 +248,14 @@ void AudioJack::resizeInputBuffer(jack_nframes_t nframes) void AudioJack::attemptToConnect(size_t index, const char *lmms_port_type, const char *source_port, const char *destination_port) { - printf("Attempting to reconnect %s port %u: %s -> %s", lmms_port_type, static_cast(index), source_port, destination_port); + std::printf("Attempting to reconnect %s port %u: %s -> %s", lmms_port_type, static_cast(index), source_port, destination_port); if (!jack_connect(m_client, source_port, destination_port)) { - printf(" - Success!\n"); + std::printf(" - Success!\n"); } else { - printf(" - Failure\n"); + std::printf(" - Failure\n"); } } @@ -247,7 +265,7 @@ void AudioJack::attemptToReconnectOutput(size_t outputIndex, const QString& targ if (targetPort == disconnectedRepresentation) { - printf("Output port %u is not connected.\n", static_cast(outputIndex)); + std::fprintf(stderr, "Output port %u is not connected.\n", static_cast(outputIndex)); return; } @@ -263,7 +281,7 @@ void AudioJack::attemptToReconnectInput(size_t inputIndex, const QString& source if (sourcePort == disconnectedRepresentation) { - printf("Input port %u is not connected.\n", static_cast(inputIndex)); + std::fprintf(stderr, "Input port %u is not connected.\n", static_cast(inputIndex)); return; } @@ -284,7 +302,7 @@ void AudioJack::startProcessing() if (jack_activate(m_client)) { - printf("cannot activate client\n"); + std::fprintf(stderr, "cannot activate client\n"); return; } @@ -483,6 +501,11 @@ AudioJack::setupWidget::setupWidget(QWidget* parent) const char* serverName = nullptr; jack_status_t status; m_client = jack_client_open("LMMS-Setup Dialog", JackNullOption, &status, serverName); + if (!m_client) + { + std::fprintf(stderr, "jack_client_open() failed, "); + printJackStatus(status); + } QFormLayout * form = new QFormLayout(this); @@ -562,6 +585,14 @@ std::vector AudioJack::setupWidget::getAudioPortNames(JackPortFlags { std::vector audioPorts; + // We are using weak_libjack. If JACK is not installed this will result in the client being nullptr. + // Because jack_get_ports in weak_libjack does not check for nullptr we have to do this here and fail gracefully, + // i.e. with an empty list of audio ports. + if (!m_client) + { + return audioPorts; + } + const char **inputAudioPorts = jack_get_ports(m_client, nullptr, JACK_DEFAULT_AUDIO_TYPE, portFlags); if (inputAudioPorts) {