diff --git a/ismas/ismas_client.cpp b/ismas/ismas_client.cpp index 7c57811..a842180 100644 --- a/ismas/ismas_client.cpp +++ b/ismas/ismas_client.cpp @@ -3,6 +3,24 @@ #include #include +#include +#include // inet_addr() +#include +#include +#include +#include +#include +#include +#include // bzero() +#include +#include // read(), write(), close() +#include + + +#include +#include +#include + #if 0 # $1: EVENT: U0001 update finished: 100% # U0002 reset TRG @@ -24,6 +42,208 @@ #include #include +void IsmasClient::printDebugMessage(int port, + QString const &clientIP, + int clientPort, + QString const &message) { + qDebug().noquote() + << "\n" + << "SEND-REQUEST-RECEIVE-RESPONSE ..." << "\n" + << "hostname ........" << "127.0.0.1" << "\n" + << "port ............" << port << "\n" + << "local address ..." << clientIP << "\n" + << "local port ......" << clientPort << "\n" + << message; +} + +void IsmasClient::printInfoMessage(int port, + QString const &clientIP, + int clientPort, + QString const &message) { + qInfo().noquote() + << "\n" + << "SEND-REQUEST-RECEIVE-RESPONSE ..." << "\n" + << "hostname ........" << "127.0.0.1" << "\n" + << "port ............" << port << "\n" + << "local address ..." << clientIP << "\n" + << "local port ......" << clientPort << "\n" + << message; +} + +void IsmasClient::printErrorMessage(int port, + QString const &clientIP, + int clientPort, + QString const &message) { + qCritical().noquote() + << "\n" + << "SEND-REQUEST-RECEIVE-RESPONSE ..." << "\n" + << "hostname ........" << "127.0.0.1" << "\n" + << "port ............" << port << "\n" + << "local address ..." << clientIP << "\n" + << "local port ......" << clientPort << "\n" + << message; +} + +std::optional +IsmasClient::sendRequestReceiveResponse(int port, QString const &request) { + + qInfo() << "REQUEST" << request; + + int sockfd; + int r; + errno = 0; + // socket create and verification + if ((sockfd = ::socket(AF_INET, SOCK_STREAM, 0)) == -1) { + qCritical().noquote() + << "\n" + << "SEND-REQUEST-RECEIVE-RESPONSE ..." << "\n" + << "SOCKET CREATION FAILED (" << strerror(errno) << ")"; + return std::nullopt; + } + + struct sockaddr_in servAddr; + bzero(&servAddr, sizeof(servAddr)); + // assign IP, PORT + servAddr.sin_family = AF_INET; + servAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + servAddr.sin_port = htons(port); + // connect the client socket to server socket + if ((r = ::connect(sockfd, (struct sockaddr *)(&servAddr), sizeof(servAddr))) != 0) { + qCritical().noquote() + << "\n" + << "SEND-REQUEST-RECEIVE-RESPONSE ..." << "\n" + << "CONNECTION WITH SERVER FAILED (" << strerror(r) << ")"; + ::close(sockfd); + return std::nullopt; + } + + struct sockaddr_in clientAddr; + bzero(&clientAddr, sizeof(clientAddr)); + socklen_t sockLen = sizeof(clientAddr); + + char clientIP[16]; + bzero(&clientIP, sizeof(clientIP)); + getsockname(sockfd, (struct sockaddr *)(&clientAddr), &sockLen); + inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, sizeof(clientIP)); + unsigned int clientPort = ntohs(clientAddr.sin_port); + + printDebugMessage(port, clientIP, clientPort, QString("CONNECTED TO SERVER")); + + struct timeval tv; + tv.tv_sec = 10; /* 10 secs timeout for read and write */ + + struct linger so_linger; + so_linger.l_onoff = 1; + so_linger.l_linger = 0; + + setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger)); + setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + int flag = 1; + setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); + + static char buf[1024*8]; + bzero(buf, sizeof(buf)); + int const bytesToWrite = strlen(request.toStdString().c_str()); + strncpy(buf, request.toStdString().c_str(), sizeof(buf)-1); + + int bytesWritten = 0; + while (bytesWritten < bytesToWrite) { + int n = ::sendto(sockfd, buf+bytesWritten, bytesToWrite-bytesWritten, 0, NULL, 0); + if (n >= 0) { + bytesWritten += n; + } else { + if (errno == EWOULDBLOCK) { + printErrorMessage(port, clientIP, clientPort, + QString("TIMEOUT (") + strerror(errno) + ")"); + ::close(sockfd); + return std::nullopt; + } else + if (errno == EINTR) { + printErrorMessage(port, clientIP, clientPort, + QString("INTERRUPTED BY SIGNAL (1) (") + strerror(errno) + ")"); + continue; + } + } + } + + // DO NOT USE SHUTDOWN! APISM CAN NOT COPE WITH IT + // errno = 0; + // if (shutdown(sockfd, SHUT_WR) < 0) { + // printErrorMessage(port, clientIP, clientPort, + // QString("CANNOT CLOSE WRITING END (") + strerror(errno) + ")"); + // } + + printInfoMessage(port, clientIP, clientPort, QString("MESSAGE SENT ") + buf); + + bzero(buf, sizeof(buf)); + int bytesToRead = sizeof(buf)-1; + int bytesRead = 0; + while (bytesRead < bytesToRead) { + errno = 0; + int n = ::recvfrom(sockfd, buf+bytesRead, bytesToRead-bytesRead, + 0, NULL, NULL); + if (n > 0) { // + bytesRead += n; + } else + if (n == 0) { + // The return value will be 0 when the peer has performed an orderly shutdown. + printErrorMessage(port, clientIP, clientPort, + QString("PEER CLOSED CONNECTION (") + strerror(errno) + ")"); + ::close(sockfd); + return std::nullopt; + } else + if (n < 0) { + if (errno == EWOULDBLOCK) { + printErrorMessage(port, clientIP, clientPort, + QString("TIMEOUT (") + strerror(errno) + ")"); + ::close(sockfd); + return std::nullopt; + } + if (errno == EINTR) { + printErrorMessage(port, clientIP, clientPort, + QString("INTERRUPTED BY SIGNAL (2) (") + strerror(errno) + ")"); + continue; + } + } + + printInfoMessage(port, clientIP, clientPort, QString("MESSAGE RECEIVED ") + buf); + QString response(buf); + + if (int idx = response.indexOf("{\"error\":\"ISMAS is offline\"}")) { + response = response.mid(0, idx); + } else + if (response == "RECORD SAVED") { + printInfoMessage(port, clientIP, clientPort, "IGNORED 'RECORD SAVED' RESPONSE"); + ::close(sockfd); + return std::nullopt; + } + + QJsonParseError parseError; + QJsonDocument document(QJsonDocument::fromJson(response.toUtf8(), &parseError)); + if (parseError.error == QJsonParseError::NoError) { + if (document.isObject()) { // done: received valid APISM response + printInfoMessage(port, clientIP, clientPort, + QString("VALID APISM RESPONSE .. \n") + response); + ::close(sockfd); + return response; + } else { + printInfoMessage(port, clientIP, clientPort, + QString("CORRUPTED RESPONSE ") + response); + ::close(sockfd); + return std::nullopt; + } + } else { + printDebugMessage(port, clientIP, clientPort, + QString("PARSE ERROR ") + response + " " + parseError.errorString()); + ::close(sockfd); + return std::nullopt; + } + } + return std::nullopt; +} + QString IsmasClient::updateNewsToIsmas(char const *event, int percent, int resultCode, @@ -53,6 +273,54 @@ QString IsmasClient::updateNewsToIsmas(char const *event, return buf; } +QString IsmasClient::errorBackendNotConnected(QString const &info, + QString const &version) { + return updateNewsToIsmas("U0003", + m_progressInPercent, + RESULT_CODE::INSTALL_ERROR, + "CHECK BACKEND CONNECTIVITY", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::backendConnected(QString const &info, QString const &version) { + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "CHECK BACKEND CONNECTIVITY", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::execOpkgCommand(QString const &info, QString const &version) { + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "EXECUTE OPKG COMMAND", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::updateTriggerSet(QString const &info, QString const &version) { + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "CHECK UPDATE TRIGGER", + info.toStdString().c_str(), + version.toStdString().c_str()); + +} + +QString IsmasClient::errorUpdateTrigger(QString const &info, QString const &version) { + return updateNewsToIsmas("U0003", + m_progressInPercent, + RESULT_CODE::INSTALL_ERROR, + "CHECK UPDATE TRIGGER", + info.toStdString().c_str(), + version.toStdString().c_str()); + +} + QString IsmasClient::updateOfPSASendVersion(PSAInstalled const &psa) { //static int const constexpr SIZE = 4096*8; static char buf[4096*2]; @@ -376,39 +644,101 @@ QString IsmasClient::updateOfPSASendVersion(PSAInstalled const &psa) { psa.pluginVersion.prmCalculatePriceConfigUi.toStdString().c_str(), psa.pluginVersion.tcpZVT.toStdString().c_str()); - printf("buf=%s\n", buf); + qInfo() << buf; return buf; } - -QString IsmasClient::updateOfPSAActivated() { +QString IsmasClient::updateOfPSAContinues(QString currentStage, + QString currentStageInfo, + QString const &version) { return updateNewsToIsmas("U0010", - 1, - 0, - "activated", - "detected WAIT state", - "1.0.0"); + m_progressInPercent, + RESULT_CODE::SUCCESS, + currentStage.toStdString().c_str(), + currentStageInfo.toStdString().c_str(), + version.toStdString().c_str()); } -QString IsmasClient::updateOfPSASucceeded() { +QString IsmasClient::updateOfPSAStarted(QString const &version) { + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "START", + "detected WAIT state: start update process", + version.toStdString().c_str()); +} + +QString IsmasClient::checkoutBranch(QString const &info, QString const &version) { + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "BRANCH-CHECKOUT", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::cloneAndCheckoutCustomerRepository(QString const &info, QString const &version) { // clone and checkout customer repository + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "CLONE-CHECKOUT", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::gitFetch(QString const &info, QString const &version) { + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "GIT-FETCH", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::errorGitFetch(int resultCode, QString const &info, QString const &version) { + return updateNewsToIsmas("U0003", + m_progressInPercent, + resultCode, + "GIT-FETCH-FAILED", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::updateOfPSAActivated(QString const &version) { // sent even after success + m_progressInPercent = 0; + return updateNewsToIsmas("U0002", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "UPDATE ACTIVATED", + "reset WAIT state", + version.toStdString().c_str()); +} + +QString IsmasClient::updateOfPSASucceeded(QString const &version) { + m_progressInPercent = 0; return updateNewsToIsmas("U0001", - 100, - 0, - "update_succeeded", - "", - "1.0.0"); + m_progressInPercent, + RESULT_CODE::SUCCESS, + "UPDATE SUCCESS", + "update process succeeded", + version.toStdString().c_str()); } -QString IsmasClient::setUpdatesAvailable() { - return updateNewsToIsmas("U0099", - 10, - 0, - "set_updates_available", - "", - ""); +QString IsmasClient::sanityCheckFailed(int resultCode, QString reason, QString const &version) { + return updateNewsToIsmas("U0003", + m_progressInPercent, + resultCode, + "SANITY-CHECK-FAILED", + reason.toStdString().c_str(), + version.toStdString().c_str()); } - - -bool checkForAvailableUpdates(); +QString IsmasClient::updateOfPSAFailed(int resultCode, QString reason, QString const &version) { + return updateNewsToIsmas("U0003", + m_progressInPercent, + resultCode, + "UPDATE ERROR", + reason.toStdString().c_str(), + version.toStdString().c_str()); +} diff --git a/ismas/ismas_client.h b/ismas/ismas_client.h index 7fbd5a7..fdda53d 100644 --- a/ismas/ismas_client.h +++ b/ismas/ismas_client.h @@ -111,7 +111,28 @@ struct PSAInstalled { class IsmasClient : public QObject { Q_OBJECT + int m_progressInPercent; public: + explicit IsmasClient() : m_progressInPercent(1) {} + + enum APISM { + DB_PORT = 7777, + DIRECT_PORT = 7778 + }; + + enum RESULT_CODE { + SUCCESS=0, + NO_UPDATE_NECESSARY=1, + BACKUP_FAILED=2, + WRONG_PACKAGE=3, + INSTALL_ERROR=4}; + + static std::optional + sendRequestReceiveResponse(int port, QString const &request); + + int getProgressInPercent() const {return m_progressInPercent; } + void setProgressInPercent(int procent) { m_progressInPercent = procent; } + QString updateNewsToIsmas(char const *event, int percent, int resultCode, @@ -119,12 +140,32 @@ public: char const *step_result, char const *version); - QString updateOfPSAActivated(); - QString updateOfPSASucceeded(); + QString updateOfPSAStarted(QString const &version = QString()); // start of update process + QString cloneAndCheckoutCustomerRepository(QString const &info, QString const &version = QString()); // clone and checkout customer repository + QString checkoutBranch(QString const &info, QString const &version = QString()); // checkout branch + QString errorBackendNotConnected(QString const &info, QString const &version = QString()); // checkout branch + QString backendConnected(QString const &info, QString const &version = QString()); + QString updateTriggerSet(QString const &info, QString const &version = QString()); + QString errorUpdateTrigger(QString const &info, QString const &version = QString()); + QString gitFetch(QString const &info, QString const &version = QString()); + QString execOpkgCommand(QString const &info, QString const &version = QString()); + QString errorGitFetch(int resultCode, QString const &info, QString const &version = QString()); + QString updateOfPSAActivated(QString const &version = QString()); + // and update accepted + QString updateOfPSASucceeded(QString const &version = QString()); // update process succeeded + QString updateOfPSAContinues(QString currentStage, QString currentStageInfo, QString const &version = QString()); + QString updateOfPSAFailed(int resultCode, QString reason, QString const &version = QString()); + QString sanityCheckFailed(int resultCode, QString reason, QString const &version = QString()); + QString updateOfPSASendVersion(PSAInstalled const &psa); - QString setUpdatesAvailable(); - bool checkForAvailableUpdates(); + private: + static void printDebugMessage(int port, QString const &clientIP, int clientPort, + QString const &message); + static void printInfoMessage(int port, QString const &clientIP, int clientPort, + QString const &message); + static void printErrorMessage(int port, QString const &clientIP, int clientPort, + QString const &message); }; #endif // ISMAS_CLIENT_H_INCLUDED