497 lines
15 KiB
C++
Executable File
497 lines
15 KiB
C++
Executable File
/**
|
|
* The ServiceProcess class represents a service which was startet as a process
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
* Created by Hannes Baeuerle, SLSE, 2016-02-15
|
|
*/
|
|
|
|
#include "service_process.h"
|
|
#include "service.h"
|
|
#include "licence_donator.h"
|
|
#include "settings.h"
|
|
#include <ServiceManagement/scm_dts_types.h>
|
|
|
|
#include <QtCore/QDateTime>
|
|
#include <QtCore/QTimer>
|
|
|
|
int ServiceManagement::ServiceProcess::s_serviceProcessIdCounter = 0;
|
|
|
|
ServiceManagement::ServiceProcess::ServiceProcess(const GroupRequest &groupData, ServiceManagement::Service *parentService)
|
|
: QProcess(parentService)
|
|
, m_serviceId(parentService->id())
|
|
, m_serviceProcessId(s_serviceProcessIdCounter++)
|
|
, m_abbreviation(parentService->abbreviation())
|
|
, m_name(parentService->name())
|
|
, m_prio(parentService->prio())
|
|
, m_groupData(groupData)
|
|
, m_remotePid(-1)
|
|
, m_runningState(Core::RunningState::NotYetStarted)
|
|
, m_lastHeartbeatMSec(0)
|
|
, m_parentService(parentService)
|
|
, m_hasLicence(false)
|
|
, m_isInDevMode(parentService->devMode())
|
|
, m_failedStartAttemptCounter(0)
|
|
, m_stoppedFromRequest(false)
|
|
, m_pidProc(new QProcess(this))
|
|
, m_logger(getLogger("ServiceProcess"))
|
|
{
|
|
QStringList args({QStringLiteral("-e"), QString::number(m_groupData.exercisePartitionId()),
|
|
QStringLiteral("-d"), QString::number(m_groupData.domainId())});
|
|
|
|
bool isInReplayGroup = false;
|
|
for(const QString ¤tGroup : parentService->groups())
|
|
{
|
|
if (currentGroup.startsWith(QLatin1String("Replay")) || currentGroup.startsWith(QLatin1String("Playback")))
|
|
{
|
|
isInReplayGroup = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (isInReplayGroup)
|
|
args << QStringLiteral("-r") << QString::number(m_groupData.replayPartitionId());
|
|
args.reserve(args.size() + m_groupData.additionalStartParameter().size());
|
|
for (const std::string &arg : m_groupData.additionalStartParameter())
|
|
args << QString::fromStdString(arg);
|
|
|
|
setArguments(args);
|
|
|
|
#ifndef LTS_DEBUG
|
|
if (m_name == QStringLiteral("HighLevelArchitecture"))
|
|
{
|
|
DEBUG_LOG(m_logger) <<" START HLA REMOTE";
|
|
killallRemote();
|
|
|
|
//QString sshCmd = QStringLiteral("ssh ctcap03 '/shd/CTC/SV/bin/svcHighLevelArchitecture %1 &'").arg(arguments().join(QLatin1Char(' ')));
|
|
QStringList remoteCommand;
|
|
remoteCommand.append(Settings::hlaRemoteServer());
|
|
remoteCommand.append(QStringLiteral("/shd/CTC/SV/bin/svcHighLevelArchitecture "));
|
|
remoteCommand.append(arguments());
|
|
setArguments(remoteCommand);
|
|
setProgram(QStringLiteral("ssh"));
|
|
}
|
|
|
|
if (m_name == QStringLiteral("Buildings"))
|
|
{
|
|
DEBUG_LOG(m_logger) <<" START Buildings Remote";
|
|
killallRemote();
|
|
|
|
//QString sshCmd = QStringLiteral("ssh ctcap03 '/shd/CTC/SV/bin/svcHighLevelArchitecture %1 &'").arg(arguments().join(QLatin1Char(' ')));
|
|
QStringList remoteCommand;
|
|
remoteCommand.append(Settings::buildingsRemoteServer());
|
|
remoteCommand.append(QStringLiteral("/shd/CTC/SV/bin/svcBuildings "));
|
|
remoteCommand.append(arguments());
|
|
setArguments(remoteCommand);
|
|
setProgram(QStringLiteral("ssh"));
|
|
}
|
|
#endif
|
|
|
|
|
|
connect(this, &ServiceProcess::stateChanged,
|
|
this, &ServiceProcess::onNewInfoFromProc);
|
|
}
|
|
|
|
ServiceManagement::ServiceProcess::~ServiceProcess()
|
|
{
|
|
disconnect(this);
|
|
#ifndef LTS_DEBUG
|
|
if (m_name == QStringLiteral("HighLevelArchitecture"))
|
|
{
|
|
killallRemote();
|
|
}
|
|
#endif
|
|
if (state() == QProcess::Running)
|
|
{
|
|
terminate();
|
|
waitForFinished(5000);
|
|
if (state() == QProcess::Running)
|
|
kill();
|
|
}
|
|
}
|
|
|
|
Core::RunningState ServiceManagement::ServiceProcess::runningState() const
|
|
{
|
|
return m_runningState;
|
|
}
|
|
|
|
bool ServiceManagement::ServiceProcess::start(OpenMode mode)
|
|
{
|
|
DEBUG_LOG(m_logger) << "Starting ServiceProcess "
|
|
<< debugInfo()
|
|
<< " executable: " << program()
|
|
<< " in dir: " << workingDirectory()
|
|
<< " with arguments " << arguments().join(QLatin1Char(' '))
|
|
<< " prio=" << m_prio;
|
|
if (m_parentService->licenceDonator()->getLicence(m_name))
|
|
{
|
|
m_hasLicence = true;
|
|
m_stoppedFromRequest = false;
|
|
#ifndef LTS_DEBUG
|
|
if (m_name == QStringLiteral("HighLevelArchitecture"))
|
|
{
|
|
DEBUG_LOG(m_logger) <<" START HLA REMOTE";
|
|
|
|
//QString sshCmd = QStringLiteral("ssh ctcap03 '/shd/CTC/SV/bin/svcHighLevelArchitecture %1 &echo $! '").arg(arguments().join(QLatin1Char(' ')));
|
|
//DEBUG_LOG(m_logger) << sshCmd;
|
|
setProgram(QStringLiteral("ssh"));
|
|
|
|
if (state() == QProcess::NotRunning)
|
|
{
|
|
QProcess::start(mode);
|
|
waitForStarted(5000);
|
|
}
|
|
DEBUG_LOG(m_logger) << program() << " " << arguments().join(QLatin1Char(' '));
|
|
|
|
m_pidProc->kill();
|
|
m_pidProc->setProgram(QStringLiteral("ssh"));
|
|
//DEBUG_LOG(m_logger) << sshCmd;
|
|
QStringList args({QStringLiteral("-e"), QString::number(m_groupData.exercisePartitionId()),
|
|
QStringLiteral("-d"), QString::number(m_groupData.domainId())});
|
|
setProgram(QStringLiteral("ssh"));
|
|
QStringList remoteCommand2;
|
|
remoteCommand2.append(Settings::hlaRemoteServer());
|
|
remoteCommand2.append(QStringLiteral("pgrep -f \'svcHighLevelArchitecture %1\'").arg(args.join(QLatin1Char(' '))));
|
|
m_pidProc->setArguments(remoteCommand2);
|
|
//args.append(QStringLiteral("pkill -9 -f \"svcHighLevelArchitecture %1\"").arg(arg.join(QLatin1Char(' '))));
|
|
|
|
|
|
m_pidProc->setProcessChannelMode(QProcess::MergedChannels);
|
|
m_pidProc->disconnect();
|
|
connect(m_pidProc, &QProcess::readyRead, this , &ServiceProcess::onDataFromPidProc, Qt::UniqueConnection);
|
|
|
|
QTimer::singleShot(3000, this, [this](){
|
|
m_pidProc->start();
|
|
m_pidProc->waitForReadyRead(5000);
|
|
});
|
|
|
|
QTimer::singleShot(15000, this, [this](){
|
|
m_pidProc->start();
|
|
m_pidProc->waitForReadyRead(5000);
|
|
});
|
|
|
|
DEBUG_LOG(m_logger) << m_pidProc->program() << " " << m_pidProc->arguments().join(QLatin1Char(' '));
|
|
|
|
}else if (m_name == QStringLiteral("Buildings"))
|
|
{
|
|
DEBUG_LOG(m_logger) <<" START Buildings REMOTE";
|
|
setProgram(QStringLiteral("ssh"));
|
|
//QString sshCmd = QStringLiteral("ssh ctcap03 '/shd/CTC/SV/bin/svcHighLevelArchitecture %1 &echo $! '").arg(arguments().join(QLatin1Char(' ')));
|
|
//DEBUG_LOG(m_logger) << sshCmd;
|
|
|
|
if (state() == QProcess::NotRunning)
|
|
{
|
|
QProcess::start(mode);
|
|
waitForStarted(5000);
|
|
}
|
|
DEBUG_LOG(m_logger) << program() << " " << arguments().join(QLatin1Char(' '));
|
|
|
|
|
|
m_pidProc->kill();
|
|
m_pidProc->setProgram(QStringLiteral("ssh"));
|
|
//DEBUG_LOG(m_logger) << sshCmd;
|
|
QStringList args({QStringLiteral("-e"), QString::number(m_groupData.exercisePartitionId()),
|
|
QStringLiteral("-d"), QString::number(m_groupData.domainId())});
|
|
setProgram(QStringLiteral("ssh"));
|
|
QStringList remoteCommand2;
|
|
remoteCommand2.append(Settings::hlaRemoteServer());
|
|
remoteCommand2.append(QStringLiteral("pgrep -f \'svcBuildings %1\'").arg(args.join(QLatin1Char(' '))));
|
|
m_pidProc->setArguments(remoteCommand2);
|
|
//args.append(QStringLiteral("pkill -9 -f \"svcHighLevelArchitecture %1\"").arg(arg.join(QLatin1Char(' '))));
|
|
|
|
|
|
m_pidProc->setProcessChannelMode(QProcess::MergedChannels);
|
|
m_pidProc->disconnect();
|
|
|
|
connect(m_pidProc, &QProcess::readyRead, this , &ServiceProcess::onDataFromPidProc, Qt::UniqueConnection);
|
|
|
|
QTimer::singleShot(9000, this, [this](){
|
|
m_pidProc->start();
|
|
m_pidProc->waitForReadyRead(5000);
|
|
});
|
|
|
|
|
|
QTimer::singleShot(15000, this, [this](){
|
|
m_pidProc->start();
|
|
m_pidProc->waitForReadyRead(5000);
|
|
});
|
|
|
|
DEBUG_LOG(m_logger) << m_pidProc->program() << " " << m_pidProc->arguments().join(QLatin1Char(' '));
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (state() == QProcess::NotRunning)
|
|
QProcess::start(mode);
|
|
}
|
|
|
|
|
|
m_lastHeartbeatMSec = QDateTime::currentMSecsSinceEpoch();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
ERROR_LOG(m_logger) << "Could not start service "
|
|
<< debugInfo()
|
|
<< " because there is no licence for it.";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int ServiceManagement::ServiceProcess::serviceProcessId() const
|
|
{
|
|
return m_serviceProcessId;
|
|
}
|
|
|
|
void ServiceManagement::ServiceProcess::setNewStateFromHeartbeat(const Core::CurrentRunningState &data)
|
|
{
|
|
DEBUG_LOG(m_logger) << "Heartbeat for " << debugInfo() << ", running state: " << data.runningState();
|
|
m_failedStartAttemptCounter = 0;
|
|
m_lastHeartbeatMSec = QDateTime::currentMSecsSinceEpoch();
|
|
m_runningState = data.runningState();
|
|
m_ports = data.ports();
|
|
|
|
std::string es;
|
|
if (data.errorString().is_set())
|
|
es = data.errorString().get();
|
|
|
|
ServiceInformation info(m_serviceProcessId,
|
|
m_lastHeartbeatMSec,
|
|
m_name.toStdString(),
|
|
m_runningState,
|
|
es,
|
|
usablePid());
|
|
|
|
emit newServiceInformation(info);
|
|
}
|
|
|
|
void ServiceManagement::ServiceProcess::killallRemote()
|
|
{
|
|
QStringList arg({QStringLiteral("-e"), QString::number(m_groupData.exercisePartitionId()),
|
|
QStringLiteral("-d"), QString::number(m_groupData.domainId())});
|
|
QProcess sh;
|
|
sh.setProgram(QStringLiteral("ssh"));
|
|
QStringList args;
|
|
// QString killForm = QLatin1String("");
|
|
QString killForm = QStringLiteral("-15");
|
|
if (m_name == QStringLiteral("Buildings"))
|
|
args.append(Settings::buildingsRemoteServer());
|
|
else
|
|
{
|
|
args.append(Settings::hlaRemoteServer());
|
|
killForm = QStringLiteral("-9");
|
|
}
|
|
|
|
args.append(QStringLiteral("pkill %1 -f \"%2 %3\"").arg(killForm, m_name,arg.join(QLatin1Char(' '))));
|
|
sh.setArguments(args);
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
|
sh.startDetached();
|
|
#else
|
|
sh.start();
|
|
#endif DEBUG_LOG(m_logger) << "kill remote " << sh.program() << " " << args.join(QLatin1Char(' '));
|
|
}
|
|
|
|
void ServiceManagement::ServiceProcess::restart()
|
|
{
|
|
#ifndef LTS_DEBUG
|
|
|
|
if (m_name == QStringLiteral("HighLevelArchitecture") || m_name == QStringLiteral("Buildings"))
|
|
{
|
|
killallRemote();
|
|
}
|
|
#endif
|
|
// else
|
|
terminate(); // already trigger terminate here to shut down apps in parallel
|
|
QTimer::singleShot(0, this, &ServiceProcess::doRestart);
|
|
}
|
|
|
|
void ServiceManagement::ServiceProcess::doRestart()
|
|
{
|
|
if (m_hasLicence)
|
|
{
|
|
m_hasLicence = false;
|
|
m_parentService->licenceDonator()->freeLicence(m_name);
|
|
}
|
|
#ifndef LTS_DEBUG
|
|
|
|
if (m_name == QStringLiteral("HighLevelArchitecture"))
|
|
{
|
|
//remoteKill
|
|
killallRemote();
|
|
}
|
|
#endif
|
|
// else
|
|
{
|
|
terminate();
|
|
waitForFinished(5000);
|
|
if (state() == QProcess::Running)
|
|
kill();
|
|
|
|
}
|
|
|
|
QTimer::singleShot(100, this, [this]{ start(); });
|
|
}
|
|
|
|
void ServiceManagement::ServiceProcess::onDataFromPidProc()
|
|
{
|
|
const QByteArray &ret = m_pidProc->readAll();
|
|
FATAL_LOG(m_logger) << "New Data Id from remote" << ret;
|
|
int newId = ret.trimmed().toInt();
|
|
if (newId > 0)
|
|
{
|
|
m_remotePid = newId;
|
|
FATAL_LOG(m_logger) << "New QProcess Id from remote" << m_name << " pid= "<< m_remotePid ;
|
|
m_pidProc->kill();
|
|
}
|
|
|
|
}
|
|
|
|
void ServiceManagement::ServiceProcess::stopFromRequest()
|
|
{
|
|
m_stoppedFromRequest = true;
|
|
QProcess::terminate();
|
|
}
|
|
|
|
unsigned short ServiceManagement::ServiceProcess::failedStartingAttempts() const
|
|
{
|
|
return m_failedStartAttemptCounter;
|
|
}
|
|
|
|
void ServiceManagement::ServiceProcess::addStartAttempt()
|
|
{
|
|
++m_failedStartAttemptCounter;
|
|
}
|
|
|
|
qint64 ServiceManagement::ServiceProcess::lastHeartbeatMSec() const
|
|
{
|
|
return m_lastHeartbeatMSec;
|
|
}
|
|
|
|
void ServiceManagement::ServiceProcess::setLastHeartbeatMSec(qint64 lastHeartbeatMSec)
|
|
{
|
|
m_lastHeartbeatMSec = lastHeartbeatMSec;
|
|
}
|
|
|
|
QString ServiceManagement::ServiceProcess::serviceName() const
|
|
{
|
|
return m_name;
|
|
}
|
|
|
|
QString ServiceManagement::ServiceProcess::abbreviation() const
|
|
{
|
|
return m_abbreviation;
|
|
}
|
|
|
|
dds::core::optional<std::vector<Core::PortInformation> > ServiceManagement::ServiceProcess::ports() const
|
|
{
|
|
return m_ports;
|
|
}
|
|
|
|
bool ServiceManagement::ServiceProcess::devMode() const
|
|
{
|
|
return m_isInDevMode;
|
|
}
|
|
|
|
bool ServiceManagement::ServiceProcess::stoppedFromRequest() const
|
|
{
|
|
return m_stoppedFromRequest;
|
|
}
|
|
|
|
void ServiceManagement::ServiceProcess::setNewStateFromChangeRequest(const Core::RunningState &newState)
|
|
{
|
|
if (m_runningState != newState)
|
|
{
|
|
switch (newState.underlying())
|
|
{
|
|
case Core::RunningState::Running:
|
|
case Core::RunningState::Starting:
|
|
if (state() != QProcess::Running)
|
|
start();
|
|
break;
|
|
case Core::RunningState::StoppedNormally:
|
|
case Core::RunningState::Stopping:
|
|
terminate();
|
|
waitForFinished(5000);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ServiceManagement::ServiceProcess::onNewInfoFromProc(ProcessState state)
|
|
{
|
|
DEBUG_LOG(m_logger) << "New state for " << debugInfo() << ": " << state;
|
|
QString errString;
|
|
switch (state)
|
|
{
|
|
case QProcess::NotRunning:
|
|
if (exitStatus() == QProcess::NormalExit)
|
|
{
|
|
if (exitCode() == 0)
|
|
DEBUG_LOG(m_logger) << "Normal exit for " << debugInfo()
|
|
<< ": " << readAllStandardError() << ", " << readAllStandardOutput();
|
|
else
|
|
ERROR_LOG(m_logger) << "Normal exit with exit code " << exitCode() << " for " << debugInfo()
|
|
<< ": " << readAllStandardError() << ", " << readAllStandardOutput();
|
|
m_runningState = Core::RunningState::StoppedNormally;
|
|
}
|
|
else
|
|
{
|
|
switch (error())
|
|
{
|
|
case QProcess::FailedToStart:
|
|
errString = QLatin1String("Got QProcess::FailedToStart error from Service process with exit code ") + QString::number(exitCode());
|
|
m_runningState = Core::RunningState::StartingError;
|
|
break;
|
|
case QProcess::Crashed:
|
|
errString = QLatin1String("Got QProcess::Crashed error from Service Process with exit code ") + QString::number(exitCode());
|
|
m_runningState = Core::RunningState::StoppedError;
|
|
break;
|
|
default:
|
|
errString = QLatin1String("Got unkown error.");
|
|
m_runningState = Core::RunningState::StoppedError;
|
|
}
|
|
errString += QLatin1String(" error string: ") + errorString();
|
|
ERROR_LOG(m_logger) << "Crashed Error for " << debugInfo() << ", error: " << errorString() << ":\n"
|
|
<< readAll();
|
|
}
|
|
m_parentService->licenceDonator()->freeLicence(m_name);
|
|
m_hasLicence = false;
|
|
m_lastHeartbeatMSec = 0;
|
|
break;
|
|
case QProcess::Starting:
|
|
m_runningState = Core::RunningState::Starting;
|
|
break;
|
|
case QProcess::Running:
|
|
DEBUG_LOG(m_logger) << "Process " << m_name
|
|
<< " has now the PID " << usablePid();
|
|
}
|
|
|
|
ServiceInformation info(m_serviceProcessId,
|
|
QDateTime::currentMSecsSinceEpoch(),
|
|
m_name.toStdString(),
|
|
m_runningState,
|
|
errString.toStdString(),
|
|
usablePid());
|
|
|
|
emit newServiceInformation(info);
|
|
}
|
|
|
|
QString ServiceManagement::ServiceProcess::debugInfo() const
|
|
{
|
|
QString partitionStr;
|
|
if (replayPartitionId() > 0)
|
|
partitionStr = QStringLiteral("%1 (replay)").arg(replayPartitionId());
|
|
else
|
|
partitionStr = QString::number(exercisePartitionId());
|
|
return QStringLiteral("%1, domain: %2, partition: %3, PID: %4, last heartbeat: %5, arguments: '%6' this: 0x%7")
|
|
.arg(serviceName())
|
|
.arg(domainId())
|
|
.arg(partitionStr)
|
|
.arg(usablePid())
|
|
.arg(QDateTime::fromMSecsSinceEpoch(m_lastHeartbeatMSec).toString(QStringLiteral("HH:mm:ss")),
|
|
arguments().join(QLatin1Char(' ')))
|
|
.arg(reinterpret_cast<qint64>(this), 8, 16, QLatin1Char('0'));
|
|
}
|