mirror of https://github.com/nymea/nymea.git
Add status report generator and finish debug interface improvements
parent
b2cb22df66
commit
a0c2143c4b
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
function selectSection(event, section) {
|
||||
|
||||
console.log("Selected tab " + section)
|
||||
console.log("Selected tab " + section);
|
||||
|
||||
var i, tabcontent, tablinks;
|
||||
tabcontent = document.getElementsByClassName("tabcontent");
|
||||
|
|
@ -42,6 +42,7 @@ function selectSection(event, section) {
|
|||
event.currentTarget.className += " active";
|
||||
}
|
||||
|
||||
|
||||
/* ========================================================================*/
|
||||
/* Websocket connection
|
||||
/* ========================================================================*/
|
||||
|
|
@ -51,14 +52,15 @@ var webSocketConnected = false;
|
|||
|
||||
function toggleWebsocketConnection() {
|
||||
if (webSocketConnected) {
|
||||
disconnectWebsocket()
|
||||
disconnectWebsocket();
|
||||
} else {
|
||||
connectWebsocket()
|
||||
connectWebsocket();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function connectWebsocket() {
|
||||
var urlString = "ws://" + window.location.hostname + ":2626"
|
||||
var urlString = "ws://" + window.location.hostname + ":2626";
|
||||
console.log("Connecting to: " + urlString);
|
||||
|
||||
try {
|
||||
|
|
@ -84,7 +86,7 @@ function connectWebsocket() {
|
|||
var message = messageEvent.data;
|
||||
console.log("WebSocket data received: " + message);
|
||||
document.getElementById("logsTextArea").value += message;
|
||||
document.getElementById("logsTextArea").scrollTop = document.getElementById("logsTextArea").scrollHeight
|
||||
document.getElementById("logsTextArea").scrollTop = document.getElementById("logsTextArea").scrollHeight;
|
||||
};
|
||||
|
||||
} catch (exception) {
|
||||
|
|
@ -93,9 +95,10 @@ function connectWebsocket() {
|
|||
|
||||
}
|
||||
|
||||
|
||||
function disconnectWebsocket() {
|
||||
console.log("Disconnecting from: " + webSocket.url);
|
||||
webSocket.close()
|
||||
webSocket.close();
|
||||
webSocketConnected = false;
|
||||
document.getElementById("toggleLogsButton").innerHTML = "Start logs";
|
||||
}
|
||||
|
|
@ -110,6 +113,7 @@ function showFile(path) {
|
|||
window.open(path, '_blank');
|
||||
}
|
||||
|
||||
|
||||
function downloadFile(filePath, fileName) {
|
||||
console.log("Download file requested " + filePath + " --> " + fileName);
|
||||
var element = document.createElement('a');
|
||||
|
|
@ -121,6 +125,63 @@ function downloadFile(filePath, fileName) {
|
|||
document.body.removeChild(element);
|
||||
}
|
||||
|
||||
|
||||
function generateReport() {
|
||||
console.log("Requesting to generate report file " + "/debug/report");
|
||||
|
||||
var button = document.getElementById("generateReportButton");
|
||||
var textArea = document.getElementById("generateReportTextArea");
|
||||
|
||||
// Request report file generation
|
||||
var reportGenerateRequest = new XMLHttpRequest();
|
||||
reportGenerateRequest.open("GET", "/debug/report", true);
|
||||
reportGenerateRequest.send(null);
|
||||
|
||||
button.disabled = true;
|
||||
textArea.value = "";
|
||||
|
||||
reportGenerateRequest.onreadystatechange = function() {
|
||||
if (reportGenerateRequest.readyState == 4) {
|
||||
console.log("Report generation finished with " + reportGenerateRequest.status);
|
||||
|
||||
if (reportGenerateRequest.status != 200) {
|
||||
console.log("Report generation finished with error.");
|
||||
textArea.value = "Something went wrong :(";
|
||||
button.disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(reportGenerateRequest.responseText);
|
||||
var responseMap = JSON.parse(reportGenerateRequest.responseText);
|
||||
var fileName = responseMap['fileName'];
|
||||
var fileSize = responseMap['fileSize'];
|
||||
var md5Sum = responseMap['md5sum'];
|
||||
|
||||
console.log("Report generation finished. " + fileName + " " + fileSize + "B | " + md5Sum)
|
||||
|
||||
textArea.value = "Report generated successfully: " + fileName + "\n";
|
||||
textArea.value += "\n";
|
||||
textArea.value += "Size: " + fileSize + " Bytes" + "\n";
|
||||
textArea.value += "MD5 checksum: " + md5Sum + "\n";
|
||||
|
||||
// Now download the generated report
|
||||
var fileRequestUrl = "/debug/report?filename=" + fileName;
|
||||
console.log("Download report file " + fileRequestUrl);
|
||||
var element = document.createElement('a');
|
||||
element.setAttribute('href', fileRequestUrl);
|
||||
element.setAttribute('download', fileName);
|
||||
element.style.display = 'none';
|
||||
document.body.appendChild(element);
|
||||
element.click();
|
||||
document.body.removeChild(element);
|
||||
|
||||
// Enable button again
|
||||
button.disabled = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/* ========================================================================*/
|
||||
/* Network test functions
|
||||
/* ========================================================================*/
|
||||
|
|
@ -136,16 +197,17 @@ function startPingTest() {
|
|||
var request = new XMLHttpRequest();
|
||||
request.open("GET", "/debug/ping", true);
|
||||
request.send(null);
|
||||
button.disabled = true
|
||||
button.disabled = true;
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState == 4) {
|
||||
console.log(request.responseText);
|
||||
textArea.value = request.responseText
|
||||
button.disabled = false
|
||||
textArea.value = request.responseText;
|
||||
button.disabled = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function startDigTest() {
|
||||
console.log("Start dig test");
|
||||
var textArea = document.getElementById("digTextArea");
|
||||
|
|
@ -158,16 +220,17 @@ function startDigTest() {
|
|||
var request = new XMLHttpRequest();
|
||||
request.open("GET", "/debug/dig", true);
|
||||
request.send(null);
|
||||
button.disabled = true
|
||||
button.disabled = true;
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState == 4) {
|
||||
console.log(request.responseText);
|
||||
textArea.value = request.responseText
|
||||
button.disabled = false
|
||||
textArea.value = request.responseText;
|
||||
button.disabled = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function startTracePathTest() {
|
||||
console.log("Start trace path test");
|
||||
var textArea = document.getElementById("tracePathTextArea");
|
||||
|
|
@ -180,12 +243,12 @@ function startTracePathTest() {
|
|||
var request = new XMLHttpRequest();
|
||||
request.open("GET", "/debug/tracepath", true);
|
||||
request.send(null);
|
||||
button.disabled = true
|
||||
button.disabled = true;
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState == 4) {
|
||||
console.log(request.responseText);
|
||||
textArea.value = request.responseText
|
||||
button.disabled = false
|
||||
textArea.value = request.responseText;
|
||||
button.disabled = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ textarea {
|
|||
cursor: pointer;
|
||||
padding: 14px 16px;
|
||||
opacity: 0.8;
|
||||
transition: 0.3s;
|
||||
transition: 0.5s;
|
||||
font-size: 18px;
|
||||
font-family: "Ubuntu", Helvetica, "Helvetica Neue", Arial;
|
||||
}
|
||||
|
|
@ -102,7 +102,7 @@ textarea {
|
|||
|
||||
.tabcontent {
|
||||
display: none;
|
||||
animation: fadeEffect 0.5s;
|
||||
animation: fadeEffect 0.8s;
|
||||
}
|
||||
|
||||
@keyframes fadeEffect {
|
||||
|
|
@ -113,6 +113,7 @@ textarea {
|
|||
.console-textarea {
|
||||
color: white;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
font-family: "Ubuntu Mono", "monospace";
|
||||
font-size: 100%;
|
||||
|
|
@ -128,6 +129,8 @@ textarea {
|
|||
|
||||
.warning {
|
||||
background-color: #ed3146;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 10px;
|
||||
opacity: 0.8;
|
||||
width: 80%;
|
||||
|
|
@ -175,7 +178,7 @@ textarea {
|
|||
|
||||
.download-path-column {
|
||||
float: left;
|
||||
width: 40%;
|
||||
width: 30%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,12 +59,15 @@ Depends: libqt5network5,
|
|||
logrotate,
|
||||
avahi-daemon,
|
||||
bluez,
|
||||
tar,
|
||||
iputils-tracepath,
|
||||
iputils-ping,
|
||||
dnsutils,
|
||||
nymea-translations,
|
||||
libnymea1 (= ${binary:Version}),
|
||||
${shlibs:Depends},
|
||||
${misc:Depends}
|
||||
Recommends: nymea-webinterface,
|
||||
nymea-cli,
|
||||
Recommends: nymea-cli,
|
||||
network-manager
|
||||
Replaces: guhd
|
||||
Description: An open source IoT server - daemon
|
||||
|
|
|
|||
|
|
@ -0,0 +1,266 @@
|
|||
#include "debugreportgenerator.h"
|
||||
#include "loggingcategories.h"
|
||||
#include "nymeasettings.h"
|
||||
#include "nymeacore.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QTimer>
|
||||
#include <QDateTime>
|
||||
#include <QCryptographicHash>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
DebugReportGenerator::DebugReportGenerator(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DebugReportGenerator::~DebugReportGenerator()
|
||||
{
|
||||
cleanupReport();
|
||||
}
|
||||
|
||||
QByteArray DebugReportGenerator::reportFileData() const
|
||||
{
|
||||
return m_reportFileData;
|
||||
}
|
||||
|
||||
QString DebugReportGenerator::reportFileName()
|
||||
{
|
||||
return m_reportFileName;
|
||||
}
|
||||
|
||||
QString DebugReportGenerator::md5Sum() const
|
||||
{
|
||||
return m_md5Sum;
|
||||
}
|
||||
|
||||
void DebugReportGenerator::generateReport()
|
||||
{
|
||||
qCDebug(dcDebugServer()) << "Start generating debug report";
|
||||
m_reportFileName = QDateTime::currentDateTime().toString("yyyyMMddhhmm") + "-nymea-debug-report";
|
||||
|
||||
m_reportDirectory.setPath(QString("/tmp/%1").arg(m_reportFileName));
|
||||
if (!m_reportDirectory.exists()) {
|
||||
qCDebug(dcDebugServer()) << "Create temporary folder to collect the data" << m_reportDirectory.path();
|
||||
if (!m_reportDirectory.mkpath(m_reportDirectory.path())) {
|
||||
qCWarning(dcDebugServer()) << "Could not create output directory for debug report";
|
||||
emit finished(false);
|
||||
return;
|
||||
}
|
||||
m_reportDirectory.mkpath(m_reportDirectory.path() + "/config");
|
||||
m_reportDirectory.mkpath(m_reportDirectory.path() + "/network");
|
||||
m_reportDirectory.mkpath(m_reportDirectory.path() + "/logs");
|
||||
}
|
||||
|
||||
m_reportFileName += ".tag.gz";
|
||||
|
||||
saveConfigs();
|
||||
saveLogFiles();
|
||||
saveEnv();
|
||||
|
||||
QProcess *pingProcess = new QProcess(this);
|
||||
pingProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
connect(pingProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onPingProcessFinished(int,QProcess::ExitStatus)));
|
||||
|
||||
QProcess *digProcess = new QProcess(this);
|
||||
digProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
connect(digProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onDigProcessFinished(int,QProcess::ExitStatus)));
|
||||
|
||||
QProcess *tracePathProcess = new QProcess(this);
|
||||
tracePathProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
connect(tracePathProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onTracePathProcessFinished(int,QProcess::ExitStatus)));
|
||||
|
||||
m_runningProcesses.append(pingProcess);
|
||||
m_runningProcesses.append(digProcess);
|
||||
m_runningProcesses.append(tracePathProcess);
|
||||
|
||||
pingProcess->start("ping", { "-c", "4", "nymea.io" } );
|
||||
digProcess->start("dig", { "nymea.io" } );
|
||||
tracePathProcess->start("tracepath", { "nymea.io" } );
|
||||
}
|
||||
|
||||
void DebugReportGenerator::copyFileToReportDirectory(const QString &fileName, const QString &subDirectory)
|
||||
{
|
||||
QFileInfo fileInfo(fileName);
|
||||
if (fileInfo.exists()) {
|
||||
QString destination = m_reportDirectory.path() + "/" + subDirectory;
|
||||
if (!QFile::copy(fileName, destination + "/" + fileInfo.fileName())) {
|
||||
qCWarning(dcDebugServer()) << "Could not copy file" << fileName << "to" << destination;
|
||||
} else {
|
||||
qCDebug(dcDebugServer()) << "Copy file" << fileName << "-->" << destination;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugReportGenerator::verifyRunningProcessesFinished()
|
||||
{
|
||||
if (m_runningProcesses.isEmpty()) {
|
||||
qCDebug(dcDebugServer()) << "All async processes are finished. Start compressing the file.";
|
||||
m_compressProcess = new QProcess(this);
|
||||
m_compressProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
m_compressProcess->setWorkingDirectory("/tmp");
|
||||
connect(m_compressProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onCompressProcessFinished(int, QProcess::ExitStatus)));
|
||||
m_compressProcess->start("tar", { "-zcf", m_reportFileName, "-C", "/tmp/", m_reportDirectory.dirName() } );
|
||||
qCDebug(dcDebugServer()) << "Execut command" << m_compressProcess->program() << m_compressProcess->arguments();
|
||||
}
|
||||
}
|
||||
|
||||
void DebugReportGenerator::saveLogFiles()
|
||||
{
|
||||
QDir logDir("/var/log/");
|
||||
QStringList syslogFiles = logDir.entryList(QStringList() << "syslog*" << "nymea.*", QDir::Files);
|
||||
foreach (const QString &logFile, syslogFiles) {
|
||||
copyFileToReportDirectory(logDir.path() + "/" + logFile, "logs");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DebugReportGenerator::saveConfigs()
|
||||
{
|
||||
// Start copy files setting files
|
||||
copyFileToReportDirectory(NymeaSettings(NymeaSettings::SettingsRoleGlobal).fileName(), "config");
|
||||
copyFileToReportDirectory(NymeaSettings(NymeaSettings::SettingsRoleDevices).fileName(), "config");
|
||||
copyFileToReportDirectory(NymeaSettings(NymeaSettings::SettingsRoleDeviceStates).fileName(), "config");
|
||||
copyFileToReportDirectory(NymeaSettings(NymeaSettings::SettingsRoleRules).fileName(), "config");
|
||||
copyFileToReportDirectory(NymeaSettings(NymeaSettings::SettingsRolePlugins).fileName(), "config");
|
||||
copyFileToReportDirectory(NymeaSettings(NymeaSettings::SettingsRoleTags).fileName(), "config");
|
||||
copyFileToReportDirectory(NymeaCore::instance()->configuration()->logDBName(), "config");
|
||||
}
|
||||
|
||||
void DebugReportGenerator::saveEnv()
|
||||
{
|
||||
QFile outputFile(m_reportDirectory.path() + "/env.txt");
|
||||
if (!outputFile.open(QIODevice::ReadWrite)) {
|
||||
qCWarning(dcDebugServer()) << "Could not open env file" << outputFile.fileName();
|
||||
return;
|
||||
}
|
||||
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
QTextStream stream(&outputFile);
|
||||
foreach(const QString &key, env.keys()) {
|
||||
qCDebug(dcDebugServer()) << "Process environment:" << key << "-->" << env.value(key);
|
||||
stream << key << "=" << env.value(key) << "\n";
|
||||
}
|
||||
outputFile.close();
|
||||
}
|
||||
|
||||
void DebugReportGenerator::cleanupReport()
|
||||
{
|
||||
QFile reportFile("/tmp/" + m_reportFileName);
|
||||
if (reportFile.exists()) {
|
||||
qCDebug(dcDebugServer()) << "Delete report file" << reportFile.fileName();
|
||||
if (!reportFile.remove()) {
|
||||
qCWarning(dcDebugServer()) << "Could not delete report file" << reportFile.fileName();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_reportDirectory.exists()) {
|
||||
qCDebug(dcDebugServer()) << "Clean up report directory" << m_reportDirectory.path();
|
||||
if (!m_reportDirectory.removeRecursively()) {
|
||||
qCWarning(dcDebugServer()) << "Could not delete report directory" << m_reportDirectory.path();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugReportGenerator::onPingProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
QProcess *process = static_cast<QProcess *>(sender());
|
||||
qCDebug(dcDebugServer()) << "Ping process finished" << exitCode << exitStatus;
|
||||
QByteArray processOutput = process->readAll();
|
||||
|
||||
QFile outputFile(m_reportDirectory.path() + "/network/ping.txt");
|
||||
if (!outputFile.open(QIODevice::ReadWrite)) {
|
||||
qCWarning(dcDebugServer()) << "Could not open ping file" << outputFile.fileName();
|
||||
} else {
|
||||
qCDebug(dcDebugServer()) << "Write result into file" << outputFile.fileName();
|
||||
outputFile.write(processOutput);
|
||||
outputFile.close();
|
||||
}
|
||||
|
||||
m_runningProcesses.removeAll(process);
|
||||
process->deleteLater();
|
||||
process = nullptr;
|
||||
|
||||
verifyRunningProcessesFinished();
|
||||
}
|
||||
|
||||
void DebugReportGenerator::onDigProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
QProcess *process = static_cast<QProcess *>(sender());
|
||||
qCDebug(dcDebugServer()) << "Dig process finished" << exitCode << exitStatus;
|
||||
QByteArray processOutput = process->readAll();
|
||||
|
||||
QFile outputFile(m_reportDirectory.path() + "/network/dns-lookup.txt");
|
||||
if (!outputFile.open(QIODevice::ReadWrite)) {
|
||||
qCWarning(dcDebugServer()) << "Could not open dig file" << outputFile.fileName();
|
||||
} else {
|
||||
qCDebug(dcDebugServer()) << "Write result into file" << outputFile.fileName();
|
||||
outputFile.write(processOutput);
|
||||
outputFile.close();
|
||||
}
|
||||
|
||||
m_runningProcesses.removeAll(process);
|
||||
process->deleteLater();
|
||||
process = nullptr;
|
||||
|
||||
verifyRunningProcessesFinished();
|
||||
}
|
||||
|
||||
void DebugReportGenerator::onTracePathProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
QProcess *process = static_cast<QProcess *>(sender());
|
||||
qCDebug(dcDebugServer()) << "Tracepath process finished" << exitCode << exitStatus;
|
||||
QByteArray processOutput = process->readAll();
|
||||
|
||||
|
||||
QFile outputFile(m_reportDirectory.path() + "/network/tracepath.txt");
|
||||
if (!outputFile.open(QIODevice::ReadWrite)) {
|
||||
qCWarning(dcDebugServer()) << "Could not open dig file" << outputFile.fileName();
|
||||
} else {
|
||||
qCDebug(dcDebugServer()) << "Write result into file" << outputFile.fileName();
|
||||
outputFile.write(processOutput);
|
||||
outputFile.close();
|
||||
}
|
||||
|
||||
m_runningProcesses.removeAll(process);
|
||||
process->deleteLater();
|
||||
process = nullptr;
|
||||
|
||||
verifyRunningProcessesFinished();
|
||||
}
|
||||
|
||||
void DebugReportGenerator::onCompressProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
QProcess *process = static_cast<QProcess *>(sender());
|
||||
qCDebug(dcDebugServer()) << "Compress process finished" << exitCode << exitStatus;
|
||||
|
||||
qCDebug(dcDebugServer()) << "Clean up report directory" << m_reportDirectory.path();
|
||||
if (!m_reportDirectory.removeRecursively()) {
|
||||
qCWarning(dcDebugServer()) << "Could not delete report directory" << m_reportDirectory.path();
|
||||
}
|
||||
|
||||
// Read the file
|
||||
QFile reportFile("/tmp/" + m_reportFileName);
|
||||
if (!reportFile.open(QIODevice::ReadOnly)) {
|
||||
qCWarning(dcDebugServer()) << "Could not open report file name for reading" << reportFile.fileName();
|
||||
emit finished(false);
|
||||
} else {
|
||||
m_reportFileData = reportFile.readAll();
|
||||
m_md5Sum = QString::fromUtf8(QCryptographicHash::hash(m_reportFileData, QCryptographicHash::Md5).toHex());
|
||||
qCDebug(dcDebugServer()) << "File generated successfully" << reportFile.fileName() << m_reportFileData.size() << "B" << m_md5Sum;
|
||||
emit finished(true);
|
||||
}
|
||||
|
||||
reportFile.close();
|
||||
|
||||
// Todo: start expire timer
|
||||
QTimer::singleShot(30000, this, &DebugReportGenerator::timeout);
|
||||
|
||||
process->deleteLater();
|
||||
process = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef DEBUGREPORTGENERATOR_H
|
||||
#define DEBUGREPORTGENERATOR_H
|
||||
|
||||
#include <QDir>
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
class DebugReportGenerator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DebugReportGenerator(QObject *parent = nullptr);
|
||||
~DebugReportGenerator();
|
||||
|
||||
QByteArray reportFileData() const;
|
||||
QString reportFileName();
|
||||
QString md5Sum() const;
|
||||
|
||||
void generateReport();
|
||||
|
||||
private:
|
||||
QDir m_reportDirectory;
|
||||
QString m_reportFileName;
|
||||
|
||||
QProcess *m_compressProcess = nullptr;
|
||||
QList<QProcess *> m_runningProcesses;
|
||||
|
||||
QByteArray m_reportFileData;
|
||||
QString m_md5Sum;
|
||||
|
||||
void copyFileToReportDirectory(const QString &fileName, const QString &subDirectory = QString());
|
||||
void verifyRunningProcessesFinished();
|
||||
|
||||
void saveLogFiles();
|
||||
void saveConfigs();
|
||||
void saveEnv();
|
||||
|
||||
void cleanupReport();
|
||||
|
||||
signals:
|
||||
void finished(bool success);
|
||||
void timeout();
|
||||
|
||||
private slots:
|
||||
void onPingProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void onDigProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void onTracePathProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void onCompressProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // DEBUGREPORTGENERATOR_H
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
#include <QXmlStreamWriter>
|
||||
#include <QCoreApplication>
|
||||
#include <QMessageLogger>
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
||||
QtMessageHandler DebugServerHandler::s_oldLogMessageHandler = nullptr;
|
||||
QList<QWebSocket*> DebugServerHandler::s_websocketClients;
|
||||
|
|
@ -39,25 +39,17 @@ namespace nymeaserver {
|
|||
DebugServerHandler::DebugServerHandler(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
m_websocketServer = new QWebSocketServer("Debug server", QWebSocketServer::NonSecureMode, this);
|
||||
connect(m_websocketServer, &QWebSocketServer::newConnection, this, &DebugServerHandler::onWebsocketClientConnected);
|
||||
|
||||
// FIXME: enable disable server with debug server
|
||||
if (!m_websocketServer->listen(QHostAddress::Any, 2626)) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: The debug server websocket interface could not listen on" << m_websocketServer->serverUrl().toString();
|
||||
}
|
||||
qCDebug(dcWebServer()) << "DebugServer: Started debug server websocket interface on" << m_websocketServer->serverUrl().toString();
|
||||
|
||||
s_oldLogMessageHandler = qInstallMessageHandler(&logMessageHandler);
|
||||
connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::debugServerEnabledChanged, this, &DebugServerHandler::onDebugServerEnabledChanged);
|
||||
onDebugServerEnabledChanged(NymeaCore::instance()->configuration()->debugServerEnabled());
|
||||
}
|
||||
|
||||
HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
||||
HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, const QUrlQuery &requestQuery)
|
||||
{
|
||||
qCDebug(dcWebServer()) << "DebugServer: Debug request for" << requestPath;
|
||||
qCDebug(dcDebugServer()) << "Debug request for" << requestPath;
|
||||
|
||||
// Check if debug page request
|
||||
if (requestPath == "/debug" || requestPath == "/debug/") {
|
||||
qCDebug(dcWebServer()) << "DebugServer: Create debug interface page";
|
||||
qCDebug(dcDebugServer()) << "Create debug interface page";
|
||||
// Fallback default debug page
|
||||
HttpReply *reply = RestResource::createSuccessReply();
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
|
|
@ -67,10 +59,10 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
|
||||
// Check if this is a logdb requested
|
||||
if (requestPath.startsWith("/debug/logdb.sql")) {
|
||||
qCDebug(dcWebServer()) << "DebugServer: Loading" << NymeaCore::instance()->configuration()->logDBName();
|
||||
qCDebug(dcDebugServer()) << "Loading" << NymeaCore::instance()->configuration()->logDBName();
|
||||
QFile logDatabaseFile(NymeaCore::instance()->configuration()->logDBName());
|
||||
if (!logDatabaseFile.exists()) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: Could not read log database file for debug download" << NymeaCore::instance()->configuration()->logDBName() << "file does not exist.";
|
||||
qCWarning(dcDebugServer()) << "Could not read log database file for debug download" << NymeaCore::instance()->configuration()->logDBName() << "file does not exist.";
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
//: The HTTP error message of the debug interface. The %1 represents the file name.
|
||||
|
|
@ -79,7 +71,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
}
|
||||
|
||||
if (!logDatabaseFile.open(QFile::ReadOnly)) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: Could not read log database file for debug download" << NymeaCore::instance()->configuration()->logDBName();
|
||||
qCWarning(dcDebugServer()) << "Could not read log database file for debug download" << NymeaCore::instance()->configuration()->logDBName();
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
//: The HTTP error message of the debug interface. The %1 represents the file name.
|
||||
|
|
@ -100,10 +92,10 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
// Check if this is a syslog requested
|
||||
if (requestPath.startsWith("/debug/syslog")) {
|
||||
QString syslogFileName = "/var/log/syslog";
|
||||
qCDebug(dcWebServer()) << "DebugServer: Loading" << syslogFileName;
|
||||
qCDebug(dcDebugServer()) << "Loading" << syslogFileName;
|
||||
QFile syslogFile(syslogFileName);
|
||||
if (!syslogFile.exists()) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: Could not read log database file for debug download" << syslogFileName << "file does not exist.";
|
||||
qCWarning(dcDebugServer()) << "Could not read log database file for debug download" << syslogFileName << "file does not exist.";
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(syslogFileName)));
|
||||
|
|
@ -111,7 +103,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
}
|
||||
|
||||
if (!syslogFile.open(QFile::ReadOnly)) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: Could not read syslog file for debug download" << syslogFileName;
|
||||
qCWarning(dcDebugServer()) << "Could not read syslog file for debug download" << syslogFileName;
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(syslogFileName)));
|
||||
|
|
@ -131,10 +123,10 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
if (requestPath.startsWith("/debug/settings")) {
|
||||
if (requestPath.startsWith("/debug/settings/devices")) {
|
||||
QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRoleDevices).fileName();
|
||||
qCDebug(dcWebServer()) << "DebugServer: Loading" << settingsFileName;
|
||||
qCDebug(dcDebugServer()) << "Loading" << settingsFileName;
|
||||
QFile settingsFile(settingsFileName);
|
||||
if (!settingsFile.exists()) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: Could not read file for debug download" << settingsFileName << "file does not exist.";
|
||||
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName)));
|
||||
|
|
@ -142,7 +134,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
}
|
||||
|
||||
if (!settingsFile.open(QFile::ReadOnly)) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: Could not read file for debug download" << settingsFileName;
|
||||
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName;
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName)));
|
||||
|
|
@ -160,10 +152,10 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
|
||||
if (requestPath.startsWith("/debug/settings/rules")) {
|
||||
QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRoleRules).fileName();
|
||||
qCDebug(dcWebServer()) << "DebugServer: Loading" << settingsFileName;
|
||||
qCDebug(dcDebugServer()) << "Loading" << settingsFileName;
|
||||
QFile settingsFile(settingsFileName);
|
||||
if (!settingsFile.exists()) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: Could not read file for debug download" << settingsFileName << "file does not exist.";
|
||||
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName)));
|
||||
|
|
@ -171,7 +163,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
}
|
||||
|
||||
if (!settingsFile.open(QFile::ReadOnly)) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: Could not read file for debug download" << settingsFileName;
|
||||
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName;
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName)));
|
||||
|
|
@ -189,10 +181,10 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
|
||||
if (requestPath.startsWith("/debug/settings/nymead")) {
|
||||
QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRoleGlobal).fileName();
|
||||
qCDebug(dcWebServer()) << "DebugServer: Loading" << settingsFileName;
|
||||
qCDebug(dcDebugServer()) << "Loading" << settingsFileName;
|
||||
QFile settingsFile(settingsFileName);
|
||||
if (!settingsFile.exists()) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: Could not read file for debug download" << settingsFileName << "file does not exist.";
|
||||
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName)));
|
||||
|
|
@ -200,7 +192,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
}
|
||||
|
||||
if (!settingsFile.open(QFile::ReadOnly)) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: Could not read file for debug download" << settingsFileName;
|
||||
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName;
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName)));
|
||||
|
|
@ -218,10 +210,10 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
|
||||
if (requestPath.startsWith("/debug/settings/devicestates")) {
|
||||
QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRoleDeviceStates).fileName();
|
||||
qCDebug(dcWebServer()) << "DebugServer: Loading" << settingsFileName;
|
||||
qCDebug(dcDebugServer()) << "Loading" << settingsFileName;
|
||||
QFile settingsFile(settingsFileName);
|
||||
if (!settingsFile.exists()) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: Could not read file for debug download" << settingsFileName << "file does not exist.";
|
||||
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName)));
|
||||
|
|
@ -229,7 +221,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
}
|
||||
|
||||
if (!settingsFile.open(QFile::ReadOnly)) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: Could not read file for debug download" << settingsFileName;
|
||||
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName;
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName)));
|
||||
|
|
@ -247,10 +239,10 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
|
||||
if (requestPath.startsWith("/debug/settings/plugins")) {
|
||||
QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRolePlugins).fileName();
|
||||
qCDebug(dcWebServer()) << "DebugServer: Loading" << settingsFileName;
|
||||
qCDebug(dcDebugServer()) << "Loading" << settingsFileName;
|
||||
QFile settingsFile(settingsFileName);
|
||||
if (!settingsFile.exists()) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: Could not read file for debug download" << settingsFileName << "file does not exist.";
|
||||
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName)));
|
||||
|
|
@ -258,7 +250,36 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
}
|
||||
|
||||
if (!settingsFile.open(QFile::ReadOnly)) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: Could not read file for debug download" << settingsFileName;
|
||||
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName;
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName)));
|
||||
return reply;
|
||||
}
|
||||
|
||||
QByteArray settingsFileData = settingsFile.readAll();
|
||||
settingsFile.close();
|
||||
|
||||
HttpReply *reply = RestResource::createSuccessReply();
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/plain");
|
||||
reply->setPayload(settingsFileData);
|
||||
return reply;
|
||||
}
|
||||
|
||||
if (requestPath.startsWith("/debug/settings/tags")) {
|
||||
QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRoleTags).fileName();
|
||||
qCDebug(dcDebugServer()) << "Loading" << settingsFileName;
|
||||
QFile settingsFile(settingsFileName);
|
||||
if (!settingsFile.exists()) {
|
||||
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName)));
|
||||
return reply;
|
||||
}
|
||||
|
||||
if (!settingsFile.open(QFile::ReadOnly)) {
|
||||
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName;
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
|
||||
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
|
||||
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName)));
|
||||
|
|
@ -280,7 +301,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
if (m_pingProcess || m_pingReply)
|
||||
return RestResource::createErrorReply(HttpReply::InternalServerError);
|
||||
|
||||
qCDebug(dcWebServer()) << "DebugServer: Start ping nymea.io process";
|
||||
qCDebug(dcDebugServer()) << "Start ping nymea.io process";
|
||||
m_pingProcess = new QProcess(this);
|
||||
m_pingProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
connect(m_pingProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onPingProcessFinished(int,QProcess::ExitStatus)));
|
||||
|
|
@ -295,7 +316,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
if (m_digProcess || m_digReply)
|
||||
return RestResource::createErrorReply(HttpReply::InternalServerError);
|
||||
|
||||
qCDebug(dcWebServer()) << "DebugServer: Start dig nymea.io process";
|
||||
qCDebug(dcDebugServer()) << "Start dig nymea.io process";
|
||||
m_digProcess = new QProcess(this);
|
||||
m_digProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
connect(m_digProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onDigProcessFinished(int,QProcess::ExitStatus)));
|
||||
|
|
@ -310,7 +331,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
if (m_tracePathProcess || m_tracePathReply)
|
||||
return RestResource::createErrorReply(HttpReply::InternalServerError);
|
||||
|
||||
qCDebug(dcWebServer()) << "DebugServer: Start tracepath nymea.io process";
|
||||
qCDebug(dcDebugServer()) << "Start tracepath nymea.io process";
|
||||
m_tracePathProcess = new QProcess(this);
|
||||
m_tracePathProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
connect(m_tracePathProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onTracePathProcessFinished(int,QProcess::ExitStatus)));
|
||||
|
|
@ -320,6 +341,38 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
return m_tracePathReply;
|
||||
}
|
||||
|
||||
if (requestPath.startsWith("/debug/report")) {
|
||||
// Check if download request or generate request
|
||||
if (requestQuery.hasQueryItem("filename")) {
|
||||
QString fileName = requestQuery.queryItemValue("filename");
|
||||
qCDebug(dcDebugServer()) << "Report download request for" << fileName;
|
||||
|
||||
if (m_finishedReportGenerators.contains(fileName)) {
|
||||
HttpReply *downloadReportReply = RestResource::createSuccessReply();
|
||||
DebugReportGenerator *generator = m_finishedReportGenerators.take(fileName);
|
||||
downloadReportReply->setPayload(generator->reportFileData());
|
||||
downloadReportReply->setHeader(HttpReply::ContentTypeHeader, "application/tar+gzip;");
|
||||
generator->deleteLater();
|
||||
|
||||
return downloadReportReply;
|
||||
} else {
|
||||
qCWarning(dcDebugServer()) << "The requested file does not exist any more" << fileName;
|
||||
HttpReply *downloadReportReply = RestResource::createErrorReply(HttpReply::NotFound);
|
||||
return downloadReportReply;
|
||||
}
|
||||
|
||||
} else {
|
||||
DebugReportGenerator *debugReportGenerator = new DebugReportGenerator(this);
|
||||
connect(debugReportGenerator, &DebugReportGenerator::finished, this, &DebugServerHandler::onDebugReportGeneratorFinished);
|
||||
connect(debugReportGenerator, &DebugReportGenerator::timeout, this, &DebugServerHandler::onDebugReportGeneratorTimeout);
|
||||
debugReportGenerator->generateReport();
|
||||
|
||||
HttpReply *debugReportReply = RestResource::createAsyncReply();
|
||||
m_runningReportGenerators.insert(debugReportGenerator, debugReportReply);
|
||||
|
||||
return debugReportReply;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this is a resource file request
|
||||
if (resourceFileExits(requestPath)) {
|
||||
|
|
@ -327,25 +380,45 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
|
|||
}
|
||||
|
||||
// If nothing matches, redirect to /debug page
|
||||
qCWarning(dcWebServer()) << "DebugServer: Resource for debug interface not found. Redirecting to /debug";
|
||||
qCWarning(dcDebugServer()) << "Resource for debug interface not found. Redirecting to /debug";
|
||||
HttpReply *reply = RestResource::createErrorReply(HttpReply::PermanentRedirect);
|
||||
reply->setHeader(HttpReply::LocationHeader, "/debug");
|
||||
return reply;
|
||||
}
|
||||
|
||||
void DebugServerHandler::logMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message) {
|
||||
s_oldLogMessageHandler(type, context, message);
|
||||
void DebugServerHandler::logMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message)
|
||||
{
|
||||
s_oldLogMessageHandler(type, context, message);
|
||||
|
||||
foreach (QWebSocket *client, s_websocketClients) {
|
||||
client->sendTextMessage(message + "\n");
|
||||
}
|
||||
QString finalMessage;
|
||||
switch (type) {
|
||||
case QtDebugMsg:
|
||||
finalMessage = QString(" I | %1: %2\n").arg(context.category).arg(message);
|
||||
break;
|
||||
case QtInfoMsg:
|
||||
finalMessage = QString(" I | %1: %2\n").arg(context.category).arg(message);
|
||||
break;
|
||||
case QtWarningMsg:
|
||||
finalMessage = QString(" W | %1: %2\n").arg(context.category).arg(message);
|
||||
break;
|
||||
case QtCriticalMsg:
|
||||
finalMessage = QString(" C | %1: %2\n").arg(context.category).arg(message);
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
finalMessage = QString(" F | %1: %2\n").arg(context.category).arg(message);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (QWebSocket *client, s_websocketClients) {
|
||||
client->sendTextMessage(finalMessage);
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray DebugServerHandler::loadResourceData(const QString &resourceFileName)
|
||||
{
|
||||
QFile resourceFile(QString(":%1").arg(resourceFileName));
|
||||
if (!resourceFile.open(QFile::ReadOnly)) {
|
||||
qCWarning(dcWebServer()) << "DebugServer: Could not open resource file" << resourceFile.fileName();
|
||||
qCWarning(dcDebugServer()) << "Could not open resource file" << resourceFile.fileName();
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
|
|
@ -387,6 +460,144 @@ HttpReply *DebugServerHandler::processDebugFileRequest(const QString &requestPat
|
|||
return reply;
|
||||
}
|
||||
|
||||
|
||||
void DebugServerHandler::onDebugServerEnabledChanged(bool enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
m_websocketServer = new QWebSocketServer("Debug server", QWebSocketServer::NonSecureMode, this);
|
||||
connect(m_websocketServer, &QWebSocketServer::newConnection, this, &DebugServerHandler::onWebsocketClientConnected);
|
||||
if (!m_websocketServer->listen(QHostAddress::Any, 2626)) {
|
||||
qCWarning(dcDebugServer()) << "The debug server websocket interface could not listen on" << m_websocketServer->serverUrl().toString();
|
||||
m_websocketServer->deleteLater();
|
||||
m_websocketServer = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcDebugServer()) << "The debug server websocket interface has been started on" << m_websocketServer->serverUrl().toString();
|
||||
} else {
|
||||
if (m_websocketServer) {
|
||||
m_websocketServer->close();
|
||||
qCDebug(dcDebugServer()) << "The debug server websocket interface has been closed" << m_websocketServer->serverUrl().toString();
|
||||
m_websocketServer->deleteLater();
|
||||
m_websocketServer = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugServerHandler::onWebsocketClientConnected()
|
||||
{
|
||||
QWebSocket *client = m_websocketServer->nextPendingConnection();
|
||||
|
||||
if (s_websocketClients.isEmpty()) {
|
||||
qCDebug(dcDebugServer()) << "Install debug message handler for live logs.";
|
||||
s_oldLogMessageHandler = qInstallMessageHandler(&logMessageHandler);
|
||||
}
|
||||
|
||||
s_websocketClients.append(client);
|
||||
qCDebug(dcDebugServer()) << "New websocket client connected:" << client->peerAddress().toString();
|
||||
|
||||
connect(client, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onWebsocketClientError(QAbstractSocket::SocketError)));
|
||||
connect(client, &QWebSocket::disconnected, this, &DebugServerHandler::onWebsocketClientDisconnected);
|
||||
}
|
||||
|
||||
void DebugServerHandler::onWebsocketClientDisconnected()
|
||||
{
|
||||
QWebSocket *client = static_cast<QWebSocket *>(sender());
|
||||
qCDebug(dcDebugServer()) << "Websocket client disconnected" << client->peerAddress().toString();
|
||||
s_websocketClients.removeAll(client);
|
||||
client->deleteLater();
|
||||
|
||||
if (s_websocketClients.isEmpty()) {
|
||||
qCDebug(dcDebugServer()) << "Uninstall debug message handler for live logs and restore default message handler";
|
||||
qInstallMessageHandler(s_oldLogMessageHandler);
|
||||
s_oldLogMessageHandler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void DebugServerHandler::onWebsocketClientError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
QWebSocket *client = static_cast<QWebSocket *>(sender());
|
||||
qCWarning(dcDebugServer()) << "Websocket client error" << client->peerAddress().toString() << error << client->errorString();
|
||||
}
|
||||
|
||||
void DebugServerHandler::onPingProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
qCDebug(dcDebugServer()) << "Ping process finished" << exitCode << exitStatus;
|
||||
QByteArray processOutput = m_pingProcess->readAll();
|
||||
qCDebug(dcDebugServer()) << "Ping output:" << endl << qUtf8Printable(processOutput);
|
||||
|
||||
m_pingReply->setPayload(processOutput);
|
||||
m_pingReply->setHttpStatusCode(HttpReply::Ok);
|
||||
m_pingReply->finished();
|
||||
m_pingReply = nullptr;
|
||||
|
||||
m_pingProcess->deleteLater();
|
||||
m_pingProcess = nullptr;
|
||||
}
|
||||
|
||||
void DebugServerHandler::onDigProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
qCDebug(dcDebugServer()) << "Dig process finished" << exitCode << exitStatus;
|
||||
QByteArray processOutput = m_digProcess->readAll();
|
||||
qCDebug(dcDebugServer()) << "Dig output:" << endl << qUtf8Printable(processOutput);
|
||||
|
||||
m_digReply->setPayload(processOutput);
|
||||
m_digReply->setHttpStatusCode(HttpReply::Ok);
|
||||
m_digReply->finished();
|
||||
m_digReply = nullptr;
|
||||
|
||||
m_digProcess->deleteLater();
|
||||
m_digProcess = nullptr;
|
||||
}
|
||||
|
||||
void DebugServerHandler::onTracePathProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
qCDebug(dcDebugServer()) << "Tracepath process finished" << exitCode << exitStatus;
|
||||
QByteArray processOutput = m_tracePathProcess->readAll();
|
||||
qCDebug(dcDebugServer()) << "Tracepath output:" << endl << qUtf8Printable(processOutput);
|
||||
|
||||
m_tracePathReply->setPayload(processOutput);
|
||||
m_tracePathReply->setHttpStatusCode(HttpReply::Ok);
|
||||
m_tracePathReply->finished();
|
||||
m_tracePathReply = nullptr;
|
||||
|
||||
m_tracePathProcess->deleteLater();
|
||||
m_tracePathProcess = nullptr;
|
||||
}
|
||||
|
||||
void DebugServerHandler::onDebugReportGeneratorFinished(bool success)
|
||||
{
|
||||
DebugReportGenerator *debugReportGenerator = static_cast<DebugReportGenerator *>(sender());
|
||||
qCDebug(dcDebugServer()) << "Report generation finished" << (success ? "successfully" : "with error") << debugReportGenerator->reportFileName();
|
||||
HttpReply *httpReply = m_runningReportGenerators.take(debugReportGenerator);
|
||||
|
||||
if (success) {
|
||||
QVariantMap reportInformation;
|
||||
reportInformation.insert("fileName", debugReportGenerator->reportFileName());
|
||||
reportInformation.insert("fileSize", debugReportGenerator->reportFileData().size());
|
||||
reportInformation.insert("md5sum", debugReportGenerator->md5Sum());
|
||||
httpReply->setHttpStatusCode(HttpReply::Ok);
|
||||
httpReply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
|
||||
httpReply->setPayload(QJsonDocument::fromVariant(reportInformation).toJson(QJsonDocument::Indented));
|
||||
|
||||
m_finishedReportGenerators.insert(debugReportGenerator->reportFileName(), debugReportGenerator);
|
||||
} else {
|
||||
httpReply->setHttpStatusCode(HttpReply::InternalServerError);
|
||||
}
|
||||
|
||||
httpReply->finished();
|
||||
}
|
||||
|
||||
void DebugServerHandler::onDebugReportGeneratorTimeout()
|
||||
{
|
||||
DebugReportGenerator *debugReportGenerator = static_cast<DebugReportGenerator *>(sender());
|
||||
qCWarning(dcDebugServer()) << "Report generation timeouted. Cleaning up" << debugReportGenerator->reportFileName();
|
||||
if (m_finishedReportGenerators.values().contains(debugReportGenerator)) {
|
||||
m_finishedReportGenerators.remove(debugReportGenerator->reportFileName());
|
||||
debugReportGenerator->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray DebugServerHandler::createDebugXmlDocument()
|
||||
{
|
||||
QByteArray data;
|
||||
|
|
@ -432,8 +643,8 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
|
|||
writer.writeEmptyElement("link");
|
||||
writer.writeAttribute("rel", "icon");
|
||||
writer.writeAttribute("type", "image/png");
|
||||
writer.writeAttribute("sizes", "64x64");
|
||||
writer.writeAttribute("href", "/debug/favicons/favicon-64x64.png");
|
||||
writer.writeAttribute("sizes", "96x96");
|
||||
writer.writeAttribute("href", "/debug/favicons/favicon-96x96.png");
|
||||
|
||||
writer.writeEmptyElement("link");
|
||||
writer.writeAttribute("rel", "icon");
|
||||
|
|
@ -447,6 +658,45 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
|
|||
writer.writeAttribute("sizes", "196x196");
|
||||
writer.writeAttribute("href", "/debug/favicons/favicon-196x196.png");
|
||||
|
||||
writer.writeEmptyElement("link");
|
||||
writer.writeAttribute("rel", "apple-touch-icon-precomposed");
|
||||
writer.writeAttribute("sizes", "57x57");
|
||||
writer.writeAttribute("href", "/debug/favicons/apple-touch-icon-57x57.png");
|
||||
|
||||
writer.writeEmptyElement("link");
|
||||
writer.writeAttribute("rel", "apple-touch-icon-precomposed");
|
||||
writer.writeAttribute("sizes", "60x60");
|
||||
writer.writeAttribute("href", "/debug/favicons/apple-touch-icon-60x60.png");
|
||||
|
||||
writer.writeEmptyElement("link");
|
||||
writer.writeAttribute("rel", "apple-touch-icon-precomposed");
|
||||
writer.writeAttribute("sizes", "72x72");
|
||||
writer.writeAttribute("href", "/debug/favicons/apple-touch-icon-72x72.png");
|
||||
|
||||
writer.writeEmptyElement("link");
|
||||
writer.writeAttribute("rel", "apple-touch-icon-precomposed");
|
||||
writer.writeAttribute("sizes", "76x76");
|
||||
writer.writeAttribute("href", "/debug/favicons/apple-touch-icon-76x76.png");
|
||||
|
||||
writer.writeEmptyElement("link");
|
||||
writer.writeAttribute("rel", "apple-touch-icon-precomposed");
|
||||
writer.writeAttribute("sizes", "114x114");
|
||||
writer.writeAttribute("href", "/debug/favicons/apple-touch-icon-114x114.png");
|
||||
|
||||
writer.writeEmptyElement("link");
|
||||
writer.writeAttribute("rel", "apple-touch-icon-precomposed");
|
||||
writer.writeAttribute("sizes", "120x120");
|
||||
writer.writeAttribute("href", "/debug/favicons/apple-touch-icon-144x144.png");
|
||||
|
||||
writer.writeEmptyElement("link");
|
||||
writer.writeAttribute("rel", "apple-touch-icon-precomposed");
|
||||
writer.writeAttribute("sizes", "144x144");
|
||||
writer.writeAttribute("href", "/debug/favicons/apple-touch-icon-144x144.png");
|
||||
|
||||
writer.writeEmptyElement("link");
|
||||
writer.writeAttribute("rel", "apple-touch-icon-precomposed");
|
||||
writer.writeAttribute("sizes", "152x152");
|
||||
writer.writeAttribute("href", "/debug/favicons/apple-touch-icon-152x152.png");
|
||||
|
||||
//: The header title of the debug server interface
|
||||
writer.writeTextElement("title", tr("Debug nymea"));
|
||||
|
|
@ -541,9 +791,9 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
|
|||
writer.writeEndElement(); // div warning
|
||||
|
||||
|
||||
writer.writeEmptyElement("hr");
|
||||
|
||||
// System information section
|
||||
writer.writeEmptyElement("hr");
|
||||
//: The server information section of the debug interface
|
||||
writer.writeTextElement("h2", tr("Server information"));
|
||||
writer.writeEmptyElement("hr");
|
||||
|
|
@ -677,6 +927,54 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
|
|||
}
|
||||
|
||||
writer.writeEndElement(); // table
|
||||
|
||||
// Generate report
|
||||
writer.writeEmptyElement("hr");
|
||||
//: In the server information section of the debug interface
|
||||
writer.writeTextElement("h2", tr("Generate report"));
|
||||
writer.writeEmptyElement("hr");
|
||||
|
||||
writer.writeTextElement("p", tr("If you want to provide all the debug information to a developer, you can generate a report file, "
|
||||
"which contains all information needed for reproducing a system and get information about possible problems."));
|
||||
|
||||
// Warning
|
||||
writer.writeStartElement("div");
|
||||
writer.writeAttribute("class", "warning");
|
||||
// Warning image
|
||||
writer.writeStartElement("div");
|
||||
writer.writeAttribute("class", "warning-image-area");
|
||||
writer.writeEmptyElement("img");
|
||||
writer.writeAttribute("class", "warning-image");
|
||||
writer.writeAttribute("src", "/debug/warning.svg");
|
||||
writer.writeEndElement(); // div warning image
|
||||
// Warning message
|
||||
writer.writeStartElement("div");
|
||||
writer.writeAttribute("class", "warning-message");
|
||||
//: The warning message of the debug interface
|
||||
writer.writeCharacters(tr("Do not share these generated information public, since they can contain sensible data and should be shared very carefully and only with people you trust!"));
|
||||
writer.writeEndElement(); // div warning message
|
||||
writer.writeEndElement(); // div warning
|
||||
|
||||
// Generate report button
|
||||
writer.writeStartElement("button");
|
||||
writer.writeAttribute("class", "button");
|
||||
writer.writeAttribute("type", "button");
|
||||
writer.writeAttribute("id", "generateReportButton");
|
||||
writer.writeAttribute("onClick", "generateReport()");
|
||||
//: The generate debug report button text of the debug interface
|
||||
writer.writeCharacters(tr("Generate report file"));
|
||||
writer.writeEndElement(); // button
|
||||
|
||||
// Logs output
|
||||
writer.writeStartElement("textarea");
|
||||
writer.writeAttribute("class", "console-textarea");
|
||||
writer.writeAttribute("id", "generateReportTextArea");
|
||||
writer.writeAttribute("readonly", "readonly");
|
||||
writer.writeAttribute("rows", "5");
|
||||
writer.writeCharacters("");
|
||||
writer.writeEndElement(); // textarea
|
||||
|
||||
|
||||
writer.writeEndElement(); // information-section
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -1027,8 +1325,58 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
|
|||
writer.writeEndElement(); // button
|
||||
writer.writeEndElement(); // form
|
||||
writer.writeEndElement(); // div show-button-column
|
||||
|
||||
writer.writeEndElement(); // div download-row
|
||||
|
||||
|
||||
|
||||
// Download row
|
||||
writer.writeStartElement("div");
|
||||
writer.writeAttribute("class", "download-row");
|
||||
|
||||
writer.writeStartElement("div");
|
||||
writer.writeAttribute("class", "download-name-column");
|
||||
//: The tag settings download description of the debug interface
|
||||
writer.writeTextElement("p", tr("Tag settings"));
|
||||
writer.writeEndElement(); // div download-name-column
|
||||
|
||||
writer.writeStartElement("div");
|
||||
writer.writeAttribute("class", "download-path-column");
|
||||
writer.writeTextElement("p", NymeaSettings(NymeaSettings::SettingsRoleTags).fileName());
|
||||
writer.writeEndElement(); // div download-path-column
|
||||
|
||||
writer.writeStartElement("div");
|
||||
writer.writeAttribute("class", "download-button-column");
|
||||
writer.writeStartElement("form");
|
||||
writer.writeAttribute("class", "download-button");
|
||||
writer.writeStartElement("button");
|
||||
writer.writeAttribute("class", "button");
|
||||
writer.writeAttribute("type", "button");
|
||||
if (!QFile::exists(NymeaSettings(NymeaSettings::SettingsRoleTags).fileName())) {
|
||||
writer.writeAttribute("disabled", "true");
|
||||
}
|
||||
writer.writeAttribute("onClick", "downloadFile('/debug/settings/tags', 'tags.conf')");
|
||||
writer.writeCharacters(tr("Download"));
|
||||
writer.writeEndElement(); // button
|
||||
writer.writeEndElement(); // form
|
||||
writer.writeEndElement(); // div download-button-column
|
||||
|
||||
writer.writeStartElement("div");
|
||||
writer.writeAttribute("class", "show-button-column");
|
||||
writer.writeStartElement("form");
|
||||
writer.writeAttribute("class", "show-button");
|
||||
writer.writeStartElement("button");
|
||||
writer.writeAttribute("class", "button");
|
||||
writer.writeAttribute("type", "button");
|
||||
if (!QFile::exists(NymeaSettings(NymeaSettings::SettingsRoleDeviceStates).fileName())) {
|
||||
writer.writeAttribute("disabled", "true");
|
||||
}
|
||||
writer.writeAttribute("onClick", "showFile('/debug/settings/tags')");
|
||||
writer.writeCharacters(tr("Show"));
|
||||
writer.writeEndElement(); // button
|
||||
writer.writeEndElement(); // form
|
||||
writer.writeEndElement(); // div show-button-column
|
||||
writer.writeEndElement(); // div download-row
|
||||
|
||||
writer.writeEndElement(); // downloads-section
|
||||
|
||||
|
||||
|
|
@ -1048,11 +1396,10 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
|
|||
writer.writeTextElement("p", tr("This section allows you to perform different network connectivity tests in order "
|
||||
"to find out if the device where nymea is running has full network connectivity."));
|
||||
|
||||
|
||||
// Ping section
|
||||
writer.writeEmptyElement("hr");
|
||||
//: The ping section of the debug interface
|
||||
writer.writeTextElement("h3", tr("Ping nymea.io"));
|
||||
writer.writeTextElement("h3", tr("Ping"));
|
||||
writer.writeEmptyElement("hr");
|
||||
|
||||
writer.writeTextElement("p", tr("This test makes four ping attempts to the nymea.io server."));
|
||||
|
|
@ -1080,7 +1427,7 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
|
|||
// Dig section
|
||||
writer.writeEmptyElement("hr");
|
||||
//: The DNS lookup section of the debug interface
|
||||
writer.writeTextElement("h3", tr("DNS lookup for nymea.io"));
|
||||
writer.writeTextElement("h3", tr("DNS lookup"));
|
||||
writer.writeEmptyElement("hr");
|
||||
|
||||
writer.writeTextElement("p", tr("This test makes a dynamic name server lookup for nymea.io."));
|
||||
|
|
@ -1108,7 +1455,7 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
|
|||
// Trace section
|
||||
writer.writeEmptyElement("hr");
|
||||
//: The trace section of the debug interface
|
||||
writer.writeTextElement("h3", tr("Trace path to nymea.io"));
|
||||
writer.writeTextElement("h3", tr("Trace path"));
|
||||
writer.writeEmptyElement("hr");
|
||||
|
||||
writer.writeTextElement("p", tr("This test showes the trace path from the nymea device to the nymea.io server."));
|
||||
|
|
@ -1263,74 +1610,4 @@ QByteArray DebugServerHandler::createErrorXmlDocument(HttpReply::HttpStatusCode
|
|||
return data;
|
||||
}
|
||||
|
||||
void DebugServerHandler::onWebsocketClientConnected()
|
||||
{
|
||||
QWebSocket *client = m_websocketServer->nextPendingConnection();
|
||||
s_websocketClients.append(client);
|
||||
qCDebug(dcWebServer()) << "DebugServer: New websocket client connected:" << client->peerAddress().toString();
|
||||
|
||||
connect(client, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onWebsocketClientError(QAbstractSocket::SocketError)));
|
||||
connect(client, &QWebSocket::disconnected, this, &DebugServerHandler::onWebsocketClientDisconnected);
|
||||
}
|
||||
|
||||
void DebugServerHandler::onWebsocketClientDisconnected()
|
||||
{
|
||||
QWebSocket *client = static_cast<QWebSocket *>(sender());
|
||||
qCDebug(dcWebServer()) << "DebugServer: Websocket client disconnected" << client->peerAddress().toString();
|
||||
s_websocketClients.removeAll(client);
|
||||
client->deleteLater();
|
||||
}
|
||||
|
||||
void DebugServerHandler::onWebsocketClientError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
QWebSocket *client = static_cast<QWebSocket *>(sender());
|
||||
qCWarning(dcWebServer()) << "DebugServer: Websocket client error" << client->peerAddress().toString() << error << client->errorString();
|
||||
}
|
||||
|
||||
void DebugServerHandler::onPingProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
qCDebug(dcWebServer()) << "DebugServer: Ping process finished" << exitCode << exitStatus;
|
||||
QByteArray processOutput = m_pingProcess->readAll();
|
||||
qCDebug(dcWebServer()) << "DebugServer: Ping output:" << endl << qUtf8Printable(processOutput);
|
||||
|
||||
m_pingReply->setPayload(processOutput);
|
||||
m_pingReply->setHttpStatusCode(HttpReply::Ok);
|
||||
m_pingReply->finished();
|
||||
m_pingReply = nullptr;
|
||||
|
||||
m_pingProcess->deleteLater();
|
||||
m_pingProcess = nullptr;
|
||||
}
|
||||
|
||||
void DebugServerHandler::onDigProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
qCDebug(dcWebServer()) << "DebugServer: Dig process finished" << exitCode << exitStatus;
|
||||
QByteArray processOutput = m_digProcess->readAll();
|
||||
qCDebug(dcWebServer()) << "DebugServer: Dig output:" << endl << qUtf8Printable(processOutput);
|
||||
|
||||
m_digReply->setPayload(processOutput);
|
||||
m_digReply->setHttpStatusCode(HttpReply::Ok);
|
||||
m_digReply->finished();
|
||||
m_digReply = nullptr;
|
||||
|
||||
m_digProcess->deleteLater();
|
||||
m_digProcess = nullptr;
|
||||
}
|
||||
|
||||
void DebugServerHandler::onTracePathProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
qCDebug(dcWebServer()) << "DebugServer: Tracepath process finished" << exitCode << exitStatus;
|
||||
QByteArray processOutput = m_tracePathProcess->readAll();
|
||||
qCDebug(dcWebServer()) << "DebugServer: Tracepath output:" << endl << qUtf8Printable(processOutput);
|
||||
|
||||
m_tracePathReply->setPayload(processOutput);
|
||||
m_tracePathReply->setHttpStatusCode(HttpReply::Ok);
|
||||
m_tracePathReply->finished();
|
||||
m_tracePathReply = nullptr;
|
||||
|
||||
m_tracePathProcess->deleteLater();
|
||||
m_tracePathProcess = nullptr;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,11 @@
|
|||
#include <QTimer>
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <QUrlQuery>
|
||||
#include <QWebSocketServer>
|
||||
|
||||
#include "httpreply.h"
|
||||
#include "debugreportgenerator.h"
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
|
|
@ -36,7 +38,7 @@ class DebugServerHandler : public QObject
|
|||
public:
|
||||
explicit DebugServerHandler(QObject *parent = nullptr);
|
||||
|
||||
HttpReply *processDebugRequest(const QString &requestPath);
|
||||
HttpReply *processDebugRequest(const QString &requestPath, const QUrlQuery &requestQuery);
|
||||
|
||||
private:
|
||||
static QtMessageHandler s_oldLogMessageHandler;
|
||||
|
|
@ -54,6 +56,9 @@ private:
|
|||
QProcess *m_tracePathProcess = nullptr;
|
||||
HttpReply *m_tracePathReply = nullptr;
|
||||
|
||||
QHash<DebugReportGenerator *, HttpReply *> m_runningReportGenerators;
|
||||
QHash<QString, DebugReportGenerator *> m_finishedReportGenerators;
|
||||
|
||||
QByteArray loadResourceData(const QString &resourceFileName);
|
||||
QString getResourceFileName(const QString &requestPath);
|
||||
bool resourceFileExits(const QString &requestPath);
|
||||
|
|
@ -64,6 +69,8 @@ private:
|
|||
QByteArray createErrorXmlDocument(HttpReply::HttpStatusCode statusCode, const QString &errorMessage);
|
||||
|
||||
private slots:
|
||||
void onDebugServerEnabledChanged(bool enabled);
|
||||
|
||||
void onWebsocketClientConnected();
|
||||
void onWebsocketClientDisconnected();
|
||||
void onWebsocketClientError(QAbstractSocket::SocketError error);
|
||||
|
|
@ -71,7 +78,8 @@ private slots:
|
|||
void onPingProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void onDigProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void onTracePathProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
void onDebugReportGeneratorFinished(bool success);
|
||||
void onDebugReportGeneratorTimeout();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ HEADERS += nymeacore.h \
|
|||
tagging/tag.h \
|
||||
jsonrpc/tagshandler.h \
|
||||
cloud/cloudtransport.h \
|
||||
debugreportgenerator.h
|
||||
|
||||
SOURCES += nymeacore.cpp \
|
||||
tcpserver.cpp \
|
||||
|
|
@ -178,3 +179,4 @@ SOURCES += nymeacore.cpp \
|
|||
tagging/tag.cpp \
|
||||
jsonrpc/tagshandler.cpp \
|
||||
cloud/cloudtransport.cpp \
|
||||
debugreportgenerator.cpp
|
||||
|
|
|
|||
|
|
@ -373,7 +373,6 @@ void WebServer::readClient()
|
|||
|
||||
// Check if this is a debug call
|
||||
if (request.url().path().startsWith("/debug")) {
|
||||
|
||||
// Check if debug server is enabled
|
||||
if (NymeaCore::instance()->configuration()->debugServerEnabled()) {
|
||||
// Verify methods
|
||||
|
|
@ -386,7 +385,8 @@ void WebServer::readClient()
|
|||
return;
|
||||
}
|
||||
|
||||
HttpReply *reply = NymeaCore::instance()->debugServerHandler()->processDebugRequest(request.url().path());
|
||||
qCDebug(dcDebugServer()) << "Request:" << request.url().toString();
|
||||
HttpReply *reply = NymeaCore::instance()->debugServerHandler()->processDebugRequest(request.url().path(), request.urlQuery());
|
||||
reply->setClientId(clientId);
|
||||
|
||||
// Handle async replies
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ Q_LOGGING_CATEGORY(dcLogEngine, "LogEngine")
|
|||
Q_LOGGING_CATEGORY(dcTcpServer, "TcpServer")
|
||||
Q_LOGGING_CATEGORY(dcTcpServerTraffic, "TcpServerTraffic")
|
||||
Q_LOGGING_CATEGORY(dcWebServer, "WebServer")
|
||||
Q_LOGGING_CATEGORY(dcDebugServer, "DebugServer")
|
||||
Q_LOGGING_CATEGORY(dcWebSocketServer, "WebSocketServer")
|
||||
Q_LOGGING_CATEGORY(dcWebSocketServerTraffic, "WebSocketServerTraffic")
|
||||
Q_LOGGING_CATEGORY(dcJsonRpc, "JsonRpc")
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ Q_DECLARE_LOGGING_CATEGORY(dcLogEngine)
|
|||
Q_DECLARE_LOGGING_CATEGORY(dcTcpServer)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcTcpServerTraffic)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcWebServer)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcDebugServer)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcWebSocketServer)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcWebSocketServerTraffic)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcJsonRpc)
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ int main(int argc, char *argv[])
|
|||
"TcpServer",
|
||||
"TcpServerTraffic",
|
||||
"WebServer",
|
||||
"DebugServer",
|
||||
"WebSocketServer",
|
||||
"WebSocketServerTraffic",
|
||||
"JsonRpc",
|
||||
|
|
@ -238,7 +239,7 @@ int main(int argc, char *argv[])
|
|||
bool startForeground = parser.isSet(foregroundOption);
|
||||
if (startForeground) {
|
||||
// inform about userid
|
||||
int userId = getuid();
|
||||
uint userId = getuid();
|
||||
if (userId != 0) {
|
||||
// check if config directory for logfile exists
|
||||
if (!QDir().mkpath(NymeaSettings::settingsPath())) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue