Add status report generator and finish debug interface improvements

This commit is contained in:
Simon Stürz 2018-11-15 11:33:36 +01:00 committed by Michael Zanetti
parent b2cb22df66
commit a0c2143c4b
12 changed files with 829 additions and 148 deletions

View File

@ -25,7 +25,7 @@
function selectSection(event, section) { function selectSection(event, section) {
console.log("Selected tab " + section) console.log("Selected tab " + section);
var i, tabcontent, tablinks; var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent"); tabcontent = document.getElementsByClassName("tabcontent");
@ -42,6 +42,7 @@ function selectSection(event, section) {
event.currentTarget.className += " active"; event.currentTarget.className += " active";
} }
/* ========================================================================*/ /* ========================================================================*/
/* Websocket connection /* Websocket connection
/* ========================================================================*/ /* ========================================================================*/
@ -51,14 +52,15 @@ var webSocketConnected = false;
function toggleWebsocketConnection() { function toggleWebsocketConnection() {
if (webSocketConnected) { if (webSocketConnected) {
disconnectWebsocket() disconnectWebsocket();
} else { } else {
connectWebsocket() connectWebsocket();
} }
} }
function connectWebsocket() { function connectWebsocket() {
var urlString = "ws://" + window.location.hostname + ":2626" var urlString = "ws://" + window.location.hostname + ":2626";
console.log("Connecting to: " + urlString); console.log("Connecting to: " + urlString);
try { try {
@ -84,7 +86,7 @@ function connectWebsocket() {
var message = messageEvent.data; var message = messageEvent.data;
console.log("WebSocket data received: " + message); console.log("WebSocket data received: " + message);
document.getElementById("logsTextArea").value += message; document.getElementById("logsTextArea").value += message;
document.getElementById("logsTextArea").scrollTop = document.getElementById("logsTextArea").scrollHeight document.getElementById("logsTextArea").scrollTop = document.getElementById("logsTextArea").scrollHeight;
}; };
} catch (exception) { } catch (exception) {
@ -93,9 +95,10 @@ function connectWebsocket() {
} }
function disconnectWebsocket() { function disconnectWebsocket() {
console.log("Disconnecting from: " + webSocket.url); console.log("Disconnecting from: " + webSocket.url);
webSocket.close() webSocket.close();
webSocketConnected = false; webSocketConnected = false;
document.getElementById("toggleLogsButton").innerHTML = "Start logs"; document.getElementById("toggleLogsButton").innerHTML = "Start logs";
} }
@ -110,6 +113,7 @@ function showFile(path) {
window.open(path, '_blank'); window.open(path, '_blank');
} }
function downloadFile(filePath, fileName) { function downloadFile(filePath, fileName) {
console.log("Download file requested " + filePath + " --> " + fileName); console.log("Download file requested " + filePath + " --> " + fileName);
var element = document.createElement('a'); var element = document.createElement('a');
@ -121,6 +125,63 @@ function downloadFile(filePath, fileName) {
document.body.removeChild(element); 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 /* Network test functions
/* ========================================================================*/ /* ========================================================================*/
@ -136,16 +197,17 @@ function startPingTest() {
var request = new XMLHttpRequest(); var request = new XMLHttpRequest();
request.open("GET", "/debug/ping", true); request.open("GET", "/debug/ping", true);
request.send(null); request.send(null);
button.disabled = true button.disabled = true;
request.onreadystatechange = function() { request.onreadystatechange = function() {
if (request.readyState == 4) { if (request.readyState == 4) {
console.log(request.responseText); console.log(request.responseText);
textArea.value = request.responseText textArea.value = request.responseText;
button.disabled = false button.disabled = false;
} }
}; };
} }
function startDigTest() { function startDigTest() {
console.log("Start dig test"); console.log("Start dig test");
var textArea = document.getElementById("digTextArea"); var textArea = document.getElementById("digTextArea");
@ -158,16 +220,17 @@ function startDigTest() {
var request = new XMLHttpRequest(); var request = new XMLHttpRequest();
request.open("GET", "/debug/dig", true); request.open("GET", "/debug/dig", true);
request.send(null); request.send(null);
button.disabled = true button.disabled = true;
request.onreadystatechange = function() { request.onreadystatechange = function() {
if (request.readyState == 4) { if (request.readyState == 4) {
console.log(request.responseText); console.log(request.responseText);
textArea.value = request.responseText textArea.value = request.responseText;
button.disabled = false button.disabled = false;
} }
}; };
} }
function startTracePathTest() { function startTracePathTest() {
console.log("Start trace path test"); console.log("Start trace path test");
var textArea = document.getElementById("tracePathTextArea"); var textArea = document.getElementById("tracePathTextArea");
@ -180,12 +243,12 @@ function startTracePathTest() {
var request = new XMLHttpRequest(); var request = new XMLHttpRequest();
request.open("GET", "/debug/tracepath", true); request.open("GET", "/debug/tracepath", true);
request.send(null); request.send(null);
button.disabled = true button.disabled = true;
request.onreadystatechange = function() { request.onreadystatechange = function() {
if (request.readyState == 4) { if (request.readyState == 4) {
console.log(request.responseText); console.log(request.responseText);
textArea.value = request.responseText textArea.value = request.responseText;
button.disabled = false button.disabled = false;
} }
}; };
} }

View File

@ -87,7 +87,7 @@ textarea {
cursor: pointer; cursor: pointer;
padding: 14px 16px; padding: 14px 16px;
opacity: 0.8; opacity: 0.8;
transition: 0.3s; transition: 0.5s;
font-size: 18px; font-size: 18px;
font-family: "Ubuntu", Helvetica, "Helvetica Neue", Arial; font-family: "Ubuntu", Helvetica, "Helvetica Neue", Arial;
} }
@ -102,7 +102,7 @@ textarea {
.tabcontent { .tabcontent {
display: none; display: none;
animation: fadeEffect 0.5s; animation: fadeEffect 0.8s;
} }
@keyframes fadeEffect { @keyframes fadeEffect {
@ -113,6 +113,7 @@ textarea {
.console-textarea { .console-textarea {
color: white; color: white;
margin-top: 20px; margin-top: 20px;
margin-bottom: 20px;
padding: 15px; padding: 15px;
font-family: "Ubuntu Mono", "monospace"; font-family: "Ubuntu Mono", "monospace";
font-size: 100%; font-size: 100%;
@ -128,6 +129,8 @@ textarea {
.warning { .warning {
background-color: #ed3146; background-color: #ed3146;
margin-top: 20px;
margin-bottom: 20px;
border-radius: 10px; border-radius: 10px;
opacity: 0.8; opacity: 0.8;
width: 80%; width: 80%;
@ -175,7 +178,7 @@ textarea {
.download-path-column { .download-path-column {
float: left; float: left;
width: 40%; width: 30%;
padding: 10px; padding: 10px;
} }

7
debian/control vendored
View File

@ -59,12 +59,15 @@ Depends: libqt5network5,
logrotate, logrotate,
avahi-daemon, avahi-daemon,
bluez, bluez,
tar,
iputils-tracepath,
iputils-ping,
dnsutils,
nymea-translations, nymea-translations,
libnymea1 (= ${binary:Version}), libnymea1 (= ${binary:Version}),
${shlibs:Depends}, ${shlibs:Depends},
${misc:Depends} ${misc:Depends}
Recommends: nymea-webinterface, Recommends: nymea-cli,
nymea-cli,
network-manager network-manager
Replaces: guhd Replaces: guhd
Description: An open source IoT server - daemon Description: An open source IoT server - daemon

View File

@ -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;
}
}

View File

@ -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

View File

@ -29,7 +29,7 @@
#include <QXmlStreamWriter> #include <QXmlStreamWriter>
#include <QCoreApplication> #include <QCoreApplication>
#include <QMessageLogger> #include <QMessageLogger>
#include <QJsonDocument>
QtMessageHandler DebugServerHandler::s_oldLogMessageHandler = nullptr; QtMessageHandler DebugServerHandler::s_oldLogMessageHandler = nullptr;
QList<QWebSocket*> DebugServerHandler::s_websocketClients; QList<QWebSocket*> DebugServerHandler::s_websocketClients;
@ -39,25 +39,17 @@ namespace nymeaserver {
DebugServerHandler::DebugServerHandler(QObject *parent) : DebugServerHandler::DebugServerHandler(QObject *parent) :
QObject(parent) QObject(parent)
{ {
m_websocketServer = new QWebSocketServer("Debug server", QWebSocketServer::NonSecureMode, this); connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::debugServerEnabledChanged, this, &DebugServerHandler::onDebugServerEnabledChanged);
connect(m_websocketServer, &QWebSocketServer::newConnection, this, &DebugServerHandler::onWebsocketClientConnected); onDebugServerEnabledChanged(NymeaCore::instance()->configuration()->debugServerEnabled());
// 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);
} }
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 // Check if debug page request
if (requestPath == "/debug" || requestPath == "/debug/") { if (requestPath == "/debug" || requestPath == "/debug/") {
qCDebug(dcWebServer()) << "DebugServer: Create debug interface page"; qCDebug(dcDebugServer()) << "Create debug interface page";
// Fallback default debug page // Fallback default debug page
HttpReply *reply = RestResource::createSuccessReply(); HttpReply *reply = RestResource::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/html"); reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
@ -67,10 +59,10 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
// Check if this is a logdb requested // Check if this is a logdb requested
if (requestPath.startsWith("/debug/logdb.sql")) { 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()); QFile logDatabaseFile(NymeaCore::instance()->configuration()->logDBName());
if (!logDatabaseFile.exists()) { 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); HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html"); reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
//: The HTTP error message of the debug interface. The %1 represents the file name. //: 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)) { 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); HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html"); reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
//: The HTTP error message of the debug interface. The %1 represents the file name. //: 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 // Check if this is a syslog requested
if (requestPath.startsWith("/debug/syslog")) { if (requestPath.startsWith("/debug/syslog")) {
QString syslogFileName = "/var/log/syslog"; QString syslogFileName = "/var/log/syslog";
qCDebug(dcWebServer()) << "DebugServer: Loading" << syslogFileName; qCDebug(dcDebugServer()) << "Loading" << syslogFileName;
QFile syslogFile(syslogFileName); QFile syslogFile(syslogFileName);
if (!syslogFile.exists()) { 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); HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html"); reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(syslogFileName))); 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)) { 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); HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html"); reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(syslogFileName))); 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")) {
if (requestPath.startsWith("/debug/settings/devices")) { if (requestPath.startsWith("/debug/settings/devices")) {
QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRoleDevices).fileName(); QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRoleDevices).fileName();
qCDebug(dcWebServer()) << "DebugServer: Loading" << settingsFileName; qCDebug(dcDebugServer()) << "Loading" << settingsFileName;
QFile settingsFile(settingsFileName); QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) { 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); HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html"); reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName))); 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)) { 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); HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html"); reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName))); 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")) { if (requestPath.startsWith("/debug/settings/rules")) {
QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRoleRules).fileName(); QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRoleRules).fileName();
qCDebug(dcWebServer()) << "DebugServer: Loading" << settingsFileName; qCDebug(dcDebugServer()) << "Loading" << settingsFileName;
QFile settingsFile(settingsFileName); QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) { 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); HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html"); reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName))); 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)) { 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); HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html"); reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName))); 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")) { if (requestPath.startsWith("/debug/settings/nymead")) {
QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRoleGlobal).fileName(); QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRoleGlobal).fileName();
qCDebug(dcWebServer()) << "DebugServer: Loading" << settingsFileName; qCDebug(dcDebugServer()) << "Loading" << settingsFileName;
QFile settingsFile(settingsFileName); QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) { 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); HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html"); reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName))); 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)) { 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); HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html"); reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName))); 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")) { if (requestPath.startsWith("/debug/settings/devicestates")) {
QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRoleDeviceStates).fileName(); QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRoleDeviceStates).fileName();
qCDebug(dcWebServer()) << "DebugServer: Loading" << settingsFileName; qCDebug(dcDebugServer()) << "Loading" << settingsFileName;
QFile settingsFile(settingsFileName); QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) { 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); HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html"); reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName))); 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)) { 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); HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html"); reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName))); 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")) { if (requestPath.startsWith("/debug/settings/plugins")) {
QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRolePlugins).fileName(); QString settingsFileName = NymeaSettings(NymeaSettings::SettingsRolePlugins).fileName();
qCDebug(dcWebServer()) << "DebugServer: Loading" << settingsFileName; qCDebug(dcDebugServer()) << "Loading" << settingsFileName;
QFile settingsFile(settingsFileName); QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) { 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); HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html"); reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName))); 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)) { 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); HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html"); reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName))); 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) if (m_pingProcess || m_pingReply)
return RestResource::createErrorReply(HttpReply::InternalServerError); 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 = new QProcess(this);
m_pingProcess->setProcessChannelMode(QProcess::MergedChannels); m_pingProcess->setProcessChannelMode(QProcess::MergedChannels);
connect(m_pingProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onPingProcessFinished(int,QProcess::ExitStatus))); 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) if (m_digProcess || m_digReply)
return RestResource::createErrorReply(HttpReply::InternalServerError); 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 = new QProcess(this);
m_digProcess->setProcessChannelMode(QProcess::MergedChannels); m_digProcess->setProcessChannelMode(QProcess::MergedChannels);
connect(m_digProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onDigProcessFinished(int,QProcess::ExitStatus))); 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) if (m_tracePathProcess || m_tracePathReply)
return RestResource::createErrorReply(HttpReply::InternalServerError); 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 = new QProcess(this);
m_tracePathProcess->setProcessChannelMode(QProcess::MergedChannels); m_tracePathProcess->setProcessChannelMode(QProcess::MergedChannels);
connect(m_tracePathProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onTracePathProcessFinished(int,QProcess::ExitStatus))); 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; 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 // Check if this is a resource file request
if (resourceFileExits(requestPath)) { if (resourceFileExits(requestPath)) {
@ -327,25 +380,45 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath)
} }
// If nothing matches, redirect to /debug page // 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); HttpReply *reply = RestResource::createErrorReply(HttpReply::PermanentRedirect);
reply->setHeader(HttpReply::LocationHeader, "/debug"); reply->setHeader(HttpReply::LocationHeader, "/debug");
return reply; return reply;
} }
void DebugServerHandler::logMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message) { void DebugServerHandler::logMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message)
s_oldLogMessageHandler(type, context, message); {
s_oldLogMessageHandler(type, context, message);
foreach (QWebSocket *client, s_websocketClients) { QString finalMessage;
client->sendTextMessage(message + "\n"); 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) QByteArray DebugServerHandler::loadResourceData(const QString &resourceFileName)
{ {
QFile resourceFile(QString(":%1").arg(resourceFileName)); QFile resourceFile(QString(":%1").arg(resourceFileName));
if (!resourceFile.open(QFile::ReadOnly)) { 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(); return QByteArray();
} }
@ -387,6 +460,144 @@ HttpReply *DebugServerHandler::processDebugFileRequest(const QString &requestPat
return reply; 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 DebugServerHandler::createDebugXmlDocument()
{ {
QByteArray data; QByteArray data;
@ -432,8 +643,8 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
writer.writeEmptyElement("link"); writer.writeEmptyElement("link");
writer.writeAttribute("rel", "icon"); writer.writeAttribute("rel", "icon");
writer.writeAttribute("type", "image/png"); writer.writeAttribute("type", "image/png");
writer.writeAttribute("sizes", "64x64"); writer.writeAttribute("sizes", "96x96");
writer.writeAttribute("href", "/debug/favicons/favicon-64x64.png"); writer.writeAttribute("href", "/debug/favicons/favicon-96x96.png");
writer.writeEmptyElement("link"); writer.writeEmptyElement("link");
writer.writeAttribute("rel", "icon"); writer.writeAttribute("rel", "icon");
@ -447,6 +658,45 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
writer.writeAttribute("sizes", "196x196"); writer.writeAttribute("sizes", "196x196");
writer.writeAttribute("href", "/debug/favicons/favicon-196x196.png"); 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 //: The header title of the debug server interface
writer.writeTextElement("title", tr("Debug nymea")); writer.writeTextElement("title", tr("Debug nymea"));
@ -541,9 +791,9 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
writer.writeEndElement(); // div warning writer.writeEndElement(); // div warning
writer.writeEmptyElement("hr");
// System information section // System information section
writer.writeEmptyElement("hr");
//: The server information section of the debug interface //: The server information section of the debug interface
writer.writeTextElement("h2", tr("Server information")); writer.writeTextElement("h2", tr("Server information"));
writer.writeEmptyElement("hr"); writer.writeEmptyElement("hr");
@ -677,6 +927,54 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
} }
writer.writeEndElement(); // table 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 writer.writeEndElement(); // information-section
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -1027,8 +1325,58 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
writer.writeEndElement(); // button writer.writeEndElement(); // button
writer.writeEndElement(); // form writer.writeEndElement(); // form
writer.writeEndElement(); // div show-button-column writer.writeEndElement(); // div show-button-column
writer.writeEndElement(); // div download-row 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 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 " 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.")); "to find out if the device where nymea is running has full network connectivity."));
// Ping section // Ping section
writer.writeEmptyElement("hr"); writer.writeEmptyElement("hr");
//: The ping section of the debug interface //: The ping section of the debug interface
writer.writeTextElement("h3", tr("Ping nymea.io")); writer.writeTextElement("h3", tr("Ping"));
writer.writeEmptyElement("hr"); writer.writeEmptyElement("hr");
writer.writeTextElement("p", tr("This test makes four ping attempts to the nymea.io server.")); writer.writeTextElement("p", tr("This test makes four ping attempts to the nymea.io server."));
@ -1080,7 +1427,7 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
// Dig section // Dig section
writer.writeEmptyElement("hr"); writer.writeEmptyElement("hr");
//: The DNS lookup section of the debug interface //: 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.writeEmptyElement("hr");
writer.writeTextElement("p", tr("This test makes a dynamic name server lookup for nymea.io.")); writer.writeTextElement("p", tr("This test makes a dynamic name server lookup for nymea.io."));
@ -1108,7 +1455,7 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
// Trace section // Trace section
writer.writeEmptyElement("hr"); writer.writeEmptyElement("hr");
//: The trace section of the debug interface //: 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.writeEmptyElement("hr");
writer.writeTextElement("p", tr("This test showes the trace path from the nymea device to the nymea.io server.")); 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; 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;
}
} }

