diff --git a/src/plugins/projectexplorer/applicationlauncher.cpp b/src/plugins/projectexplorer/applicationlauncher.cpp index 8b59f32ab50..cefeabda9ea 100644 --- a/src/plugins/projectexplorer/applicationlauncher.cpp +++ b/src/plugins/projectexplorer/applicationlauncher.cpp @@ -32,6 +32,9 @@ #ifdef Q_OS_WIN #include "windebuginterface.h" #endif +#ifdef WITH_JOURNALD +#include "journaldwatcher.h" +#endif #include @@ -125,6 +128,10 @@ ApplicationLauncher::ApplicationLauncher(QObject *parent) connect(WinDebugInterface::instance(), SIGNAL(debugOutput(qint64,QString)), this, SLOT(checkDebugOutput(qint64,QString))); #endif +#ifdef WITH_JOURNALD + connect(JournaldWatcher::instance(), &JournaldWatcher::journaldOutput, + this, &ApplicationLauncher::checkDebugOutput); +#endif } ApplicationLauncher::~ApplicationLauncher() @@ -277,13 +284,13 @@ void ApplicationLauncher::cannotRetrieveDebugOutput() disconnect(WinDebugInterface::instance(), 0, this, 0); emit appendMessage(msgWinCannotRetrieveDebuggingOutput(), Utils::ErrorMessageFormat); } +#endif void ApplicationLauncher::checkDebugOutput(qint64 pid, const QString &message) { if (applicationPID() == pid) emit appendMessage(message, Utils::DebugFormat); } -#endif void ApplicationLauncher::processDone(int exitCode, QProcess::ExitStatus status) { diff --git a/src/plugins/projectexplorer/applicationlauncher.h b/src/plugins/projectexplorer/applicationlauncher.h index d3b7249a94d..a4c59d05dd4 100644 --- a/src/plugins/projectexplorer/applicationlauncher.h +++ b/src/plugins/projectexplorer/applicationlauncher.h @@ -87,8 +87,8 @@ private slots: void readStandardError(); #ifdef Q_OS_WIN void cannotRetrieveDebugOutput(); - void checkDebugOutput(qint64 pid, const QString &message); #endif + void checkDebugOutput(qint64 pid, const QString &message); void processDone(int, QProcess::ExitStatus); void bringToForeground(); diff --git a/src/plugins/projectexplorer/journaldwatcher.cpp b/src/plugins/projectexplorer/journaldwatcher.cpp new file mode 100644 index 00000000000..05a2f15796c --- /dev/null +++ b/src/plugins/projectexplorer/journaldwatcher.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://www.qt.io/licensing. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "journaldwatcher.h" + +#include +#include + +#include +#include + +#include + +namespace ProjectExplorer { + +JournaldWatcher *JournaldWatcher::m_instance = 0; + +namespace Internal { + +class JournaldWatcherPrivate +{ +public: + JournaldWatcherPrivate() : + m_journalContext(0), + m_notifier(0) + { } + + ~JournaldWatcherPrivate() + { + teardown(); + } + + bool setup(); + void teardown(); + + JournaldWatcher::LogEntry retrieveEntry(); + + class SubscriberInformation { + public: + SubscriberInformation(QObject *sr, const JournaldWatcher::Subscription &sn) : + subscriber(sr), subscription(sn) + { } + + QObject *subscriber; + JournaldWatcher::Subscription subscription; + }; + QList m_subscriptions; + + sd_journal *m_journalContext; + QSocketNotifier *m_notifier; +}; + +bool JournaldWatcherPrivate::setup() +{ + QTC_ASSERT(!m_journalContext, return false); + int r = sd_journal_open(&m_journalContext, 0); + if (r != 0) + return false; + + r = sd_journal_seek_tail(m_journalContext); + if (r != 0) + return false; + + // Work around https://bugs.freedesktop.org/show_bug.cgi?id=64614 + sd_journal_previous(m_journalContext); + + int fd = sd_journal_get_fd(m_journalContext); + if (fd < 0) + return false; + + m_notifier = new QSocketNotifier(fd, QSocketNotifier::Read); + return true; +} + +void JournaldWatcherPrivate::teardown() +{ + delete m_notifier; + m_notifier = 0; + + if (m_journalContext) { + sd_journal_close(m_journalContext); + m_journalContext = 0; + } +} + +JournaldWatcher::LogEntry JournaldWatcherPrivate::retrieveEntry() +{ + JournaldWatcher::LogEntry result; + + // Advance: + int r = sd_journal_next(m_journalContext); + if (r == 0) + return result; + + const void *rawData; + size_t length; + SD_JOURNAL_FOREACH_DATA(m_journalContext, rawData, length) { + QByteArray tmp = QByteArray::fromRawData(static_cast(rawData), length); + int offset = tmp.indexOf('='); + if (offset < 0) + continue; + result.insert(tmp.left(offset), tmp.mid(offset + 1)); + } + + return result; +} + +} // namespace Internal + +using namespace Internal; + +static JournaldWatcherPrivate *d = 0; + +JournaldWatcher::~JournaldWatcher() +{ + d->teardown(); + + m_instance = 0; + + delete d; + d = 0; +} + +JournaldWatcher *JournaldWatcher::instance() +{ + return m_instance; +} + +bool JournaldWatcher::subscribe(QObject *subscriber, const Subscription &subscription) +{ + // Check that we do not have that subscriber yet: + int pos = Utils::indexOf(d->m_subscriptions, + [subscriber](const JournaldWatcherPrivate::SubscriberInformation &info) { + return info.subscriber == subscriber; + }); + QTC_ASSERT(pos >= 0, return false); + + d->m_subscriptions.append(JournaldWatcherPrivate::SubscriberInformation(subscriber, subscription)); + connect(subscriber, &QObject::destroyed, m_instance, &JournaldWatcher::unsubscribe); + + return true; +} + +void JournaldWatcher::unsubscribe(QObject *subscriber) +{ + int pos = Utils::indexOf(d->m_subscriptions, + [subscriber](const JournaldWatcherPrivate::SubscriberInformation &info) { + return info.subscriber == subscriber; + }); + if (pos < 0) + return; + + d->m_subscriptions.removeAt(pos); +} + +JournaldWatcher::JournaldWatcher() +{ + QTC_ASSERT(!m_instance, return); + + d = new JournaldWatcherPrivate; + m_instance = this; + + if (!d->setup()) + d->teardown(); + else + connect(d->m_notifier, &QSocketNotifier::activated, m_instance, &JournaldWatcher::handleEntry); + m_instance->handleEntry(); // advance to the end of file... +} + +void JournaldWatcher::handleEntry() +{ + // process events: + if (!d->m_notifier || sd_journal_process(d->m_journalContext) == SD_JOURNAL_NOP) + return; + + LogEntry logEntry; + forever { + logEntry = d->retrieveEntry(); + if (logEntry.isEmpty()) + break; + + foreach (const JournaldWatcherPrivate::SubscriberInformation &info, d->m_subscriptions) + info.subscription(logEntry); + + QByteArray tmp = logEntry.value(QByteArrayLiteral("_PID")); + quint64 pid = tmp.isEmpty() ? 0 : QString::fromLatin1(tmp).toInt(); + + QString message = QString::fromUtf8(logEntry.value(QByteArrayLiteral("MESSAGE"))); + + emit journaldOutput(pid, message); + } +} + +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/journaldwatcher.h b/src/plugins/projectexplorer/journaldwatcher.h new file mode 100644 index 00000000000..9f266ef97e3 --- /dev/null +++ b/src/plugins/projectexplorer/journaldwatcher.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://www.qt.io/licensing. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef JOURNALDWATCHER_H +#define JOURNALDWATCHER_H + +#include + +#include + +namespace ProjectExplorer { + +class ProjectExplorerPlugin; + +class JournaldWatcher : public QObject +{ + Q_OBJECT + +public: + typedef QMap LogEntry; + typedef std::function Subscription; + + ~JournaldWatcher(); + + static JournaldWatcher *instance(); + + static bool subscribe(QObject *subscriber, const Subscription &subscription); + static void unsubscribe(QObject *subscriber); + +signals: + void journaldOutput(quint64 pid, const QString &message); + +private: + JournaldWatcher(); + + void handleEntry(); + + static JournaldWatcher *m_instance; + + friend class ProjectExplorerPlugin; +}; + +} // namespace ProjectExplorer + +#endif // JOURNALDWATCHER_H diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 10f06fca498..ba9ea4e3681 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -36,6 +36,9 @@ #include "deployablefile.h" #include "deployconfiguration.h" #include "gcctoolchainfactories.h" +#ifdef WITH_JOURNALD +#include "journaldwatcher.h" +#endif #include "jsonwizard/jsonwizardfactory.h" #include "jsonwizard/jsonwizardgeneratorfactory.h" #include "jsonwizard/jsonwizardpagefactory_p.h" @@ -284,6 +287,9 @@ public: QStringList m_arguments; QList m_panelFactories; QString m_renameFileError; +#ifdef WITH_JOURNALD + JournaldWatcher *m_journalWatcher; +#endif }; ProjectExplorerPluginPrivate::ProjectExplorerPluginPrivate() : @@ -293,6 +299,9 @@ ProjectExplorerPluginPrivate::ProjectExplorerPluginPrivate() : m_kitManager(0), m_toolChainManager(0), m_shuttingDown(false) +#ifdef WITH_JOURNALD + , m_journalWatcher(0) +#endif { } @@ -331,6 +340,9 @@ ProjectExplorerPlugin::~ProjectExplorerPlugin() // Force sequence of deletion: delete dd->m_kitManager; // remove all the profile information delete dd->m_toolChainManager; +#ifdef WITH_JOURNALD + delete dd->m_journalWatcher; +#endif ProjectPanelFactory::destroyFactories(); delete dd; } @@ -354,6 +366,10 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er addAutoReleasedObject(new DeviceManager); +#ifdef WITH_JOURNALD + dd->m_journalWatcher = new JournaldWatcher; +#endif + // Add ToolChainFactories: #ifdef Q_OS_WIN addAutoReleasedObject(new WinDebugInterface); diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro index efc0edd3d2d..e0c9ca9b9e5 100644 --- a/src/plugins/projectexplorer/projectexplorer.pro +++ b/src/plugins/projectexplorer/projectexplorer.pro @@ -333,6 +333,13 @@ equals(TEST, 1) { outputparser_test.h } +journald { + SOURCES += journaldwatcher.cpp + HEADERS += journaldwatcher.h + DEFINES += WITH_JOURNALD + LIBS += -lsystemd +} + macx:LIBS += -framework Carbon RESOURCES += projectexplorer.qrc