diff --git a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp index ed7ad8b2..a934bdd6 100644 --- a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp +++ b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp @@ -49,7 +49,7 @@ void QMLBackend::init(GRPCConfig const &serviceConfig) this->connectGrpcEvents(); QString error; - if (app().grpc().connectToServer(serviceConfig, error)) + if (app().grpc().connectToServer(serviceConfig, app().bridgeMonitor(), error)) app().log().info("Connected to backend via gRPC service."); else throw Exception(QString("Cannot connectToServer to go backend via gRPC: %1").arg(error)); diff --git a/internal/frontend/bridge-gui/bridge-gui/main.cpp b/internal/frontend/bridge-gui/bridge-gui/main.cpp index ab986b75..b4ea36f6 100644 --- a/internal/frontend/bridge-gui/bridge-gui/main.cpp +++ b/internal/frontend/bridge-gui/bridge-gui/main.cpp @@ -288,7 +288,7 @@ int main(int argc, char *argv[]) } log.info(QString("Retrieving gRPC service configuration from '%1'").arg(QDir::toNativeSeparators(grpcServerConfigPath()))); - app().backend().init(GRPCClient::waitAndRetrieveServiceConfig(attach ? 0 : grpcServiceConfigWaitDelayMs)); + app().backend().init(GRPCClient::waitAndRetrieveServiceConfig(attach ? 0 : grpcServiceConfigWaitDelayMs, app().bridgeMonitor())); if (!attach) GRPCClient::removeServiceConfigFile(); @@ -312,7 +312,7 @@ int main(int argc, char *argv[]) if (bridgeMonitor) { const ProcessMonitor::MonitorStatus& status = bridgeMonitor->getStatus(); - if (!status.running && !attach) + if (status.ended && !attach) { // ProcessMonitor already stopped meaning we are attached to an orphan Bridge. // Restart the full process to be sure there is no more bridge orphans diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.cpp b/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.cpp index ca14b2a0..5945fe4c 100644 --- a/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.cpp +++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.cpp @@ -19,6 +19,7 @@ #include "GRPCClient.h" #include "GRPCUtils.h" #include "../Exception/Exception.h" +#include "../ProcessMonitor.h" using namespace google::protobuf; @@ -56,9 +57,10 @@ void GRPCClient::removeServiceConfigFile() //**************************************************************************************************************************************************** /// \param[in] timeoutMs The timeout in milliseconds +/// \param[in] serverProcess An optional server process to monitor. If the process it, no need and retry, as connexion cannot be established. Ignored if null. /// \return The service config. //**************************************************************************************************************************************************** -GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(qint64 timeoutMs) +GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(qint64 timeoutMs, ProcessMonitor *serverProcess) { QString const path = grpcServerConfigPath(); QFile file(path); @@ -68,6 +70,9 @@ GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(qint64 timeoutMs) bool found = false; while (true) { + if (serverProcess && serverProcess->getStatus().ended) + throw Exception("Bridge application exited before providing a gRPC service configuration file."); + if (file.exists()) { found = true; @@ -100,9 +105,10 @@ void GRPCClient::setLog(Log *log) //**************************************************************************************************************************************************** /// \param[out] outError If the function returns false, this variable contains a description of the error. +/// \param[in] serverProcess An optional server process to monitor. If the process it, no need and retry, as connexion cannot be established. Ignored if null. /// \return true iff the connection was successful. //**************************************************************************************************************************************************** -bool GRPCClient::connectToServer(GRPCConfig const &config, QString &outError) +bool GRPCClient::connectToServer(GRPCConfig const &config, ProcessMonitor *serverProcess, QString &outError) { try { @@ -123,6 +129,9 @@ bool GRPCClient::connectToServer(GRPCConfig const &config, QString &outError) int i = 0; while (true) { + if (serverProcess && serverProcess->getStatus().ended) + throw Exception("Bridge application ended before gRPC connexion could be established."); + this->logInfo(QString("Connection to gRPC server at %1. attempt #%2").arg(address).arg(++i)); if (channel_->WaitForConnected(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(grpcConnectionRetryDelayMs, GPR_TIMESPAN)))) diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.h b/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.h index fa1612f2..8e0b9099 100644 --- a/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.h +++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.h @@ -50,7 +50,7 @@ class GRPCClient : public QObject Q_OBJECT public: // static member functions static void removeServiceConfigFile(); ///< Delete the service config file. - static GRPCConfig waitAndRetrieveServiceConfig(qint64 timeoutMs); ///< Wait and retrieve the service configuration. + static GRPCConfig waitAndRetrieveServiceConfig(qint64 timeoutMs, class ProcessMonitor *serverProcess); ///< Wait and retrieve the service configuration. public: // member functions. GRPCClient() = default; ///< Default constructor. @@ -60,7 +60,7 @@ public: // member functions. GRPCClient &operator=(GRPCClient const &) = delete; ///< Disabled assignment operator. GRPCClient &operator=(GRPCClient &&) = delete; ///< Disabled move assignment operator. void setLog(Log *log); ///< Set the log for the client. - bool connectToServer(GRPCConfig const &config, QString &outError); ///< Establish connection to the gRPC server. + bool connectToServer(GRPCConfig const &config, class ProcessMonitor *serverProcess, QString &outError); ///< Establish connection to the gRPC server. grpc::Status checkTokens(QString const &clientConfigPath, QString &outReturnedClientToken); ///< Performs a token check. grpc::Status addLogEntry(Log::Level level, QString const &package, QString const &message); ///< Performs the "AddLogEntry" gRPC call. diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/ProcessMonitor.cpp b/internal/frontend/bridge-gui/bridgepp/bridgepp/ProcessMonitor.cpp index 359eb6f4..b79a17c0 100644 --- a/internal/frontend/bridge-gui/bridgepp/bridgepp/ProcessMonitor.cpp +++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/ProcessMonitor.cpp @@ -33,6 +33,8 @@ ProcessMonitor::ProcessMonitor(QString const &exePath, QStringList const &args, : Worker(parent) , exePath_(exePath) , args_(args) + , out_(stdout) + , err_(stderr) { QFileInfo fileInfo(exePath); if (!fileInfo.exists()) @@ -42,6 +44,26 @@ ProcessMonitor::ProcessMonitor(QString const &exePath, QStringList const &args, } +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +void ProcessMonitor::forwardProcessOutput(QProcess &p) { + QByteArray array = p.readAllStandardError(); + if (!array.isEmpty()) + { + err_ << array; + err_.flush(); + } + + array = p.readAllStandardOutput(); + if (!array.isEmpty()) + { + out_ << array; + out_.flush(); + } +} + + //**************************************************************************************************************************************************** // //**************************************************************************************************************************************************** @@ -49,35 +71,31 @@ void ProcessMonitor::run() { try { + { + QMutexLocker locker(&statusMutex_); + status_.ended = false; + status_.pid = -1; + } + emit started(); QProcess p; p.start(exePath_, args_); p.waitForStarted(); - status_.running = true; - status_.pid = p.processId(); - - QTextStream out(stdout), err(stderr); - QByteArray array; - while (!p.waitForFinished(100)) { - array = p.readAllStandardError(); - if (!array.isEmpty()) - { - err << array; - err.flush(); - } - - array = p.readAllStandardOutput(); - if (!array.isEmpty()) - { - out << array; - out.flush(); - } + QMutexLocker locker(&statusMutex_); + status_.pid = p.processId(); } - status_.running = false; + while (!p.waitForFinished(100)) + { + this->forwardProcessOutput(p); + } + this->forwardProcessOutput(p); + + QMutexLocker locker(&statusMutex_); + status_.ended = true; status_.returnCode = p.exitCode(); emit processExited(status_.returnCode); @@ -93,8 +111,9 @@ void ProcessMonitor::run() //**************************************************************************************************************************************************** /// \return status of the monitored process //**************************************************************************************************************************************************** -const ProcessMonitor::MonitorStatus &ProcessMonitor::getStatus() +const ProcessMonitor::MonitorStatus ProcessMonitor::getStatus() { + QMutexLocker locker(&statusMutex_); return status_; } diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/ProcessMonitor.h b/internal/frontend/bridge-gui/bridgepp/bridgepp/ProcessMonitor.h index aeb37d8e..11506263 100644 --- a/internal/frontend/bridge-gui/bridgepp/bridgepp/ProcessMonitor.h +++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/ProcessMonitor.h @@ -36,7 +36,7 @@ Q_OBJECT public: // static member functions struct MonitorStatus { - bool running = false; + bool ended = false; int returnCode = 0; qint64 pid = 0; }; @@ -49,15 +49,21 @@ public: // member functions. ProcessMonitor &operator=(ProcessMonitor const &) = delete; ///< Disabled assignment operator. ProcessMonitor &operator=(ProcessMonitor &&) = delete; ///< Disabled move assignment operator. void run() override; ///< Run the worker. - MonitorStatus const &getStatus(); + MonitorStatus const getStatus(); ///< Retrieve the current status of the process. signals: void processExited(int code); ///< Slot for the exiting of the process. +private: // member functions + void forwardProcessOutput(QProcess &p); ///< Forward the standard output and error from the process to this application standard output and error. + private: // data members + QMutex statusMutex_; ///< The status mutex. QString const exePath_; ///< The path to the executable. QStringList args_; ///< arguments to be passed to the brigde. MonitorStatus status_; ///< Status of the monitoring. + QTextStream out_; ///< The standard output stream. + QTextStream err_; ///< The standard error stream. };