@@ -1,221 +0,0 @@
1−/*
2− * Copyright 2016 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU Lesser General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU Lesser General Public License for more details.
12− *
13− * You should have received a copy of the GNU Lesser General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15−*/
16−
17−#include "consolelog.h"
18−
19−#include <unistd.h>
20−#include <stdio.h>
21−#include <thread>
22−#include <iostream>
23−#include <sys/ioctl.h>
24−
25−#include <functional>
26−
27−namespace
28−{
29−
30−int wait_fd(std::function<int(void)> fn)
31−{
32− int ret = -1;
33− bool fd_blocked = false;
34− do
35− {
36− ret = fn();
37− fd_blocked = (errno == EINTR || errno == EBUSY);
38− if (fd_blocked)
39− QThread::msleep(10);
40− }
41− while (ret < 0);
42− return ret;
43−}
44−
45−#define PIPE_WAIT(val) wait_fd(std::bind(pipe, val))
46−#define DUP_WAIT(fd) wait_fd(std::bind(dup, fd))
47−#define DUP2_WAIT(fd1, fd2) wait_fd(std::bind(dup2, fd1, fd2))
48−#define CLOSE_WAIT(fd) wait_fd(std::bind(close, fd))
49−
50−}
51−
52−LogRedirector::LogRedirector()
53− : m_ref(0)
54− , m_stop(false)
55−{
56− setObjectName("ConsoleLog");
57−
58− // make stdout & stderr streams unbuffered
59− // so that we don't need to flush the streams
60− // before capture and after capture
61− // (fflush can cause a deadlock if the stream is currently being used)
62− setvbuf(stdout,NULL,_IONBF,0);
63− setvbuf(stderr,NULL,_IONBF,0);
64−}
65−
66−void LogRedirector::run()
67−{
68− PIPE_WAIT(m_pipe);
69−
70− int oldStdOut = DUP_WAIT(fileno(stdout));
71− int oldStdErr = DUP_WAIT(fileno(stderr));
72−
73− DUP2_WAIT(m_pipe[WRITE], fileno(stdout));
74− DUP2_WAIT(m_pipe[WRITE], fileno(stderr));
75− CLOSE_WAIT(m_pipe[WRITE]);
76−
77− while(true) {
78− {
79− QMutexLocker lock(&m_mutex);
80− if (m_stop) break;
81− }
82− checkLog();
83− QThread::msleep(50);
84− }
85−
86− DUP2_WAIT(oldStdOut, fileno(stdout));
87− DUP2_WAIT(oldStdErr, fileno(stderr));
88−
89− CLOSE_WAIT(oldStdOut);
90− CLOSE_WAIT(oldStdErr);
91− CLOSE_WAIT(m_pipe[READ]);
92−}
93−
94−
95−void LogRedirector::checkLog()
96−{
97− // Do not allow read to block with no data.
98− // If we stop the thread while waiting a read,
99− // it will block the main thread waiting for this thread to stop.
100− int count = 0;
101− ioctl(m_pipe[READ], FIONREAD, &count);
102− if (count <= 0) return;
103−
104− std::string captured;
105− std::string buf;
106− const int bufSize = 1024;
107− buf.resize(bufSize);
108− int bytesRead = 0;
109− bytesRead = read(m_pipe[READ], &(*buf.begin()), bufSize);
110− while(bytesRead == bufSize)
111− {
112− captured += buf;
113− bytesRead = 0;
114− bytesRead = read(m_pipe[READ], &(*buf.begin()), bufSize);
115− }
116− if (bytesRead > 0)
117− {
118− buf.resize(bytesRead);
119− captured += buf;
120− }
121−
122− if (!captured.empty()) {
123− Q_EMIT log(QString::fromStdString(captured));
124− }
125−}
126−
127−LogRedirector *LogRedirector::instance()
128−{
129− static LogRedirector* log = nullptr;
130− if (!log) {
131− log = new LogRedirector();
132− }
133− return log;
134−}
135−
136−void LogRedirector::add(ConsoleLog* logger)
137−{
138− QMutexLocker lock(&m_mutex);
139− connect(this, &LogRedirector::log, logger, &ConsoleLog::logged, Qt::UniqueConnection);
140−
141− m_ref++;
142− if (!LogRedirector::instance()->isRunning()) {
143− m_stop = false;
144− LogRedirector::instance()->start();
145− }
146−}
147−
148−void LogRedirector::remove(ConsoleLog* logger)
149−{
150− QMutexLocker lock(&m_mutex);
151− disconnect(this, &LogRedirector::log, logger, &ConsoleLog::logged);
152−
153− m_ref = qMax(m_ref-1, 0);
154− if (m_ref == 0 && LogRedirector::instance()->isRunning()) {
155− m_stop = true;
156− lock.unlock();
157− LogRedirector::instance()->wait();
158− }
159−}
160−
161−ConsoleLog::ConsoleLog(QObject *parent)
162− : QObject(parent)
163− , m_enabled(false)
164− , m_maxLines(60)
165−{
166− auto updateEnabled = [this]() {
167− if (m_enabled) {
168− LogRedirector::instance()->add(this);
169− } else {
170− LogRedirector::instance()->remove(this);
171− }
172− };
173− connect(this, &ConsoleLog::enabledChanged, this, updateEnabled);
174−}
175−
176−ConsoleLog::~ConsoleLog()
177−{
178− if (m_enabled) {
179− LogRedirector::instance()->remove(this);
180− }
181−}
182−
183−void ConsoleLog::setEnabled(bool enabled)
184−{
185− if (m_enabled == enabled) {
186− return;
187− }
188−
189− m_enabled = enabled;
190− Q_EMIT enabledChanged();
191−}
192−
193−QString ConsoleLog::out() const
194−{
195− return m_out.join("\n");
196−}
197−
198−void ConsoleLog::setMaxLines(int maxLines)
199−{
200− if (m_maxLines == maxLines) {
201− return;
202− }
203−
204− m_maxLines = maxLines;
205− while (m_out.count() > m_maxLines) {
206− m_out.removeLast();
207− }
208− Q_EMIT outChanged();
209− Q_EMIT maxLinesChanged();
210−}
211−
212−void ConsoleLog::logged(QString captured)
213−{
214− QStringList li = captured.split("\n", QString::SkipEmptyParts);
215− li << m_out;
216− m_out = li;
217− while (m_out.count() > m_maxLines) {
218− m_out.removeLast();
219− }
220− Q_EMIT outChanged();
221−}