diff options
| author | Sami Littow <sami.littow@qt.io> | 2023-01-11 11:16:48 +0200 |
|---|---|---|
| committer | Sami Littow <sami.littow@qt.io> | 2023-01-11 11:16:48 +0200 |
| commit | d336134912de78c78594ab674b246d0a520889f6 (patch) | |
| tree | bd5660a33bd4a7a8803d998ff55b185cf04415d4 | |
| parent | cd8509d3b5c91d93b16b3f6da2e760632cb49e67 (diff) | |
Preliminary Squish support
| -rw-r--r-- | CHANGELOG | 5 | ||||
| -rw-r--r-- | CMakePresets.json | 35 | ||||
| -rw-r--r-- | include/commonsetup.h | 35 | ||||
| -rw-r--r-- | include/httpclient.h | 5 | ||||
| -rw-r--r-- | include/licenser.h | 2 | ||||
| -rw-r--r-- | include/utils.h | 3 | ||||
| -rw-r--r-- | qtlicd.ini | 9 | ||||
| -rw-r--r-- | qtlicensetool/qtlicensetool.cpp | 45 | ||||
| -rw-r--r-- | qtlicensetool/qtlicensetool.h | 4 | ||||
| -rw-r--r-- | src/clienthandler.cpp | 19 | ||||
| -rw-r--r-- | src/daemon_clients/clienthandler.h | 10 | ||||
| -rw-r--r-- | src/daemon_clients/clitoolhandler.h | 13 | ||||
| -rw-r--r-- | src/daemon_clients/cocohandler.h | 3 | ||||
| -rw-r--r-- | src/daemon_clients/squishhandler.h | 55 | ||||
| -rw-r--r-- | src/daemon_clients/squishidehandler.h | 3 | ||||
| -rw-r--r-- | src/httpclient.cpp | 31 | ||||
| -rw-r--r-- | src/jsonhandler.cpp | 2 | ||||
| -rw-r--r-- | src/licenser.cpp | 94 | ||||
| -rw-r--r-- | src/tcpserver.cpp | 6 | ||||
| -rw-r--r-- | src/utils.cpp | 32 |
20 files changed, 243 insertions, 168 deletions
@@ -37,5 +37,8 @@ Changes in 2.0.4: Changes in 2.1.0: - Floating license support added (Squish) - Daemon name 'licd' changed to 'qtlicd' -- Concept of "long-term" changed to "permanent" +- Concept of "long-term" renamed to "permanent" - Daemon architecture changed towards more modular approach +- Fixed a bug with SSL support +- Added HW ID calculation if empty (hw_id now empty by default in qtlicd.ini) +- Removed possibility to override license server address from CLI diff --git a/CMakePresets.json b/CMakePresets.json index ba0884e..d4d9f74 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,4 +1,4 @@ -{ +{ "version": 3, "configurePresets": [ { @@ -7,9 +7,7 @@ "generator": "Ninja", "binaryDir": "${sourceDir}/build", "installDir": "${sourceDir}/deploy/${presetName}", - "cacheVariables": { - - }, + "cacheVariables": {}, "condition": { "type": "equals", "lhs": "${hostSystemName}", @@ -55,16 +53,25 @@ "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } + }, + { + "name": "arm64", + "displayName": "Configure preset using toolchain file", + "description": "Sets Ninja generator, build and install directory", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_TOOLCHAIN_FILE": "", + "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}" + } } ], - - "buildPresets": - [ - { - "name": "build", - "configurePreset": "x64-debug", - "jobs": 2 - } + "buildPresets": [ + { + "name": "build", + "configurePreset": "x64-debug", + "jobs": 2 + } ] - -} +}
\ No newline at end of file diff --git a/include/commonsetup.h b/include/commonsetup.h index 65a78e3..92d27bb 100644 --- a/include/commonsetup.h +++ b/include/commonsetup.h @@ -19,9 +19,10 @@ #define DAEMON_VERSION_CMD "daemon_version" #define RESERVATION_QUERY_CMD "reservation_query" #define LICENSE_REQUEST_CMD "license" -#define LONGTERM_REQUEST_CMD "longterm" +#define PERMANENT_REQUEST_CMD "permanent" #define OP_ADD_RESERVATION "add" #define OP_REMOVE_RESERVATION "remove" +#define OP_QA_RELEASE_RESERVATION "release" #define QTLICENSETOOL_APP_NAME "clitool" #define SQUISH_IDE_APP_NAME "squish-ide" @@ -50,27 +51,27 @@ struct License { uint64_t last_timestamp = 0; // | uint64_t current_timestamp = 0; // | For internal use only, not in server resp JSON - uint64_t expiry_epoch = 0; // _| - uint16_t leeway_hours = 0; - std::string message; + uint64_t expiry_epoch = 0; // | bool status = false; - std::string expiry_date; - std::string operation; + std::string message; + std::string user_id; std::string license_key; std::string license_id; + std::string expiry_date; std::string reservation_id; - std::string parent_reservation_id; // TODO Coming with qa tools: Check with server end! - std::string user_id; + std::string parentReservation; + uint16_t leeway_hours = 0; }; enum RequestReply { - e_bad_request = -1, - e_got_response = 0, - e_license_granted = 1, - e_license_rejected = 2, - e_no_conn_leeway = 3, - e_license_pool_full = 4, - e_bad_connection = 5 + e_bad_request = -1, + e_got_response = 0, + e_license_granted = 1, + e_license_rejected = 2, + e_no_conn_leeway = 3, + e_license_pool_full = 4, + e_no_permanent_to_release = 5, + e_bad_connection = 6 }; enum class RequestType { @@ -97,7 +98,7 @@ enum class ClientType { // Struct to store request info struct RequestInfo { uint16_t socketId; - RequestType type = RequestType::no_request; + RequestType reqType = RequestType::no_request; ClientType client; uint16_t updateIntervalSecs; std::string licenseFile; @@ -112,7 +113,7 @@ struct RequestInfo { std::string serverAddr; uint64_t startTimestamp; // used by QA-Tools only uint64_t stopTimestamp = 0; // used by QA-Tools only - std::string runnerType; // 'tester' | 'exe', used by QA-Tools only + std::string runnerType; // "qa_tester" || "qa_exe", used by QA-Tools only std::string parentReservationId; // used by QA-Tools only }; diff --git a/include/httpclient.h b/include/httpclient.h index 6ee1f86..b1a63c1 100644 --- a/include/httpclient.h +++ b/include/httpclient.h @@ -26,7 +26,6 @@ struct HttpRequest { std::string payload; std::string url; - std::string authKey; std::string reply; curl_slist *headers = nullptr; }; @@ -40,7 +39,7 @@ class HttpClient public: explicit HttpClient(const std::string &serverUrl, const std::string &requestAccessPoint, - const std::string &longtermAccessPoint, + const std::string &permanentccessPoint, const std::string &versionAccessPoint); ~HttpClient() {} @@ -50,7 +49,7 @@ public: private: std::string m_serverUrl; std::string m_requestAccessPoint; - std::string m_longtermAccessPoint; + std::string m_permanentAccessPoint; std::string m_versionAccessPoint; std::string m_userAgent = "License daemon / "; std::string m_lastError; diff --git a/include/licenser.h b/include/licenser.h index 449ee9a..1f7256d 100644 --- a/include/licenser.h +++ b/include/licenser.h @@ -56,7 +56,7 @@ private: std::string askServerVersion(); std::string getDaemonVersion(); int checkReportsDue(); - void clientDisconnected(int socketId); + void removeClient(int socketId); void setHwId(); LicdSetup *settings; diff --git a/include/utils.h b/include/utils.h index 13a4e6c..a215ffd 100644 --- a/include/utils.h +++ b/include/utils.h @@ -75,8 +75,9 @@ std::string epochToString(time_t epochTime, const char* format = "%Y-%m-%d %H:%M time_t stringToEpoch(const char* theTime, const char* format = "%Y-%m-%d %H:%M:%S"); uint64_t getTimestampNow(); -// Find out host OS +// System utils std::string getOsName(); +std::string getSystemHwInfoString(); // Swap endian of any data template <typename T> T swapEndian(T val) @@ -26,7 +26,7 @@ reservation_access_point=/api/v2/reservations # long-term_access_point # License server REST access point for long-term license reservation requests # Type: string -long-term_access_point=/api/v2/reservations/long-term +permanent_access_point=/api/v2/reservations/long-term ################### # version_query_access_point # License server REST access point for version query requests @@ -42,7 +42,7 @@ moc_renewal_interval=24 # squish_report_interval # Duration (in secs) between update calls to the server while Squish is working. # Type: int -squish_report_interval=300 +squish_report_interval=5 ################### # coco_update_interval # Duration (in secs) between update calls to the server while Coco is working. @@ -53,9 +53,10 @@ coco_update_interval=5 tcp_listening_port=60000 ################### # hw_id -# Calculated hardware ID of the host machine. Goes obsolete as soon it can be calculated on the fly +# Calculated hardware ID of the host machine. This is empty by default, will be calculated in the first daemon run. +# Can be recalculated by deleting the value (making it empty again) and restarting the daemon # Type: string -hw_id=c31aa37762ef3bb84e09eeea0bc9bccb6b96a81b2657b1a356ec51709975a5ea +hw_id= # user_settings_tag # Tag under which user settings should reside # Type: string diff --git a/qtlicensetool/qtlicensetool.cpp b/qtlicensetool/qtlicensetool.cpp index 75f0343..f0e6105 100644 --- a/qtlicensetool/qtlicensetool.cpp +++ b/qtlicensetool/qtlicensetool.cpp @@ -25,10 +25,10 @@ int main(int argc, char *argv[]) } // Then those requiring connection - if (action == "--longterm" || action == "-L") { + if (action == "--permanent" || action == "-p") { // Override setup with cmd-line args: overrideSetup(&setup, argc, argv); - doLongtermRequest(&setup); + doPermanentRequest(&setup); } else if (action == "--serverversion" || action == "-S") { askStatus(SERVER_VERSION_CMD, &setup); } else if (action == "--daemonversion" || action == "-D") { @@ -75,23 +75,23 @@ void overrideSetup(LicdSetup *setup, int argc, char *argv[]) { // Pick up the cmd-line switches: Start looping args from index 1, // as first one is the command itself - bool longterm = false; + bool permanent = false; for (int i = 1; i < argc; i++) { std::string sw = utils::trimStr(argv[i]); std::string val = ""; - if (sw == "-L" || sw == "--longterm") { - longterm = true; + if (sw == "-p" || sw == "--permanent") { + permanent = true; continue; } - if (longterm) { - // Longterm operation (add|remove) has to come right after the -L switch, not later + if (permanent) { + // Permanent operation (add|remove) has to come right after the -P switch, not later if (sw == OP_ADD_RESERVATION || sw == OP_REMOVE_RESERVATION) { setup->set("operation", sw); - longterm = false; + permanent = false; continue; } else { - errorAndExit("Invalid longterm operation"); + errorAndExit("Invalid operation for permanen license request"); } } @@ -120,14 +120,6 @@ void overrideSetup(LicdSetup *setup, int argc, char *argv[]) } setup->set("licd_addr", val.substr(0, colPos)); setup->set("licd_port", val.substr(colPos+1)); - } else if (sw == "-l") { - size_t colPos = val.find(':'); - if (colPos == std::string::npos) { - errorAndExit("Invalid license server address: Must be in form <url>:<port>"); - } - setup->set("license_server_addr", val.substr(0, colPos)); - setup->set("license_server_port", val.substr(colPos + 1)); - } else { std::string error = "Invalid argument:" + sw; errorAndExit(error); @@ -135,11 +127,11 @@ void overrideSetup(LicdSetup *setup, int argc, char *argv[]) } } -int doLongtermRequest(LicdSetup *setup) +int doPermanentRequest(LicdSetup *setup) { std::string operation = setup->get("operation"); if (operation != OP_ADD_RESERVATION && operation != OP_REMOVE_RESERVATION) { - std::string error = "Invalid operation \"" + operation; + std::string error = "Invalid operation \"" + operation + "\""; error += "\""; errorAndExit(error); } @@ -150,17 +142,12 @@ int doLongtermRequest(LicdSetup *setup) // Ready to build the request std::stringstream request; - request << LONGTERM_REQUEST_CMD << " " << setup->get("operation") + request << PERMANENT_REQUEST_CMD << " " << setup->get("operation") << " -u " << setup->get("user_id") << " -i " << setup->get("license_id") << " -e " << setup->get("user_email") << " -a " << QTLICENSETOOL_APP_NAME << " -v " << QTLICENSETOOL_VERSION; - if (!setup->get("license_server_addr").empty()) { - // Add optional license server URL if specified - request << " -l " << setup->get("license_server_addr") - << ":" << setup->get("license_server_port"); - } // Connect and send/receive the request TcpClient tcp(setup->get("licd_addr"), daemonPort); @@ -198,8 +185,8 @@ void helpAndExit() << " -S or --serverversion : Prints out License Server version\n" << " -D or --daemonversion : Prints out the License Service (daemon) version\n" << " -r or --reservation : Prints out the list of active reservations\n" - << " -L or --longterm : Longterm reservation (add or remove)\n" - << " For longterm usage ('-L' or '--longterm'), you must tell which operation to do:\n" + << " -p or --permanent : Request permanent reservation (add or remove)\n" + << " For permanent request ('-P' or '--peramanent'), you must tell which operation to do:\n" << " add|remove <--- Either one needs to be there\n" << " Optionat parameters which override [default| section settings:\n" << " -d = Full license daemon address and port separated by ':'\n" @@ -207,8 +194,8 @@ void helpAndExit() << " -i = Your Qt license ID number (must be an int)\n" << " -l = License server address and port separated by ':'\n" << " Example:\n" - << " > qtlicensetool --longterm add : Make a long-term license reservation\n" - << " > qtlicensetool -L add -i 462412 : Same as above, but using different license ID\n" + << " > qtlicensetool --permanent add : Make a permanent license reservation\n" + << " > qtlicensetool -P add -i 462412 : Same as above, but using different license ID\n" << " > qtlicensetool --daemonversion : Prints out a daemon version number\n"; exit(EXIT_SUCCESS); } diff --git a/qtlicensetool/qtlicensetool.h b/qtlicensetool/qtlicensetool.h index c92c33c..8a88560 100644 --- a/qtlicensetool/qtlicensetool.h +++ b/qtlicensetool/qtlicensetool.h @@ -41,13 +41,13 @@ enum qt_tool_action_type { #include "commonsetup.h" #include "licdsetup.h" -#define APP_NAME "Qt long-term license CLI" +#define APP_NAME "Qt licensing CLI" #define BUFFER_SIZE 1024 int main(int argc, char *argv[]); void showVersion(); int askStatus(const std::string &statusRequest, LicdSetup *setup); -int doLongtermRequest(LicdSetup *setup); +int doPermanentRequest(LicdSetup *setup); void overrideSetup(LicdSetup *setup, int argc, char *argv[]); void errorAndExit(const std::string &reason = ""); void helpAndExit(); diff --git a/src/clienthandler.cpp b/src/clienthandler.cpp index 789176b..c60d730 100644 --- a/src/clienthandler.cpp +++ b/src/clienthandler.cpp @@ -44,27 +44,28 @@ bool ClientHandler::checkLicenseExpiryTime(std::string &reply) int ClientHandler::parseRequest() { // First find out the command (and operation for longterm case) + std::string cmd = utils::trimStr(params[0]); - m_request.type = RequestType::no_request; + m_request.reqType = RequestType::no_request; if (cmd == LICENSE_REQUEST_CMD) { - m_request.type = RequestType::license_request; - } else if (cmd == LONGTERM_REQUEST_CMD) { - m_request.type = RequestType::long_term_request; + m_request.reqType = RequestType::license_request; + } else if (cmd.substr(0, std::string(PERMANENT_REQUEST_CMD).length()) == PERMANENT_REQUEST_CMD) { + m_request.reqType = RequestType::long_term_request; // find either 'add' or 'remove' - std::string op = utils::trimStr(cmd.substr(8, cmd.length() -1)); + std::string op = utils::trimStr(cmd.substr(std::string(PERMANENT_REQUEST_CMD).length(), cmd.length() -1)); if (op != OP_ADD_RESERVATION && op != OP_REMOVE_RESERVATION) { - std::cout << "Invalid longterm operation: " << op; + std::cout << "Invalid operation for permanent request: " << op; return e_bad_request; } m_request.operation = op; } else if (cmd == SERVER_VERSION_CMD) { - m_request.type = RequestType::server_version; + m_request.reqType = RequestType::server_version; buildRequestJson(); return 0; } else if (cmd == DAEMON_VERSION_CMD) { - m_request.type = RequestType::daemon_version; + m_request.reqType = RequestType::daemon_version; } else if (cmd == RESERVATION_QUERY_CMD) { - m_request.type = RequestType::reservation_query; + m_request.reqType = RequestType::reservation_query; } else { std::cout << "Invalid command: " << cmd << std::endl; return e_bad_request; diff --git a/src/daemon_clients/clienthandler.h b/src/daemon_clients/clienthandler.h index 1181294..695847e 100644 --- a/src/daemon_clients/clienthandler.h +++ b/src/daemon_clients/clienthandler.h @@ -21,8 +21,6 @@ class ClientHandler { } virtual ~ClientHandler() { } - // If this client is using a floating model, it needs to be cached - // (Squish, Coco - Socket is alive until client stops working): Set this to true std::vector<std::string> params; @@ -30,15 +28,17 @@ class ClientHandler virtual bool isLicenseRequestDue() = 0; virtual int parseAndSaveResponse(std::string &response) = 0; virtual void buildRequestJson() = 0; + virtual void prepareRelease() { return; } + virtual void resetTimer() { return; } bool hasFloatingLicense() { return m_floatingLicense; } + bool hasParent() { return m_hasParent; } void updateLicense(const std::string &responseJson); int parseRequest(); RequestInfo getRequest() {return m_request;} int getSocketId() { return m_request.socketId; } - RequestType getRequestType() { return m_request.type; } + RequestType getRequestType() { return m_request.reqType; } int getClientType() { return (int)m_request.client; } - virtual void release() { return; } protected: License m_license; @@ -46,6 +46,7 @@ class ClientHandler LicdSetup m_settings;; uint64_t m_updateInterval; bool m_floatingLicense = false; + bool m_hasParent = false; bool checkLicenseExpiryTime(std::string &reply); bool checkLeewayTime(std::string &reply); @@ -57,6 +58,7 @@ static std::map<RequestReply, std::string> replyString { {e_license_rejected, "No valid license acquired"}, {e_no_conn_leeway, "License granted with warning: No server connection. Leeway time left: "}, {e_license_pool_full, "All licenses in use. No more license available on the server."}, + {e_no_permanent_to_release, "No permanent reservations available to be released"}, {e_bad_connection, "No connection to server. Try again later."} }; diff --git a/src/daemon_clients/clitoolhandler.h b/src/daemon_clients/clitoolhandler.h index a63dd40..c9446d1 100644 --- a/src/daemon_clients/clitoolhandler.h +++ b/src/daemon_clients/clitoolhandler.h @@ -19,10 +19,11 @@ class CliToolHandler : public virtual ClientHandler { void buildRequestJson() override { - if (m_request.type == RequestType::server_version) { + if (m_request.reqType == RequestType::server_version) { m_request.payload = ""; return; } + // Permanent operation: std::stringstream pay; pay << "{"; pay<< "\"license_number\":" << "\"" << m_request.licenseId << "\","; @@ -36,22 +37,22 @@ class CliToolHandler : public virtual ClientHandler { { JsonHandler json(response); std::stringstream ss; - std::cout << json.dump(4); - if (m_request.type == RequestType::long_term_request) { + if (m_request.reqType == RequestType::long_term_request) { // long-term request: Have "message" field from response JSON directly as a reply to the user response = json.get("message"); if (json.get("status") != "true" ) { - if (json.get("message") == "License fully reserved") { + if (response == "License fully reserved") { response = replyString[e_license_pool_full]; + } else if (response.find("cannot be early released") != std::string::npos) { + response = replyString[e_no_permanent_to_release]; } else { response = replyString[e_license_rejected]; } // Do not touch the license file, just return return 1; } - } else if (m_request.type == RequestType::server_version) { - std::cout << "Parsing server version response\n"; + } else if (m_request.reqType == RequestType::server_version) { response = "Qt License Server v"; response += json.get("version"); return 0; // Do not touch cached license files pls. diff --git a/src/daemon_clients/cocohandler.h b/src/daemon_clients/cocohandler.h index d25b36c..d4a32d9 100644 --- a/src/daemon_clients/cocohandler.h +++ b/src/daemon_clients/cocohandler.h @@ -13,13 +13,14 @@ class CocoHandler : public ClientHandler { : ClientHandler(request, settings) { m_updateInterval = utils::strToInt(m_settings.get("coco_report_interval")); + m_floatingLicense = true; } bool isLicenseRequestDue() override { return true; } bool isCachedReservationValid(std::string &reply) override {return true;} int parseAndSaveResponse(std::string &response) override { return 0; } void buildRequestJson() override {return;} - void release() override + void prepareRelease() override { // TODO return; diff --git a/src/daemon_clients/squishhandler.h b/src/daemon_clients/squishhandler.h index 366c624..24c5aed 100644 --- a/src/daemon_clients/squishhandler.h +++ b/src/daemon_clients/squishhandler.h @@ -15,26 +15,32 @@ class SquishHandler : public ClientHandler { m_updateInterval = utils::strToInt(m_settings.get("squish_report_interval")); m_request.operation = OP_ADD_RESERVATION; m_request.startTimestamp = utils::getTimestampNow(); + m_license.last_timestamp = 0; + m_floatingLicense = true; } - bool isLicenseRequestDue() override { return true; } - bool isCachedReservationValid(std::string &reply) override { return true; } + bool isCachedReservationValid(std::string &reply) override { return false; } void buildRequestJson() override { std::stringstream pay; pay << "{"; - pay<< "\"license_number\":" << "\"" << m_request.licenseId << "\","; - pay << "\"user_id\":" << "\"" << m_request.userId << "\","; - pay << "\"hw_id\":" << "\"" << m_settings.get("hw_id") << "\","; - pay << "\"operation\":" << "\"" << m_request.operation << "\","; - pay << "\"src\":" << "\"" << m_request.appName << "\","; - pay << "\"host_os\":" << "\"" << m_settings.get("host_os") << "\","; - pay << "\"src_version\":" << "\"" << m_request.appVersion << "\","; - pay << "\"email\":" << "\"" << m_request.email << "\""; + pay << "\"license_number\":\"" << m_request.licenseId << "\","; + pay << "\"user_id\":\"" << m_request.userId << "\","; + pay << "\"hw_id\":\"" << m_settings.get("hw_id") << "\","; + pay << "\"operation\":\"" << m_request.operation << "\","; + pay << "\"type\":\"" << m_request.runnerType << "\","; + pay << "\"src\":\"" << m_request.appName << "\","; + pay << "\"host_os\":\"" << m_settings.get("host_os") << "\","; + pay << "\"src_version\":\"" << m_request.appVersion << "\","; + pay << "\"reservation_id\":\"" << m_request.parentReservationId << "\","; + pay << "\"email\":\"" << m_request.email << "\""; + if (m_request.operation == OP_QA_RELEASE_RESERVATION) { + pay << "\"custom_duration\":\"" << m_request.stopTimestamp + - m_request.startTimestamp << "\","; + } pay << "}"; m_request.payload = pay.str(); - m_floatingLicense = true; } int parseAndSaveResponse(std::string &response) override @@ -43,8 +49,6 @@ class SquishHandler : public ClientHandler { // response JSON is converted to a reply string to the client from now on std::stringstream ss; m_license.status = (json.get("status") == "true") ? true : false; - - if (m_license.status) { ss << replyString[e_license_granted]; ss << " expiry_date=" << json.get("expiry_date"); @@ -65,15 +69,11 @@ class SquishHandler : public ClientHandler { m_license.user_id = json.get("user_id"); m_license.reservation_id = json.get("reservation_id"); m_license.license_key = json.get("license_key"); - m_license.parent_reservation_id = json.get("parent_reservvation_id"); - m_license.operation = "add"; // Add timestamp into license JSON - struct timeval tp; - gettimeofday(&tp, NULL); - uint64_t timeNow = tp.tv_sec; + m_license.last_timestamp = utils::getTimestampNow(); // Save the JSON, adding timestamp - json.add("last_timestamp", timeNow); + json.add("last_timestamp", m_license.last_timestamp); int result = utils::writeToFile(m_request.licenseFile, json.dump(4)); if (result != 0) { std::cout << "ERROR saving license file: '" << m_request.licenseFile << "': " << strerror(result) << std::endl; @@ -81,10 +81,23 @@ class SquishHandler : public ClientHandler { return 0; } - void release() override + void prepareRelease() override { - m_request.operation = OP_REMOVE_RESERVATION; + m_request.operation = OP_QA_RELEASE_RESERVATION; m_request.stopTimestamp = utils::getTimestampNow(); buildRequestJson(); } + + void resetTimer() override + { + m_license.last_timestamp = utils::getTimestampNow(); + } + + bool isLicenseRequestDue() override { + uint64_t now = utils::getTimestampNow(); + if (m_license.last_timestamp == 0) return true; // startup + if ((now - m_license.last_timestamp) > m_updateInterval) + return true; + return false; + } };
\ No newline at end of file diff --git a/src/daemon_clients/squishidehandler.h b/src/daemon_clients/squishidehandler.h index a17b2db..8f08e0c 100644 --- a/src/daemon_clients/squishidehandler.h +++ b/src/daemon_clients/squishidehandler.h @@ -14,13 +14,14 @@ class SquishIdeHandler : public ClientHandler { { m_updateInterval = utils::strToInt(m_settings.get("squish_report_interval")); m_request.operation = OP_ADD_RESERVATION; + m_floatingLicense = true; } bool isLicenseRequestDue() override { return true; } bool isCachedReservationValid(std::string &reply) override {return true;} void buildRequestJson() override {return;} int parseAndSaveResponse(std::string &response) override { return 0; } - void release() override + void prepareRelease() override { // TODO return; diff --git a/src/httpclient.cpp b/src/httpclient.cpp index 730b310..0682be8 100644 --- a/src/httpclient.cpp +++ b/src/httpclient.cpp @@ -7,18 +7,18 @@ size_t WriteCallback(char *contents, size_t size, size_t nmemb, void *userp) { - std::cout << "Reading data\n"; + //std::cout << "Reading data\n"; ((std::string *)userp)->append(contents, size * nmemb); return size * nmemb; } HttpClient::HttpClient( const std::string &serverUrl, const std::string &requestAccessPoint, - const std::string &longtermAccessPoint, + const std::string &permanentAccessPoint, const std::string &versionAccessPoint) : m_serverUrl(serverUrl), m_requestAccessPoint(requestAccessPoint), - m_longtermAccessPoint(longtermAccessPoint), + m_permanentAccessPoint(permanentAccessPoint), m_versionAccessPoint(versionAccessPoint) { m_userAgent += DAEMON_VERSION; @@ -32,6 +32,8 @@ int HttpClient::sendRequest(std::string &reply, const std::string &payload, /* specify URL to POST */ request.url = server; + request.payload = payload; + if (server.empty()) { // If server URL is not given as param, we use the default request.url = m_serverUrl; @@ -39,30 +41,26 @@ int HttpClient::sendRequest(std::string &reply, const std::string &payload, if (!authKey.empty()) { // normal request request.url += m_requestAccessPoint; + std::string auth = "Authorization: " + authKey; + //std::cout << "Setting authorization: " << auth << std::endl; + request.headers = curl_slist_append(request.headers, auth.c_str()); } else { if (payload.empty()) { // version query request.url += m_versionAccessPoint; } else { - // long-term request - request.url += m_longtermAccessPoint; + // permanentrequest + request.url += m_permanentAccessPoint; } } - request.authKey = authKey; - request.payload = payload; - if (!request.authKey.empty()) { - // Set authorization only if applicable - std::string auth = "Authorization: " + request.authKey; - request.headers = curl_slist_append(request.headers, auth.c_str()); - } std::string agent = "User-Agent: " + m_userAgent; request.headers = curl_slist_append(request.headers, agent.c_str()); request.headers = curl_slist_append(request.headers, "Accept: */*"); request.headers = curl_slist_append(request.headers, "Content-Type: application/json"); request.headers = curl_slist_append(request.headers, "charset: utf-8"); - std::cout << "HTTPClient() -- server URL " << request.url << std::endl; + //std::cout << "HTTPClient() -- server URL " << request.url << std::endl; int retVal = 0; @@ -78,8 +76,8 @@ int HttpClient::sendRequest(std::string &reply, const std::string &payload, retVal = 1; } if (retVal == 0) { - std::cout << request.reply.length() << " bytes retrieved from license server:\n"; - std::cout << request.reply << std::endl; + std::cout << request.reply.length() << " bytes retrieved from license server\n"; + //std::cout << request.reply << std::endl; } else { std::cout << m_lastError << std::endl; } @@ -112,6 +110,9 @@ int HttpClient::doRequest(CURL *curl, HttpRequest &request) else { curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); } + if(curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL) != CURLE_OK) { + std::cout << "Warning! No SSL support available\n"; + } curl_easy_setopt(curl, CURLOPT_TIMEOUT, SERVER_CONN_TIMEOUT); // get it diff --git a/src/jsonhandler.cpp b/src/jsonhandler.cpp index f46fd72..18430c1 100644 --- a/src/jsonhandler.cpp +++ b/src/jsonhandler.cpp @@ -76,10 +76,10 @@ std::string JsonHandler::dump(uint8_t indent) top << "{"; if (indent > 0) { res << ind << "\"reservation\": {" << std::endl; - top << std::endl; } else { res << "\"reservation\":{"; } + top << std::endl; uint8_t count = 0; for (auto const &item : m_items) { diff --git a/src/licenser.cpp b/src/licenser.cpp index 66c4c2d..0b3cc48 100644 --- a/src/licenser.cpp +++ b/src/licenser.cpp @@ -10,7 +10,7 @@ Licenser::Licenser(uint16_t tcpPort) // Init daemon settings settings = new LicdSetup(e_set_type_daemon); - // Check if our setup already has some hw id - generate it if not + // Check if our setup already has some hw id - generate one if not if (settings->get("hw_id").empty()) { setHwId(); } @@ -22,7 +22,7 @@ Licenser::Licenser(uint16_t tcpPort) // Start the HTTP client m_http = new HttpClient(settings->get("server_addr"), settings->get("reservation_access_point"), - settings->get("long-term_access_point"), + settings->get("permanent_access_point"), settings->get("version_query_access_point")); // Start the TCP/IP server m_server = new TcpServer(tcpPort); @@ -37,7 +37,6 @@ Licenser::~Licenser() std::cout << "Daemon stopped." << std::endl; } - int Licenser::listen() { m_infoString = ""; @@ -45,31 +44,32 @@ int Licenser::listen() std::string input = m_server->listenToClients(socket); input = utils::trimStr(input); if (input.empty()) { + if (m_floatingClients.size() > 0) { + checkReportsDue(); + } return 0; //continue; } if (input == CLIENT_CLOSED_MSG) { std::cout << "Client disconnected, socket id " << socket << std::endl; - // Now to check if the client had a floating license: - clientDisconnected(socket); + removeClient(socket); return 0; } - std::cout << "Got an request: " << input << std::endl; + + std::cout << "Got a request: " << input << std::endl; std::string reply = ""; // Holds server response first (if got any). After parsing, holds reply to the client int clientType = parseInputAndCreateCLient(socket, input); - std::cout << "Client type: " << clientType << std::endl; if (clientType != (int)ClientType::client_undefined) { if (m_currentClient->parseRequest() != e_bad_request) { RequestType reqType = m_currentClient->getRequestType(); - std::string payload = ""; if (reqType == RequestType::reservation_query) { reply = checkReservations(); } else if (reqType == RequestType::daemon_version) { reply = getDaemonVersion(); } else { - std::cout << "Initiating request to server\n"; + std::cout << "Initiating a request to the server\n"; if (m_currentClient->isLicenseRequestDue()) { if (sendServerRequest(reply) == e_bad_connection) { // Bad server connection: Check the existing license file for expiry date @@ -92,7 +92,8 @@ int Licenser::listen() } if (m_currentClient->hasFloatingLicense()) { - // QA tool, Coco or Squish (cLient with floating license): Store it in cache + // QA tool, Coco or Squish (client with floating license): Store it in cache + std::cout << "Storing client in cache\n"; m_floatingClients.push_back(m_currentClient); } @@ -104,17 +105,23 @@ int Licenser::listen() if (m_floatingClients.size() > 0) { checkReportsDue(); } + std::cout << "Current clients in cache: " << m_floatingClients.size() << std::endl; return 0; } + int Licenser::checkReportsDue() { // Check if there's any floating clients which has periodic reports due - // TODO!! ClientHandler *client; for (int i = 0 ; i < m_floatingClients.size(); i++) { client = m_floatingClients[i]; if (client->isLicenseRequestDue()) { - // TODO Ping server (Req Json might need updating here) + std::string reply; + //std::cout << "Reporting to server (socket ID " << client->getSocketId() << std::endl; + if (sendServerRequest(reply) != e_bad_connection) { + std::cout << "Reported alive floating reservation with socket ID " << client->getSocketId() << std::endl; + client->resetTimer(); + } } } return 0; @@ -127,6 +134,7 @@ int Licenser::parseInputAndCreateCLient(uint16_t socketId, const std::string &in // to initiate into m_currentClient member RequestInfo request; request.socketId = socketId; + // Find out which class of client is calling ClientType retVal = ClientType::client_undefined; std::vector<std::string> paramList = utils::splitStr(incoming, '-'); @@ -180,14 +188,20 @@ int Licenser::parseInputAndCreateCLient(uint16_t socketId, const std::string &in int Licenser::sendServerRequest(std::string &reply) { - std::string authKey; + std::string auth; RequestInfo request = m_currentClient->getRequest(); if (request.client != ClientType::client_CLI) { // Generate auth hash for HTTP headers (CLI tool does not need it) - doHmacHashSha256(request.payload, settings->get("server_secret"), authKey); // 19755982232ff7b6f6d0f3c57ffc1c0e4f03060e7175d478f7b146fb1e000507"; + std::string hash; + doHmacHashSha256(request.payload, settings->get("server_secret"), hash); // 19755982232ff7b6f6d0f3c57ffc1c0e4f03060e7175d478f7b146fb1e000507"; + // Bearer is the same hash (?) + auth = "apikey " + hash; + //auth += ", Bearer " + hash; } + // Send the request - if (m_http->sendRequest(reply, request.payload, request.serverAddr, authKey) != 0) { + std::cout << "Sending request: " << request.payload << std::endl; + if (m_http->sendRequest(reply, request.payload, request.serverAddr, auth) != 0) { return e_bad_connection; } return e_got_response; @@ -196,7 +210,7 @@ int Licenser::sendServerRequest(std::string &reply) void Licenser::doHmacHashSha256(const std::string &payload, const std::string &secret, std::string &authKey) { std::stringstream ss_result; - ss_result << "apikey "; + ss_result << ""; // Allocate memory for the HMAC std::vector<uint8_t> out(SHA256_HASH_SIZE); @@ -213,10 +227,6 @@ void Licenser::doHmacHashSha256(const std::string &payload, const std::string &s authKey = ss_result.str(); JsonHandler pay(payload); - // Print out the result - std::cout << "Message: " << pay.dump(4) << std::endl; - std::cout << "Key: " << secret << std::endl; - std::cout << "HMAC: " << authKey << std::endl; } std::string Licenser::checkReservations() @@ -246,7 +256,8 @@ std::string Licenser::checkReservations() return reply.str(); } -void Licenser::clientDisconnected(int socketId) { +void Licenser::removeClient(int socketId) { + // Check if the client had a floating license: int index = -1; ClientHandler *client; for (int i = 0; i < m_floatingClients.size(); i++) { @@ -257,13 +268,23 @@ void Licenser::clientDisconnected(int socketId) { break; } } - if (index != -1 && m_currentClient->hasFloatingLicense()) { - m_currentClient->release(); // not implemented yet - // m_floatingClients.erase(index); // TODO don't erase yet, server might be down + if (index == -1 ) { + // non-floating + return; } + // Client has a floating license - need to release the reservation now -} + m_currentClient->prepareRelease(); + std::string reply; + if (sendServerRequest(reply) != e_bad_connection) { + // if connection succeeds, we can forget about that client + std::cout << "Removing floating reservation with socket ID " << m_currentClient->getSocketId() << std::endl; + m_floatingClients.erase(m_floatingClients.begin() + index); + } + + return; +} std::string Licenser::getDaemonVersion() { @@ -273,8 +294,23 @@ std::string Licenser::getDaemonVersion() } void Licenser::setHwId() { - - // TODO calculate hwid hash out of MAC, uname etc... - // Edit the qtlicd.ini to make it permanent + std::cout << "No HW ID found, generating it ... "; + std::string hwInfo = utils::getSystemHwInfoString(); + std::string hash; + doHmacHashSha256(hwInfo, settings->get("server_secret"), hash); + std::string settingsString; + utils::readFile(settingsString, DAEMON_SETTINGS_FILE); + std::vector<std::string> lines = utils::splitStr(settingsString, '\n'); + settingsString = ""; + std::string wanted = "hw_id="; + for (std::string line : lines) { + if (line.substr(0, wanted.length()) == wanted) { + line = wanted + hash; + } + line += '\n'; + settingsString += line; + } + utils::writeToFile(DAEMON_SETTINGS_FILE, settingsString); + settings->set("hw_id", hash); return; -}
\ No newline at end of file +} diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp index b1205ba..ebbdf63 100644 --- a/src/tcpserver.cpp +++ b/src/tcpserver.cpp @@ -34,7 +34,7 @@ TcpServer::TcpServer(uint16_t serverPort) perror("setsockopt failed"); exit(EXIT_FAILURE); } - timeval tv {2, 0}; //t.tv_sec = 2; t.tv_usec = 0; + timeval tv {1, 0}; //t.tv_sec = 2; t.tv_usec = 0; int ret = setsockopt(m_masterSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(timeval)); if (ret < 0) { @@ -105,7 +105,7 @@ std::string TcpServer::listenToClients(int &socket) max_sd = sd; } // timeout - timeval tv{2, 0}; //t.tv_sec = 2; t.tv_usec = 0; + timeval tv{1, 0}; //t.tv_sec = 2; t.tv_usec = 0; // Wait for an activity on one of the sockets. int activity = select((int)max_sd + 1, &m_readfds, NULL, NULL, &tv); @@ -125,6 +125,7 @@ std::string TcpServer::listenToClients(int &socket) // If position is free if (m_clientSocket[i] == 0) { m_clientSocket[i] = new_socket; + socket = i; std::cout << "New connection - adding to list of sockets with id " << i << std::endl; break; } @@ -147,6 +148,7 @@ std::string TcpServer::listenToClients(int &socket) getpeername(sd, (struct sockaddr *)&m_address, (socklen_t *)&m_addrlen); // Close the socket and mark as 0 in list for reuse + socket = i; doCloseSocket(sd); m_clientSocket[i] = 0; data = "closed"; diff --git a/src/utils.cpp b/src/utils.cpp index cb68829..1fa48f7 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -158,7 +158,7 @@ int readFile(std::string &str, const std::string &filepath) if (file.is_open()) { std::string line; while (getline(file, line)) { - ss << line; + ss << line << std::endl; } file.close(); } else { @@ -237,13 +237,13 @@ std::vector<std::string> getDirListing(const std::string &directory, const std:: int deleteFile(const std::string &filepath) { - std::remove(filepath.c_str()); // delete file - bool failed = !std::ifstream("file1.txt"); - if(failed) { - std::perror("Error deleting file"); - return 1; + if (fileExists(filepath)) { + std::remove(filepath.c_str()); // delete file + return 0; } - return 0; + std::cout << "No file " << filepath << " found to delete\n"; + + return 1; } std::string getOsName() @@ -263,6 +263,24 @@ std::string getOsName() #endif } +std::string getSystemHwInfoString() { + // Trying to get individual HW string, using network parameters and current time (epoch) + std::stringstream hwInfo; + std::string ip_mac; +#if _WIN32 + system("ipconfig > hw.txt"); +#elif __APPLE__ || __MACH__ + system("ifconfig > hw.txt"); +#elif __linux__ + system("ip addr > hw.txt"); +#endif + readFile(ip_mac, "hw.txt"); + hwInfo << getTimestampNow() << std::endl << ip_mac; + deleteFile("hw.txt"); + return hwInfo.str(); +} + + #if _WIN32 /* * Windows implementation of missing POSIX strptime() |
