You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
5.8 KiB
241 lines
5.8 KiB
// SPDX-License-Identifier: BSD-3-Clause
|
|
// Copyright (c) 2014-2021, The Monero Project.
|
|
|
|
#include "DaemonManager.h"
|
|
#include <QElapsedTimer>
|
|
#include <QFile>
|
|
#include <QMutexLocker>
|
|
#include <QThread>
|
|
#include <QFileInfo>
|
|
#include <QDir>
|
|
#include <QDebug>
|
|
#include <QUrl>
|
|
#include <QtConcurrent/QtConcurrent>
|
|
#include <QApplication>
|
|
#include <QProcess>
|
|
#include <QStorageInfo>
|
|
#include <QVariantMap>
|
|
#include <QVariant>
|
|
#include <QMap>
|
|
#include <utility>
|
|
|
|
bool DaemonManager::start(const QStringList &flags, int port)
|
|
{
|
|
if (!QFileInfo(m_monerod).isFile())
|
|
{
|
|
emit daemonStartFailure("\"" + QDir::toNativeSeparators(m_monerod) + "\" " + tr("executable is missing"));
|
|
return false;
|
|
}
|
|
|
|
m_port = port;
|
|
|
|
// prepare command line arguments and pass to monerod
|
|
QStringList arguments = flags;
|
|
|
|
arguments << "--check-updates" << "disabled";
|
|
|
|
qDebug() << "starting monerod " + m_monerod;
|
|
qDebug() << "With command line arguments " << arguments;
|
|
|
|
QMutexLocker locker(&m_daemonMutex);
|
|
|
|
m_daemon.reset(new QProcess());
|
|
|
|
// Connect output slots
|
|
connect(m_daemon.get(), &QProcess::readyReadStandardOutput, this, &DaemonManager::printOutput);
|
|
connect(m_daemon.get(), &QProcess::readyReadStandardError, this, &DaemonManager::printError);
|
|
|
|
// add state changed listener
|
|
connect(m_daemon.get(), &QProcess::stateChanged, this, &DaemonManager::stateChanged);
|
|
|
|
// Start monerod
|
|
m_daemon->start(m_monerod, arguments);
|
|
|
|
// Start start watcher
|
|
m_scheduler.run([this] {
|
|
if (startWatcher()) {
|
|
emit daemonStarted();
|
|
}
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
void DaemonManager::stop()
|
|
{
|
|
m_daemon->terminate();
|
|
m_scheduler.run([this]{
|
|
stopWatcher();
|
|
});
|
|
}
|
|
|
|
bool DaemonManager::isRunning() const {
|
|
return m_running;
|
|
}
|
|
|
|
bool DaemonManager::startWatcher()
|
|
{
|
|
// Check if daemon is started every 2 seconds
|
|
QElapsedTimer timer;
|
|
timer.start();
|
|
while(!m_app_exit && m_running) {
|
|
QThread::sleep(2);
|
|
if(!started()) {
|
|
qDebug() << "daemon not running. checking again in 2 seconds.";
|
|
} else {
|
|
qDebug() << "daemon is started. Waiting 5 seconds to let daemon catch up";
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DaemonManager::stopWatcher()
|
|
{
|
|
// Check if daemon is running every 2 seconds. Kill if still running after 10 seconds
|
|
int counter = 0;
|
|
while(!m_app_exit && !m_running) {
|
|
QThread::sleep(2);
|
|
counter++;
|
|
if(!started()) {
|
|
return true;
|
|
} else {
|
|
qDebug() << "Daemon still running. " << counter;
|
|
if(counter >= 5) {
|
|
m_daemon->kill();
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void DaemonManager::stateChanged(QProcess::ProcessState state)
|
|
{
|
|
qDebug() << "STATE CHANGED: " << state;
|
|
m_running = (state != QProcess::NotRunning);
|
|
if (state == QProcess::NotRunning) {
|
|
emit daemonStopped();
|
|
}
|
|
}
|
|
|
|
void DaemonManager::printOutput()
|
|
{
|
|
QByteArray byteArray = [this]() {
|
|
QMutexLocker locker(&m_daemonMutex);
|
|
return m_daemon->readAllStandardOutput();
|
|
}();
|
|
QStringList strLines = QString(byteArray).split("\n");
|
|
|
|
for (const auto& line: strLines) {
|
|
emit daemonConsoleUpdated(line);
|
|
}
|
|
}
|
|
|
|
void DaemonManager::printError()
|
|
{
|
|
QByteArray byteArray = [this]() {
|
|
QMutexLocker locker(&m_daemonMutex);
|
|
return m_daemon->readAllStandardError();
|
|
}();
|
|
QStringList strLines = QString(byteArray).split("\n");
|
|
|
|
for (const auto& line: strLines) {
|
|
emit daemonConsoleUpdated(line);
|
|
qDebug() << "Daemon ERROR: " + line;
|
|
}
|
|
}
|
|
|
|
bool DaemonManager::started()
|
|
{
|
|
QString status;
|
|
sendCommand({"sync_info"}, status);
|
|
qDebug() << status;
|
|
m_started = status.contains("Height:");
|
|
return m_started;
|
|
}
|
|
|
|
bool DaemonManager::sendCommand(const QStringList &cmd, QString &message) const
|
|
{
|
|
QProcess p;
|
|
QStringList external_cmd(cmd);
|
|
|
|
external_cmd << "--rpc-bind-port" << QString::number(m_port);
|
|
|
|
qDebug() << "sending external cmd: " << external_cmd;
|
|
|
|
p.start(m_monerod, external_cmd);
|
|
|
|
bool started = p.waitForFinished(-1);
|
|
message = p.readAllStandardOutput();
|
|
emit daemonConsoleUpdated(message);
|
|
return started;
|
|
}
|
|
|
|
void DaemonManager::sendCommandAsync(const QStringList &cmd, std::function<void(QVariantList)> callback) const
|
|
{
|
|
m_scheduler.run([this, cmd] {
|
|
QString message;
|
|
return QVariantList({sendCommand(cmd, message), message});
|
|
}, std::move(callback));
|
|
}
|
|
|
|
void DaemonManager::exit()
|
|
{
|
|
qDebug("DaemonManager: exit()");
|
|
m_app_exit = true;
|
|
}
|
|
//
|
|
//QVariantMap DaemonManager::validateDataDir(const QString &dataDir) const
|
|
//{
|
|
// QVariantMap result;
|
|
// bool valid = true;
|
|
// bool readOnly = false;
|
|
// int storageAvailable = 0;
|
|
// bool lmdbExists = true;
|
|
//
|
|
// QStorageInfo storage(dataDir);
|
|
// if (storage.isValid() && storage.isReady()) {
|
|
// if (storage.isReadOnly()) {
|
|
// readOnly = true;
|
|
// valid = false;
|
|
// }
|
|
//
|
|
// // Make sure there is 75GB storage available
|
|
// storageAvailable = storage.bytesAvailable()/1000/1000/1000;
|
|
// if (storageAvailable < 75) {
|
|
// valid = false;
|
|
// }
|
|
// } else {
|
|
// valid = false;
|
|
// }
|
|
//
|
|
//
|
|
// if (!QDir(dataDir+"/lmdb").exists()) {
|
|
// lmdbExists = false;
|
|
// valid = false;
|
|
// }
|
|
//
|
|
// result.insert("valid", valid);
|
|
// result.insert("lmdbExists", lmdbExists);
|
|
// result.insert("readOnly", readOnly);
|
|
// result.insert("storageAvailable", storageAvailable);
|
|
//
|
|
// return result;
|
|
//}
|
|
|
|
void DaemonManager::setMonerodPath(const QString &path) {
|
|
m_monerod = path;
|
|
}
|
|
|
|
DaemonManager::DaemonManager(QObject *parent)
|
|
: QObject(parent)
|
|
, m_scheduler(this)
|
|
{
|
|
}
|
|
|
|
DaemonManager::~DaemonManager()
|
|
{
|
|
m_scheduler.shutdownWaitForFinished();
|
|
}
|