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.
MoneroDaemonService/src/DaemonManager.cpp

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();
}