Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of Qt Creator.
7**
8** Commercial License Usage
9** Licensees holding valid commercial Qt licenses may use this file in
10** accordance with the commercial license agreement provided with the
11** Software or, alternatively, in accordance with the terms contained in
12** a written agreement between you and The Qt Company. For licensing terms
13** and conditions see https://www.qt.io/terms-conditions. For further
14** information use the contact form at https://www.qt.io/contact-us.
15**
16** GNU General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU
18** General Public License version 3 as published by the Free Software
19** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20** included in the packaging of this file. Please review the following
21** information to ensure the GNU General Public License requirements will
22** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23**
24****************************************************************************/
25
26#include "journaldwatcher.h"
27
28#include <utils/algorithm.h>
29#include <utils/qtcassert.h>
30
31#include <QSocketNotifier>
32
33#include <systemd/sd-journal.h>
34
35namespace ProjectExplorer {
36
37JournaldWatcher *JournaldWatcher::m_instance = nullptr;
38
39namespace Internal {
40
41class JournaldWatcherPrivate
42{
43public:
44 JournaldWatcherPrivate() = default;
45 ~JournaldWatcherPrivate()
46 {
47 teardown();
48 }
49
50 bool setup();
51 void teardown();
52
53 JournaldWatcher::LogEntry retrieveEntry();
54
55 class SubscriberInformation {
56 public:
57 SubscriberInformation(QObject *sr, const JournaldWatcher::Subscription &sn) :
58 subscriber(sr), subscription(sn)
59 { }
60
61 QObject *subscriber;
62 JournaldWatcher::Subscription subscription;
63 };
64 QList<SubscriberInformation> m_subscriptions;
65
66 sd_journal *m_journalContext = nullptr;
67 QSocketNotifier *m_notifier = nullptr;
68};
69
70bool JournaldWatcherPrivate::setup()
71{
72 QTC_ASSERT(!m_journalContext, return false);
73 int r = sd_journal_open(&m_journalContext, 0);
74 if (r != 0)
75 return false;
76
77 r = sd_journal_seek_tail(m_journalContext);
78 if (r != 0)
79 return false;
80
81 // Work around https://bugs.freedesktop.org/show_bug.cgi?id=64614
82 sd_journal_previous(m_journalContext);
83
84 int fd = sd_journal_get_fd(m_journalContext);
85 if (fd < 0)
86 return false;
87
88 m_notifier = new QSocketNotifier(fd, QSocketNotifier::Read);
89 return true;
90}
91
92void JournaldWatcherPrivate::teardown()
93{
94 delete m_notifier;
95 m_notifier = nullptr;
96
97 if (m_journalContext) {
98 sd_journal_close(m_journalContext);
99 m_journalContext = nullptr;
100 }
101}
102
103JournaldWatcher::LogEntry JournaldWatcherPrivate::retrieveEntry()
104{
105 JournaldWatcher::LogEntry result;
106
107 // Advance:
108 int r = sd_journal_next(m_journalContext);
109 if (r == 0)
110 return result;
111
112 const void *rawData;
113 size_t length;
114 SD_JOURNAL_FOREACH_DATA(m_journalContext, rawData, length) {
115 QByteArray tmp = QByteArray::fromRawData(static_cast<const char *>(rawData), length);
116 int offset = tmp.indexOf('=');
117 if (offset < 0)
118 continue;
119 result.insert(tmp.left(offset), tmp.mid(offset + 1));
120 }
121
122 return result;
123}
124
125} // namespace Internal
126
127using namespace Internal;
128
129static JournaldWatcherPrivate *d = nullptr;
130
131JournaldWatcher::~JournaldWatcher()
132{
133 d->teardown();
134
135 m_instance = nullptr;
136
137 delete d;
138 d = nullptr;
139}
140
141JournaldWatcher *JournaldWatcher::instance()
142{
143 return m_instance;
144}
145
146const QByteArray &JournaldWatcher::machineId()
147{
148 static QByteArray id;
149 if (id.isEmpty()) {
150 sd_id128 sdId;
151 if (sd_id128_get_machine(&sdId) == 0) {
152 id.resize(32);
153 sd_id128_to_string(sdId, id.data());
154 }
155 }
156 return id;
157}
158
159bool JournaldWatcher::subscribe(QObject *subscriber, const Subscription &subscription)
160{
161 // Check that we do not have that subscriber yet:
162 int pos = Utils::indexOf(d->m_subscriptions,
163 [subscriber](const JournaldWatcherPrivate::SubscriberInformation &info) {
164 return info.subscriber == subscriber;
165 });
166 QTC_ASSERT(pos < 0, return false);
167
168 d->m_subscriptions.append(JournaldWatcherPrivate::SubscriberInformation(subscriber, subscription));
169 connect(subscriber, &QObject::destroyed, m_instance, &JournaldWatcher::unsubscribe);
170
171 return true;
172}
173
174void JournaldWatcher::unsubscribe(QObject *subscriber)
175{
176 int pos = Utils::indexOf(d->m_subscriptions,
177 [subscriber](const JournaldWatcherPrivate::SubscriberInformation &info) {
178 return info.subscriber == subscriber;
179 });
180 if (pos < 0)
181 return;
182
183 d->m_subscriptions.removeAt(pos);
184}
185
186JournaldWatcher::JournaldWatcher()
187{
188 QTC_ASSERT(!m_instance, return);
189
190 d = new JournaldWatcherPrivate;
191 m_instance = this;
192
193 if (!d->setup())
194 d->teardown();
195 else
196 connect(d->m_notifier, &QSocketNotifier::activated,
197 m_instance, &JournaldWatcher::handleEntry);
198 m_instance->handleEntry(); // advance to the end of file...
199}
200
201void JournaldWatcher::handleEntry()
202{
203 if (!d->m_notifier)
204 return;
205
206 if (sd_journal_process(d->m_journalContext) != SD_JOURNAL_APPEND)
207 return;
208
209 LogEntry logEntry;
210 forever {
211 logEntry = d->retrieveEntry();
212 if (logEntry.isEmpty())
213 break;
214
215 foreach (const JournaldWatcherPrivate::SubscriberInformation &info, d->m_subscriptions)
216 info.subscription(logEntry);
217 }
218}
219
220} // namespace ProjectExplorer
221

Warning: That file was not part of the compilation database. It may have many parsing errors.