View File

@ -24,9 +24,11 @@
#include <QTimer> #include <QTimer>
#include <QObject> #include <QObject>
#include <QProcess> #include <QProcess>
#include <QUrlQuery>
#include <QWebSocketServer> #include <QWebSocketServer>
#include "httpreply.h" #include "httpreply.h"
#include "debugreportgenerator.h"
namespace nymeaserver { namespace nymeaserver {
@ -36,7 +38,7 @@ class DebugServerHandler : public QObject
public: public:
explicit DebugServerHandler(QObject *parent = nullptr); explicit DebugServerHandler(QObject *parent = nullptr);
HttpReply *processDebugRequest(const QString &requestPath); HttpReply *processDebugRequest(const QString &requestPath, const QUrlQuery &requestQuery);
private: private:
static QtMessageHandler s_oldLogMessageHandler; static QtMessageHandler s_oldLogMessageHandler;
@ -54,6 +56,9 @@ private:
QProcess *m_tracePathProcess = nullptr; QProcess *m_tracePathProcess = nullptr;
HttpReply *m_tracePathReply = nullptr; HttpReply *m_tracePathReply = nullptr;
QHash<DebugReportGenerator *, HttpReply *> m_runningReportGenerators;
QHash<QString, DebugReportGenerator *> m_finishedReportGenerators;
QByteArray loadResourceData(const QString &resourceFileName); QByteArray loadResourceData(const QString &resourceFileName);
QString getResourceFileName(const QString &requestPath); QString getResourceFileName(const QString &requestPath);
bool resourceFileExits(const QString &requestPath); bool resourceFileExits(const QString &requestPath);
@ -64,6 +69,8 @@ private:
QByteArray createErrorXmlDocument(HttpReply::HttpStatusCode statusCode, const QString &errorMessage); QByteArray createErrorXmlDocument(HttpReply::HttpStatusCode statusCode, const QString &errorMessage);
private slots: private slots:
void onDebugServerEnabledChanged(bool enabled);
void onWebsocketClientConnected(); void onWebsocketClientConnected();
void onWebsocketClientDisconnected(); void onWebsocketClientDisconnected();
void onWebsocketClientError(QAbstractSocket::SocketError error); void onWebsocketClientError(QAbstractSocket::SocketError error);
@ -71,7 +78,8 @@ private slots:
void onPingProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); void onPingProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void onDigProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); void onDigProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void onTracePathProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); void onTracePathProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void onDebugReportGeneratorFinished(bool success);
void onDebugReportGeneratorTimeout();
}; };
} }

