129 lines
3.5 KiB
C++
129 lines
3.5 KiB
C++
#include "ninepconnection.h"
|
|
|
|
NinePConnection::NinePConnection(QObject *parent)
|
|
: QObject(parent)
|
|
{
|
|
m_retryTimer.setSingleShot(true);
|
|
connect(&m_retryTimer, &QTimer::timeout, this, &NinePConnection::retry);
|
|
}
|
|
|
|
NinePConnection::~NinePConnection()
|
|
{
|
|
stop();
|
|
}
|
|
|
|
void NinePConnection::start(const QString &program, const QStringList &args, int retryMs, bool probe)
|
|
{
|
|
m_program = program;
|
|
m_args = args;
|
|
m_retryMs = retryMs;
|
|
m_probe = probe;
|
|
m_stopRequested = false;
|
|
m_openMarkerSeen = false;
|
|
m_retryTimer.stop();
|
|
if (!m_process)
|
|
startProcess();
|
|
}
|
|
|
|
void NinePConnection::stop()
|
|
{
|
|
m_stopRequested = true;
|
|
m_retryTimer.stop();
|
|
if (m_process) {
|
|
m_process->disconnect(this);
|
|
m_process->kill();
|
|
m_process->waitForFinished(1000);
|
|
m_process->deleteLater();
|
|
m_process = nullptr;
|
|
}
|
|
setState(Disconnected);
|
|
}
|
|
|
|
void NinePConnection::startProcess()
|
|
{
|
|
if (m_stopRequested || m_process)
|
|
return;
|
|
m_stderr.clear();
|
|
m_openMarkerSeen = false;
|
|
m_process = new QProcess(this);
|
|
connect(m_process, &QProcess::started, this, &NinePConnection::onStarted);
|
|
connect(m_process, &QProcess::readyReadStandardOutput, this, &NinePConnection::onStdout);
|
|
connect(m_process, &QProcess::readyReadStandardError, this, &NinePConnection::onStderr);
|
|
connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
|
this, &NinePConnection::onFinished);
|
|
// A finite probe refreshes an already connected agent. Keep the
|
|
// established state while the replacement probe is opening so a
|
|
// successful periodic probe does not flash disconnected in the UI.
|
|
if (!(m_probe && m_state == Connected))
|
|
setState(Connecting);
|
|
m_process->start(m_program, m_args);
|
|
}
|
|
|
|
void NinePConnection::onStarted()
|
|
{
|
|
// The helper emits the readiness marker only after the 9P open succeeds.
|
|
}
|
|
|
|
void NinePConnection::onStdout()
|
|
{
|
|
const QByteArray data = m_process->readAllStandardOutput();
|
|
if (!data.isEmpty())
|
|
emit readyRead(data);
|
|
}
|
|
|
|
void NinePConnection::onStderr()
|
|
{
|
|
m_stderr += m_process->readAllStandardError();
|
|
while (true) {
|
|
const int end = m_stderr.indexOf('\n');
|
|
if (end < 0) break;
|
|
const QByteArray line = m_stderr.left(end).trimmed();
|
|
m_stderr.remove(0, end + 1);
|
|
if (line == "OLLIE_9P_OPEN") {
|
|
m_openMarkerSeen = true;
|
|
setState(Connected);
|
|
}
|
|
}
|
|
}
|
|
|
|
void NinePConnection::onFinished(int exitCode, QProcess::ExitStatus)
|
|
{
|
|
if (!m_process) return;
|
|
m_process->deleteLater();
|
|
m_process = nullptr;
|
|
if (m_probe && m_state == Connected && m_openMarkerSeen && exitCode == 0 && !m_stopRequested) {
|
|
if (m_retryMs > 0)
|
|
m_retryTimer.start(m_retryMs);
|
|
else
|
|
startProcess(); // immediate re-probe
|
|
return;
|
|
}
|
|
if (m_stopRequested || m_retryMs <= 0) {
|
|
setState(Disconnected);
|
|
return;
|
|
}
|
|
setState(RetryWait);
|
|
m_retryTimer.start(m_retryMs);
|
|
}
|
|
|
|
void NinePConnection::retry()
|
|
{
|
|
if (!m_stopRequested)
|
|
startProcess();
|
|
}
|
|
|
|
void NinePConnection::setState(State state)
|
|
{
|
|
if (m_state == state) return;
|
|
const State previous = m_state;
|
|
m_state = state;
|
|
emit stateChanged(state);
|
|
if (state == Connected) {
|
|
emit connected();
|
|
if (m_probe && m_process)
|
|
m_process->closeWriteChannel();
|
|
}
|
|
else if (state != Connected && previous != Disconnected)
|
|
emit disconnected();
|
|
}
|