Add status report generator and finish debug interface improvements
This commit is contained in:
parent
b2cb22df66
commit
a0c2143c4b
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
7
debian/control
vendored
@ -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
|
||||||
|
|||||||
266
libnymea-core/debugreportgenerator.cpp
Normal file
266
libnymea-core/debugreportgenerator.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
56
libnymea-core/debugreportgenerator.h
Normal file
56
libnymea-core/debugreportgenerator.h
Normal 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
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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")
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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())) {
|
||||||
|
|||||||
Reference in New Issue
Block a user