View File

@ -97,6 +97,7 @@ HEADERS += nymeacore.h \
tagging/tag.h \ tagging/tag.h \
jsonrpc/tagshandler.h \ jsonrpc/tagshandler.h \
cloud/cloudtransport.h \ cloud/cloudtransport.h \
debugreportgenerator.h
SOURCES += nymeacore.cpp \ SOURCES += nymeacore.cpp \
tcpserver.cpp \ tcpserver.cpp \
@ -178,3 +179,4 @@ SOURCES += nymeacore.cpp \
tagging/tag.cpp \ tagging/tag.cpp \
jsonrpc/tagshandler.cpp \ jsonrpc/tagshandler.cpp \
cloud/cloudtransport.cpp \ cloud/cloudtransport.cpp \
debugreportgenerator.cpp

View File

@ -373,7 +373,6 @@ void WebServer::readClient()
// Check if this is a debug call // Check if this is a debug call
if (request.url().path().startsWith("/debug")) { if (request.url().path().startsWith("/debug")) {
// Check if debug server is enabled // Check if debug server is enabled
if (NymeaCore::instance()->configuration()->debugServerEnabled()) { if (NymeaCore::instance()->configuration()->debugServerEnabled()) {
// Verify methods // Verify methods
@ -386,7 +385,8 @@ void WebServer::readClient()
return; 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); reply->setClientId(clientId);
// Handle async replies // Handle async replies

View File

@ -33,6 +33,7 @@ Q_LOGGING_CATEGORY(dcLogEngine, "LogEngine")
Q_LOGGING_CATEGORY(dcTcpServer, "TcpServer") Q_LOGGING_CATEGORY(dcTcpServer, "TcpServer")
Q_LOGGING_CATEGORY(dcTcpServerTraffic, "TcpServerTraffic") Q_LOGGING_CATEGORY(dcTcpServerTraffic, "TcpServerTraffic")
Q_LOGGING_CATEGORY(dcWebServer, "WebServer") Q_LOGGING_CATEGORY(dcWebServer, "WebServer")
Q_LOGGING_CATEGORY(dcDebugServer, "DebugServer")
Q_LOGGING_CATEGORY(dcWebSocketServer, "WebSocketServer") Q_LOGGING_CATEGORY(dcWebSocketServer, "WebSocketServer")
Q_LOGGING_CATEGORY(dcWebSocketServerTraffic, "WebSocketServerTraffic") Q_LOGGING_CATEGORY(dcWebSocketServerTraffic, "WebSocketServerTraffic")
Q_LOGGING_CATEGORY(dcJsonRpc, "JsonRpc") Q_LOGGING_CATEGORY(dcJsonRpc, "JsonRpc")

View File

@ -41,6 +41,7 @@ Q_DECLARE_LOGGING_CATEGORY(dcLogEngine)
Q_DECLARE_LOGGING_CATEGORY(dcTcpServer) Q_DECLARE_LOGGING_CATEGORY(dcTcpServer)
Q_DECLARE_LOGGING_CATEGORY(dcTcpServerTraffic) Q_DECLARE_LOGGING_CATEGORY(dcTcpServerTraffic)
Q_DECLARE_LOGGING_CATEGORY(dcWebServer) Q_DECLARE_LOGGING_CATEGORY(dcWebServer)
Q_DECLARE_LOGGING_CATEGORY(dcDebugServer)
Q_DECLARE_LOGGING_CATEGORY(dcWebSocketServer) Q_DECLARE_LOGGING_CATEGORY(dcWebSocketServer)
Q_DECLARE_LOGGING_CATEGORY(dcWebSocketServerTraffic) Q_DECLARE_LOGGING_CATEGORY(dcWebSocketServerTraffic)
Q_DECLARE_LOGGING_CATEGORY(dcJsonRpc) Q_DECLARE_LOGGING_CATEGORY(dcJsonRpc)

View File

@ -110,6 +110,7 @@ int main(int argc, char *argv[])
"TcpServer", "TcpServer",
"TcpServerTraffic", "TcpServerTraffic",
"WebServer", "WebServer",
"DebugServer",
"WebSocketServer", "WebSocketServer",
"WebSocketServerTraffic", "WebSocketServerTraffic",
"JsonRpc", "JsonRpc",
@ -238,7 +239,7 @@ int main(int argc, char *argv[])
bool startForeground = parser.isSet(foregroundOption); bool startForeground = parser.isSet(foregroundOption);
if (startForeground) { if (startForeground) {
// inform about userid // inform about userid
int userId = getuid(); uint userId = getuid();
if (userId != 0) { if (userId != 0) {
// check if config directory for logfile exists // check if config directory for logfile exists
if (!QDir().mkpath(NymeaSettings::settingsPath())) { if (!QDir().mkpath(NymeaSettings::settingsPath())) {