From 6fc0ab0b4dedbba26f55bfb108bc10f1c49c0a29 Mon Sep 17 00:00:00 2001 From: "Degott, Francois Regis" Date: Thu, 12 May 2022 10:32:14 +0200 Subject: [PATCH 1/7] Revert "revert alarm migration" This reverts commit ae5f4f5d88a9ad7a546837a7440c0488221c9386. --- ecpp/alert_rules_list.ecpp | 488 +++++++++++++++++++------------------ 1 file changed, 253 insertions(+), 235 deletions(-) diff --git a/ecpp/alert_rules_list.ecpp b/ecpp/alert_rules_list.ecpp index 1c50fd1bd..661393abd 100644 --- a/ecpp/alert_rules_list.ecpp +++ b/ecpp/alert_rules_list.ecpp @@ -30,9 +30,11 @@ #include #include #include -#include #include #include +#include +#include +#include #include #include #include @@ -49,7 +51,7 @@ bool database_ready; // verify server is ready if (!database_ready) { log_debug ("Database is not ready yet."); - std::string err = TRANSLATE_ME ("Database is not ready yet, please try again after a while."); + std::string err = TRANSLATE_ME ("Database is not ready yet, please try again after a while."); http_die ("internal-error", err.c_str ()); } @@ -60,284 +62,300 @@ bool database_ready; }; CHECK_USER_PERMISSIONS_OR_DIE (PERMISSIONS); - std::string checked_type; - std::string checked_rule_class; - { - std::string type = qparam.param("type"); - std::string rule_class = qparam.param("rule_class"); + // free params + std::string checked_type = qparam.param("type"); + std::string checked_rule_class = qparam.param("rule_class"); // kept for compatibility + std::string checked_asset_type = qparam.param("asset_type"); + std::string checked_asset_sub_type = qparam.param("asset_sub_type"); + std::string checked_in = qparam.param("in"); // location + std::string checked_category = qparam.param("category"); + + // connect to malamute + MlmClientGuard client(mlm_client_new ()); + if (!client.get()) { + log_fatal ("mlm_client_new() failed."); + std::string err = TRANSLATE_ME ("mlm_client_new() failed."); + http_die ("internal-error", err.c_str ()); + } + + std::string client_name = utils::generate_mlm_client_id("web.alert_rules_list"); + log_debug ("malamute client name = '%s'.", client_name.c_str ()); - // type - if (type.empty ()) { - type = "all"; + int rv = mlm_client_connect (client, MLM_ENDPOINT, 1000, client_name.c_str ()); + if (rv == -1) { + log_fatal ("mlm_client_connect (endpoint = '%s', timeout = '%d', address = '%s') failed.", + MLM_ENDPOINT, 1000, client_name.c_str ()); + std::string err = TRANSLATE_ME ("mlm_client_connect() failed."); + http_die ("internal-error", err.c_str ()); + } + + // built json payload as rules filter (agent inputs) + std::string jsonInputs = "{"; + { + std::string sep; // empty + if (!checked_type.empty()) { + jsonInputs += sep + "\"type\":\"" + checked_type + "\""; + sep = ","; + } + if (!checked_rule_class.empty()) { + jsonInputs += sep + "\"rule_class\":\"" + checked_rule_class + "\""; + sep = ","; + } + if (!checked_asset_type.empty()) { + jsonInputs += sep + "\"asset_type\":\"" + checked_asset_type + "\""; + sep = ","; + } + if (!checked_asset_sub_type.empty()) { + jsonInputs += sep + "\"asset_sub_type\":\"" + checked_asset_sub_type + "\""; + sep = ","; } - if (type != "threshold" && type != "single" && type != "pattern" && type != "all" && type != "flexible") { // unknown parameter 'type' - std::string expected = TRANSLATE_ME ("one of the following values ['threshold', 'single', 'pattern', 'all', 'flexible']"); - http_die ("request-param-bad", "type", std::string ("'").append (type).append ("'").c_str (), - expected.c_str ()); + if (!checked_in.empty()) { + jsonInputs += sep + "\"in\":\"" + checked_in + "\""; + sep = ","; } - checked_type = type; - - // rule_class - http_errors_t errors; - if (check_regex_text ("rule_class", rule_class, "^.{0,100}$", errors)) { - checked_rule_class = rule_class; - } else { - http_die_error (errors); + if (!checked_category.empty()) { + jsonInputs += sep + "\"category\":\"" + checked_category + "\""; + sep = ","; } } + jsonInputs += "}"; -// connect to malamute -MlmClientGuard client(mlm_client_new ()); -if (!client.get()) { - log_fatal ("mlm_client_new() failed."); - std::string err = TRANSLATE_ME ("mlm_client_new() failed."); - http_die ("internal-error", err.c_str ()); -} - -std::string client_name = utils::generate_mlm_client_id("web.alert_rules_list"); -log_debug ("malamute client name = '%s'.", client_name.c_str ()); - -int rv = mlm_client_connect (client, MLM_ENDPOINT, 1000, client_name.c_str ()); -if (rv == -1) { - log_fatal ("mlm_client_connect (endpoint = '%s', timeout = '%d', address = '%s') failed.", - MLM_ENDPOINT, 1000, client_name.c_str ()); - std::string err = TRANSLATE_ME ("mlm_client_connect() failed."); - http_die ("internal-error", err.c_str ()); -} - -// prepare rfc-evaluator-rules LIST message -zmsg_t *send_msg = zmsg_new (); -if (!send_msg) { - log_fatal ("zmsg_new() failed."); - std::string err = TRANSLATE_ME ("zmsg_new() failed."); - http_die ("internal-error", err.c_str ()); -} -zmsg_addstr (send_msg, "LIST"); -zmsg_addstr (send_msg, checked_type.c_str ()); -zmsg_addstr (send_msg, checked_rule_class.c_str ()); - -const char *dest = "fty-alert-engine"; -if (checked_type == "flexible") dest = "fty-alert-flexible"; - -// send it - if (mlm_client_sendto (client, dest, "rfc-evaluator-rules", NULL, 1000, &send_msg) != 0) { - log_debug ("mlm_client_sendto (address = '%s', subject = '%s', tracker = NULL, timeout = '%d') failed.", - dest, "rfc-evaluator-rules", 1000); - zmsg_destroy (&send_msg); - std::string err = TRANSLATE_ME ("mlm_client_sendto() failed."); - http_die ("internal-error", err.c_str ()); -} - -ZmsgGuard recv_msg; -zsock_t *pipe = mlm_client_msgpipe (client); -if (!pipe) { - log_fatal ("mlm_client_msgpipe() failed."); - std::string err = TRANSLATE_ME ("mlm_client_msgpipe() failed."); - http_die ("internal-error", err.c_str ()); -} -ZpollerGuard poller(zpoller_new (pipe, NULL)); -if (!poller) { - log_fatal ("zpoller_new() failed."); - std::string err = TRANSLATE_ME ("zpoller_new() failed."); - http_die ("internal-error", err.c_str ()); -} -while (true) { - zsock_t *which = static_cast(zpoller_wait(poller, 5000)); - if (which) { - recv_msg = mlm_client_recv (client); + // set address destinations related to type ('all' induce multiple requests) + std::vector destinations; + if (checked_type.empty() || checked_type == "all") { + destinations.push_back("fty-alert-engine"); + destinations.push_back("fty-alert-flexible"); } - if (!recv_msg) { - if (zpoller_expired (poller)) { - log_error ("zpoller_wait(timeout = 5000) timed out waiting for message."); - std::string err = TRANSLATE_ME ("Timed out waiting for message."); - http_die ("internal-error", err.c_str ()); - } - log_error ("mlm_client_recv() failed."); - std::string err = TRANSLATE_ME ("mlm_client_recv() failed."); - http_die ("internal-error", err.c_str ()); + else if (checked_type == "flexible") { + destinations.push_back("fty-alert-flexible"); } - if (streq (mlm_client_sender (client), dest)) - break; -} -// Got it -// Check subject -if (!streq (mlm_client_subject (client), "rfc-evaluator-rules")) { - log_error ("Unexpected reply from '%s'. Subject expected = '%s', received = '%s'.", - mlm_client_sender (client), "rfc-evaluator-rules", mlm_client_subject (client)); - std::string err = TRANSLATE_ME ("Bad message."); - http_die ("internal-error", err.c_str ()); -} - -// Check command. Can be LIST or ERROR -ZstrGuard part (zmsg_popstr (recv_msg)); -if (streq (part, "LIST")) { - part = zmsg_popstr (recv_msg); - // type received must be equal to type requested - if (checked_type.compare (part) != 0) { - log_error ("Unexpected reply from '%s'. Type expected = '%s', received = '%s' . Protocol: rfc-evaluator-rules; message: 1) LIST.", - mlm_client_sender (client), checked_type.c_str (), part.get()); - std::string err = TRANSLATE_ME ("Received type != expected one!"); - http_die ("internal-error", err.c_str ()); + else { // default (threshold, single, pattern, ...) + destinations.push_back("fty-alert-engine"); } - part = zmsg_popstr (recv_msg); - // class received must be equal to type requested - if (checked_rule_class.compare (part) != 0) { - log_error ("Unexpected reply from '%s'. rule_class expected = '%s', received = '%s' . Protocol: rfc-evaluator-rules; message: 1) LIST.", - mlm_client_sender (client), checked_rule_class.c_str (), part.get()); - std::string err = TRANSLATE_ME ("Received rule_class != expected one!"); - http_die ("internal-error", err.c_str ()); - } - part = zmsg_popstr (recv_msg); - - cxxtools::SerializationInfo si; - si.setCategory(cxxtools::SerializationInfo::Category::Array); - //variables for assetLink and ename - std::string assetLink = ""; - std::string externalName = ""; + // list of rules received from agents + cxxtools::SerializationInfo siReply; + siReply.setCategory(cxxtools::SerializationInfo::Category::Array); - log_debug ("Collecting the data:"); - - while (part) { - - const char *internalCat = "\"CAT_INTERNAL\""; - const char *automationCat = "\"CAT_AUTOMATION\""; + // request agents, append siReply + for (auto& destination : destinations) + { + const char* RULES_SUBJECT = "rfc-evaluator-rules"; + const char* COMMAND = "LIST2"; // LIST version 2 + + // Send message + { + // prepare rfc-evaluator-rules message + zmsg_t* msg = zmsg_new (); + if (!msg) { + log_fatal ("zmsg_new() failed."); + std::string err = TRANSLATE_ME ("zmsg_new() failed."); + http_die ("internal-error", err.c_str ()); + } + zmsg_addstr (msg, COMMAND); + zmsg_addstr (msg, jsonInputs.c_str ()); + + log_debug("send (%s, '%s') to %s agent (subject: %s)", + COMMAND, jsonInputs.c_str (), destination.c_str(), RULES_SUBJECT); + + // Send it + rv = mlm_client_sendto (client, destination.c_str(), RULES_SUBJECT, NULL, 1000, &msg); + zmsg_destroy (&msg); // useless + if (rv != 0) { + log_debug ("mlm_client_sendto (address = '%s', subject = '%s', tracker = NULL, timeout = '%d') failed.", + destination.c_str(), RULES_SUBJECT, 1000); + std::string err = TRANSLATE_ME ("mlm_client_sendto() failed."); + http_die ("internal-error", err.c_str ()); + } + } - //we need to serialize the section - if ((strstr(part, internalCat) == nullptr) && (strstr(part, automationCat) == nullptr)) { + // Get response + ZmsgGuard recv_msg; + { + zsock_t *pipe = mlm_client_msgpipe (client); + if (!pipe) { + log_fatal ("mlm_client_msgpipe() failed."); + std::string err = TRANSLATE_ME ("mlm_client_msgpipe() failed."); + http_die ("internal-error", err.c_str ()); + } + ZpollerGuard poller(zpoller_new (pipe, NULL)); + if (!poller) { + log_fatal ("zpoller_new() failed."); + std::string err = TRANSLATE_ME ("zpoller_new() failed."); + http_die ("internal-error", err.c_str ()); + } + while (true) { + zsock_t *which = static_cast(zpoller_wait(poller, 5000)); + if (which) { + recv_msg = mlm_client_recv (client); + } + if (!recv_msg) { + if (zpoller_expired (poller)) { + log_error ("zpoller_wait(timeout = 5000) timed out waiting for message."); + std::string err = TRANSLATE_ME ("Timed out waiting for message."); + http_die ("internal-error", err.c_str ()); + } + log_error ("mlm_client_recv() failed."); + std::string err = TRANSLATE_ME ("mlm_client_recv() failed."); + http_die ("internal-error", err.c_str ()); + } + if (streq (mlm_client_sender (client), destination.c_str())) + break; + } - std::string data(part.get()); - //log_debug ("Part: %s \n\n", data.c_str()); + // Got it. Check subject + if (!streq (mlm_client_subject (client), RULES_SUBJECT)) { + log_error ("Unexpected reply from '%s'. Subject expected = '%s', received = '%s'.", + mlm_client_sender (client), RULES_SUBJECT, mlm_client_subject (client)); + std::string err = TRANSLATE_ME ("Bad message."); + http_die ("internal-error", err.c_str ()); + } + } - //recover the ename JSON => \"ename\" : { \"value\" : \"test\", \"assetLink\" : \"datacenter-3\"} - // Be careful: the ename section is a JSON inside an JSON attribut. + // Check response + // //rule1/../ruleN + // ERROR/reason - std::size_t found = data.find("\\\"ename\\\""); + ZstrGuard part (zmsg_popstr (recv_msg)); - if(found!=std::string::npos){ + if (streq (part, COMMAND)) { // success + part = zmsg_popstr (recv_msg); // pop replicated jsonInputs (not checked) - try - { - //we have an ename section. Extract { \"value\" : \"test\", \"assetLink\" : \"datacenter-3\"} - std::size_t start = data.find("{", found); - std::size_t end = data.find("}", found); + log_debug ("Collecting the data:"); - std::string currentEname = data.substr(start, (end - start +1)); + // variables for assetLink and ename + std::string assetLink = ""; + std::string externalName = ""; - log_debug("Current ename: %s", currentEname.c_str()); + const char* CAT_INTERNAL = "\"CAT_INTERNAL\""; + const char* CAT_AUTOMATION = "\"CAT_AUTOMATION\""; - //get a proper json { "value" : "test", "assetLink" : "datacenter-3"} - std::string enameData = std::regex_replace (currentEname ,std::regex("\\\\\""), "\""); + // loop on rules (json payload) + part = zmsg_popstr (recv_msg); // mv to first rule or null + while (part) + { + // we need to serialize the section + if (!strstr(part, CAT_INTERNAL) && !strstr(part, CAT_AUTOMATION)) { - log_debug("Current in JSON format: %s", enameData.c_str()); + std::string data(part.get()); + //log_debug ("Part: %s \n\n", data.c_str()); - //unserialize the section - cxxtools::SerializationInfo siEname; + // recover the ename JSON => \"ename\" : { \"value\" : \"test\", \"assetLink\" : \"datacenter-3\"} + // Be careful: the ename section is a JSON inside a JSON attribut. - std::stringstream input(enameData); + std::size_t found = data.find("\\\"ename\\\""); + if (found != std::string::npos) { + try { + //we have an ename section. Extract { \"value\" : \"test\", \"assetLink\" : \"datacenter-3\"} + std::size_t start = data.find("{", found); + std::size_t end = data.find("}", found); - cxxtools::JsonDeserializer deserializer(input); - deserializer.deserialize(siEname); + std::string currentEname = data.substr(start, (end - start + 1)); - const cxxtools::SerializationInfo & siAssetLink = siEname.getMember("assetLink"); + log_debug("Current ename: %s", currentEname.c_str()); - //recover assetLink - std::string tmpAssetLink; - siAssetLink.getValue(tmpAssetLink); + //get a proper json { "value" : "test", "assetLink" : "datacenter-3"} + std::string enameData = std::regex_replace (currentEname ,std::regex("\\\\\""), "\""); - log_debug("Current assetLink: %s", tmpAssetLink.c_str()); + log_debug("Current in JSON format: %s", enameData.c_str()); - //recover the data of the asset if needed - if(tmpAssetLink != assetLink) - { - DBAssets::name_to_extname(tmpAssetLink, externalName); - assetLink = tmpAssetLink; - } + //unserialize the section + cxxtools::SerializationInfo siEname; + std::stringstream input(enameData); + cxxtools::JsonDeserializer deserializer(input); + deserializer.deserialize(siEname); - //create it manualy to match the good format { \"value\" : \"test\", \"assetLink\" : \"datacenter-3\"} - std::string newEname = "{ \\\"value\\\" : \\\""+externalName+"\\\", \\\"assetLink\\\" : \\\""+assetLink+"\\\"}"; + //recover assetLink + const cxxtools::SerializationInfo& siAssetLink = siEname.getMember("assetLink"); + std::string tmpAssetLink; + siAssetLink.getValue(tmpAssetLink); - log_debug("New ename: %s", newEname.c_str()); + log_debug("Current assetLink: %s", tmpAssetLink.c_str()); - //Replace the ename section by the correct one in the file - //make a copy first + //recover the data of the asset if needed + if (tmpAssetLink != assetLink) + { + DBAssets::name_to_extname(tmpAssetLink, externalName); + assetLink = tmpAssetLink; + } - std::string newData = data; + //create it manualy to match the good format { \"value\" : \"test\", \"assetLink\" : \"datacenter-3\"} + std::string newEname = "{ \\\"value\\\" : \\\""+externalName+"\\\", \\\"assetLink\\\" : \\\""+assetLink+"\\\"}"; - size_t index = 0; - while (true) - { - //Locate the section to replace. - index = newData.find(currentEname, index); + log_debug("New ename: %s", newEname.c_str()); - //end of string - if (index == std::string::npos) break; + //Replace the ename section by the correct one in the file + //make a copy first - // Make the replacement. - newData.replace(index, currentEname.size(), newEname); + std::string newData{data}; + size_t index = 0; + while (true) { + //Locate the section to replace. + index = newData.find(currentEname, index); + if (index == std::string::npos) + break; // end of string - //Advance index forward so the next iteration doesn't pick it up as well. - index += newEname.size(); - } + // Make the replacement. + newData.replace(index, currentEname.size(), newEname); - log_debug("Ename has been replaced."); + //Advance index forward so the next iteration doesn't pick it up as well. + index += newEname.size(); + } - //ensure we are able to unserialize newData - cxxtools::SerializationInfo siTmp; - std::stringstream inputTmp(newData); - cxxtools::JsonDeserializer deserializerTmp(inputTmp); - deserializerTmp.deserialize(siTmp); + log_debug("Ename has been replaced."); - data = newData; + //ensure we are able to unserialize newData + { + std::stringstream inputTmp(newData); + cxxtools::JsonDeserializer deserializerTmp(inputTmp); + cxxtools::SerializationInfo siTmp; + deserializerTmp.deserialize(siTmp); + } + data = newData; + } + catch (const std::exception& e) { + //ignore the change and continue + log_error ("Error during replacement of ename: %s", e.what()); + } + }//if(found) + + // deserialize the new data to add it in siReply + cxxtools::SerializationInfo& siRule = siReply.addMember(""); + std::stringstream input(data); + cxxtools::JsonDeserializer deserializer(input); + deserializer.deserialize(siRule); } - catch(const std::exception& e) - { - //ignore the change and continue - log_error ("Error during replacement of ename: %s", e.what()); - } + + part = zmsg_popstr (recv_msg); // next rule + }//while(part) + } + else if (streq (part, "ERROR")) { // error + part = zmsg_popstr (recv_msg); + if (!part) { + log_error ("Unexpected reply from '%s'. Expected ERROR/reason. Got ERROR/(null).", mlm_client_sender (client)); + std::string err = TRANSLATE_ME ("Bad message."); + http_die ("internal-error", err.c_str ()); } - //deserialize the new data to add it in the answer json - cxxtools::SerializationInfo & siRule = si.addMember(""); + std::stringstream quotedJsonIn; + quotedJsonIn << std::quoted(jsonInputs); - std::stringstream input(data); - cxxtools::JsonDeserializer deserializer(input); - deserializer.deserialize(siRule); + std::string reason = part.get(); + log_error ("rules list failed (inputs: '%s', reason: '%s')", jsonInputs.c_str(), reason.c_str()); + std::string err = TRANSLATE_ME ("Error while retrieving list of rules with '%s': %s.", quotedJsonIn.str().c_str(), reason.c_str()); + http_die ("internal-error", err.c_str ()); } + else { // unexpected + // Message does not conform to protocol + log_error ("Unexpected reply from '%s'. Does not conform to %s.", mlm_client_sender (client), RULES_SUBJECT); + std::string err = TRANSLATE_ME ("Bad message."); + http_die ("internal-error", err.c_str ()); + } + }//for(destination) - part = zmsg_popstr (recv_msg); - } - + // Here all rules are stored in siReply. Serialize it in reply.out(). cxxtools::JsonSerializer serializer(reply.out()); - serializer.beautify (true); - serializer.serialize(si).finish(); - - return HTTP_OK; -} - -if (streq (part, "ERROR")) { - part = zmsg_popstr (recv_msg); - if (!part) { - log_error ("Unexpected reply from '%s'. Expected ERROR/reason. Got ERROR/(null).", mlm_client_sender (client)); - std::string err = TRANSLATE_ME ("Bad message."); - http_die ("internal-error", err.c_str ()); - } - if (streq (part, "NOT_FOUND")) { - log_error ("Rule type '%s' does not exist.", checked_type.c_str ()); - std::string expected = TRANSLATE_ME ("one of the following values [ 'threshold', 'single', 'pattern', 'all' ] or empty"); - http_die ("request-param-bad", "type", std::string ("'").append (checked_type).append ("'").c_str (), - expected.c_str ()); - } - log_error ("%s", part.get()); - std::string reason = part.get(); - std::string err = TRANSLATE_ME ("Error while retrieving list of rules with type = '%s': %s.", checked_type.c_str (), reason.c_str ()); - http_die ("internal-error", err.c_str ()); -} - -// Message does not conform to protocol -log_error ("Unexptected reply from '%s'. Does not conform to rfc-evaluator-rules.", - mlm_client_sender (client)); -std::string err = TRANSLATE_ME ("Bad message."); -http_die ("internal-error", err.c_str ()); + serializer.beautify(false); + serializer.serialize(siReply).finish(); From acae2f9e23248defec7c98a9813a0b15f1e79a6a Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Thu, 12 May 2022 12:20:17 +0200 Subject: [PATCH 2/7] Update control Switch to Debian official version of libcidr, conversely to IPM 2.5.0, which was using a 42ITy homebrewed version --- packaging/debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/debian/control b/packaging/debian/control index a564408f6..6e0f632d1 100644 --- a/packaging/debian/control +++ b/packaging/debian/control @@ -33,7 +33,7 @@ Build-Depends: debhelper (>= 9), libmagic-dev, libczmq-dev (>= 3.0.2), libmlm-dev, - libcidr0-dev, + libcidr-dev, libcxxtools-dev, libtntnet-dev, libtntdb-dev, From d96e28a83ae1d04edc6d4020e386c0da6ca6eef8 Mon Sep 17 00:00:00 2001 From: "Degott, Francois Regis" Date: Fri, 29 Jul 2022 14:12:29 +0200 Subject: [PATCH 3/7] uptime.ecpp: use zpoller (non blocking) Signed-off-by: Degott, Francois Regis --- ecpp/uptime.ecpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/ecpp/uptime.ecpp b/ecpp/uptime.ecpp index b8c73d427..d20b3935a 100644 --- a/ecpp/uptime.ecpp +++ b/ecpp/uptime.ecpp @@ -99,11 +99,15 @@ bool database_ready; // Sanity check end std::stringstream json; - mlm_client_t *client; + mlm_client_t *client = NULL; + zpoller_t* poller = NULL; try { client = mlm_client_new (); if (!client) throw std::runtime_error ("Can't allocate malamute client"); + poller = zpoller_new (mlm_client_msgpipe(client), NULL); + if (!poller) + throw std::runtime_error ("Can't allocate malamute poller"); std::string client_name = utils::generate_mlm_client_id("web.uptime"); mlm_client_connect (client, MLM_ENDPOINT, 1000, client_name.c_str()); @@ -121,9 +125,13 @@ bool database_ready; DCNames[D].c_str(), NULL); int r; - char *subject, *command, *total, *offline; - // blocking!!! - r = mlm_client_recvx (client, &subject, &command, &total, &offline, NULL); + char *subject = NULL, *command = NULL, *total = NULL, *offline = NULL; + if (zpoller_wait(poller, 5000)) { + r = mlm_client_recvx (client, &subject, &command, &total, &offline, NULL); + } + else { + r = -1; + } if (r == -1) throw std::runtime_error ("Can't send the request"); zstr_free(&subject); @@ -156,9 +164,11 @@ bool database_ready; } json << "\t]\n}\n"; + zpoller_destroy (&poller); mlm_client_destroy (&client); } catch (const std::exception& e) { + zpoller_destroy (&poller); mlm_client_destroy (&client); log_error ("%s", e.what ()); http_die ("internal-error", ""); From 2b8971c8334da7b0816bcf5632b01cdd8d8149f4 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Fri, 12 Aug 2022 10:13:02 +0200 Subject: [PATCH 4/7] Per brand Product Feedback email address Signed-off-by: Arnaud Quette --- ecpp/email_feedback.ecpp | 58 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/ecpp/email_feedback.ecpp b/ecpp/email_feedback.ecpp index 32b31bc26..41dd8f54f 100644 --- a/ecpp/email_feedback.ecpp +++ b/ecpp/email_feedback.ecpp @@ -36,6 +36,46 @@ #include #include //POSIX version of basename!! #include +#include + + +static const char* BRANDING_INFO = "/etc/etn-ipm2-branding.conf"; + +static cxxtools::SerializationInfo* s_load_branding_info() +{ + cxxtools::SerializationInfo* si = new cxxtools::SerializationInfo(); + try { + std::ifstream f(BRANDING_INFO); + std::string json_string(std::istreambuf_iterator(f), {}); + std::stringstream s(json_string); + cxxtools::JsonDeserializer json(s); + json.deserialize(*si); + log_info("etn-licensing: load %s OK", BRANDING_INFO); + } catch (const std::exception& e) { + log_error("Error while parsing JSON: %s", e.what()); + } + return si; +} + +static char* s_get_branding_info(cxxtools::SerializationInfo* si, const char* key, const char* dfl) +{ + std::string value; + try { + si->getMember(key) >>= value; + } catch (const std::exception& e) { + log_info("Problem with getting %s in JSON: %s", key, e.what()); + if (dfl) { + return strdup(dfl); + } else { + return NULL; + } + } + if (value.empty() && dfl) { + log_debug("%s: can't get value. Using default '%s'", __func__, dfl); + return strdup(dfl); + } + return strdup(value.c_str()); +} // encode GET message for smtp agent static zmsg_t* @@ -129,15 +169,26 @@ UserInfo user; bool attach_system_state = false; bool participate = false; unsigned _timeout = 60; + char *feedback_email = NULL; { - std::string to = qparam.param ("to"); if (to.empty ()) { const char* c_to = getenv ("BIOS_FEEDBACK_EMAIL"); if (c_to) to = std::string {c_to}; - else - to = std::string {"EatonProductFeedback@Eaton.com"}; + else { + // Get Product feedback mail from branding info + cxxtools::SerializationInfo* bi = s_load_branding_info(); + feedback_email = s_get_branding_info(bi, "feedback_email", "EatonProductFeedback@Eaton.com"); + if (bi) { + delete bi; + bi = nullptr; + } + if (feedback_email) { + to = std::string {feedback_email}; + zstr_free (&feedback_email); + } + } } if (to.find ('@', 0) == std::string::npos) { @@ -147,6 +198,7 @@ UserInfo user; } checked_to = to; + logInfo("Using Product Feedback email address '{}'", checked_to.c_str()); std::string _reply = qparam.param ("reply"); From dbf478cde6cb32ba57e14647801e4883549d3f9a Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Fri, 12 Aug 2022 12:56:55 +0200 Subject: [PATCH 5/7] Try to fool Jenkins on Build-deps check dpkg-checkbuilddeps is done against the Jenkins builder, not the release branch target. This results in a failure, though the package is the right one! Signed-off-by: Arnaud Quette --- packaging/debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/debian/control b/packaging/debian/control index a564408f6..4a8797138 100644 --- a/packaging/debian/control +++ b/packaging/debian/control @@ -33,7 +33,7 @@ Build-Depends: debhelper (>= 9), libmagic-dev, libczmq-dev (>= 3.0.2), libmlm-dev, - libcidr0-dev, + libcidr-dev | libcidr0-dev, libcxxtools-dev, libtntnet-dev, libtntdb-dev, From de5e1e7b853ce4b2e849eac8c890c30ee5f4b1b7 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 17 Aug 2022 13:00:49 +0200 Subject: [PATCH 6/7] More fixes for Santak mailing Signed-off-by: Arnaud Quette --- ecpp/email_feedback.ecpp | 10 +++-- ecpp/email_vote.ecpp | 83 +++++++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 18 deletions(-) diff --git a/ecpp/email_feedback.ecpp b/ecpp/email_feedback.ecpp index 41dd8f54f..fb89d2984 100644 --- a/ecpp/email_feedback.ecpp +++ b/ecpp/email_feedback.ecpp @@ -1,6 +1,6 @@ <# # - # Copyright (C) 2016 - 2020 Eaton + # Copyright (C) 2016 - 2022 Eaton # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ * \file email_feedback.ecpp * \author Barbora Stepankova * \author Michal Vyskocil + * \author Arnaud Quette * \brief Sends feedback email. */ #><%pre> @@ -38,7 +39,7 @@ #include #include - +// TODO: put in fty-common-rest static const char* BRANDING_INFO = "/etc/etn-ipm2-branding.conf"; static cxxtools::SerializationInfo* s_load_branding_info() @@ -50,7 +51,7 @@ static cxxtools::SerializationInfo* s_load_branding_info() std::stringstream s(json_string); cxxtools::JsonDeserializer json(s); json.deserialize(*si); - log_info("etn-licensing: load %s OK", BRANDING_INFO); + log_info("email_feedback: load %s OK", BRANDING_INFO); } catch (const std::exception& e) { log_error("Error while parsing JSON: %s", e.what()); } @@ -76,6 +77,7 @@ static char* s_get_branding_info(cxxtools::SerializationInfo* si, const char* ke } return strdup(value.c_str()); } +// end-of TODO: put in fty-common-rest // encode GET message for smtp agent static zmsg_t* @@ -374,7 +376,7 @@ UserInfo user; log_error_audit ("Request CREATE email_feedback FAILED"); http_die ("internal-error", err.c_str ()); } - log_info_audit ("Request CREATE email_feedback SUCCESS"); + log_info_audit ("Request CREATE email_feedback SUCCESS (using %s)", checked_to.c_str()); { diff --git a/ecpp/email_vote.ecpp b/ecpp/email_vote.ecpp index 6c25af8a7..141af5f44 100644 --- a/ecpp/email_vote.ecpp +++ b/ecpp/email_vote.ecpp @@ -1,6 +1,6 @@ <# # - # Copyright (C) 2016 - 2020 Eaton + # Copyright (C) 2016 - 2022 Eaton # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ * \file email_vote.ecpp * \author Barbora Stepankova * \author Michal Vyskocil + * \author Arnaud Quette * \brief Sends voting email. */ #><%pre> @@ -36,6 +37,48 @@ #include //POSIX version of basename!! #include +#include + +// TODO: put in fty-common-rest +static const char* BRANDING_INFO = "/etc/etn-ipm2-branding.conf"; + +static cxxtools::SerializationInfo* s_load_branding_info() +{ + cxxtools::SerializationInfo* si = new cxxtools::SerializationInfo(); + try { + std::ifstream f(BRANDING_INFO); + std::string json_string(std::istreambuf_iterator(f), {}); + std::stringstream s(json_string); + cxxtools::JsonDeserializer json(s); + json.deserialize(*si); + log_info("email_feedback: load %s OK", BRANDING_INFO); + } catch (const std::exception& e) { + log_error("Error while parsing JSON: %s", e.what()); + } + return si; +} + +static char* s_get_branding_info(cxxtools::SerializationInfo* si, const char* key, const char* dfl) +{ + std::string value; + try { + si->getMember(key) >>= value; + } catch (const std::exception& e) { + log_info("Problem with getting %s in JSON: %s", key, e.what()); + if (dfl) { + return strdup(dfl); + } else { + return NULL; + } + } + if (value.empty() && dfl) { + log_debug("%s: can't get value. Using default '%s'", __func__, dfl); + return strdup(dfl); + } + return strdup(value.c_str()); +} +// end-of TODO: put in fty-common-rest + // encode GET message for smtp agent static zmsg_t* s_smtp_GET_message ( @@ -90,7 +133,7 @@ UserInfo user; {BiosProfile::Admin, "C"}, {BiosProfile::Dashboard, "C"} }; - std::string audit_msg = std::string ("Request CREATE email_test FAILED"); + std::string audit_msg = std::string ("Request CREATE email_vote FAILED"); CHECK_USER_PERMISSIONS_OR_DIE_AUDIT (PERMISSIONS, audit_msg.empty () ? nullptr : audit_msg.c_str ()); std::string checked_to; @@ -98,25 +141,37 @@ UserInfo user; std::string checked_context; bool attach_system_state = false; unsigned _timeout = 60; + char *feedback_email = NULL; { std::string to = qparam.param ("to"); if (to.empty ()) { const char* c_to = getenv ("BIOS_FEEDBACK_EMAIL"); if (c_to) to = std::string {c_to}; - else - to = std::string {"EatonProductFeedback@Eaton.com"}; + else { + // Get Product feedback mail from branding info + cxxtools::SerializationInfo* bi = s_load_branding_info(); + feedback_email = s_get_branding_info(bi, "feedback_email", "EatonProductFeedback@Eaton.com"); + if (bi) { + delete bi; + bi = nullptr; + } + if (feedback_email) { + to = std::string {feedback_email}; + zstr_free (&feedback_email); + } + } } checked_to = to; if (to.find ('@', 0) == std::string::npos) { std::string expected = TRANSLATE_ME ("valid email address"); - log_error_audit ("Request CREATE email_test FAILED"); + log_error_audit ("Request CREATE email_vote FAILED"); http_die ("request-param-bad", "to", to.c_str (), expected.c_str ()); } std::string value = qparam.param ("value"); if (value.empty ()) { - log_error_audit ("Request CREATE email_test FAILED"); + log_error_audit ("Request CREATE email_vote FAILED"); http_die ("request-param-required", "value"); } @@ -125,13 +180,13 @@ UserInfo user; } catch (const std::exception& e) { std::string expected = TRANSLATE_ME ("number 1-3"); - log_error_audit ("Request CREATE email_test FAILED"); + log_error_audit ("Request CREATE email_vote FAILED"); http_die ("request-param-bad", "value", value.c_str (), expected.c_str ()); } std::string context = qparam.param ("context"); if (context.empty ()) { - log_error_audit ("Request CREATE email_test FAILED"); + log_error_audit ("Request CREATE email_vote FAILED"); http_die ("request-param-required", "context"); } @@ -156,7 +211,7 @@ UserInfo user; if (!client) { log_fatal ("Error: mlm_pool.get () failed."); std::string err = TRANSLATE_ME ("mlm_pool.get () failed."); - log_error_audit ("Request CREATE email_test FAILED"); + log_error_audit ("Request CREATE email_vote FAILED"); http_die ("internal-error", err.c_str ()); } @@ -177,7 +232,7 @@ UserInfo user; AGENT_FTY_EMAIL_SENDMAIL_ONLY, "SENDMAIL"); std::string err = TRANSLATE_ME ("client->sendto () failed"); zmsg_destroy (&send); - log_error_audit ("Request CREATE email_test FAILED"); + log_error_audit ("Request CREATE email_vote FAILED"); http_die ("internal-error", err.c_str ()); } @@ -186,7 +241,7 @@ UserInfo user; { std::string msg = TRANSLATE_ME ("Error: client-> recv (timeout = '%d') returned NULL", _timeout); log_error ("%s", msg.c_str ()); - log_error_audit ("Request CREATE email_test FAILED"); + log_error_audit ("Request CREATE email_vote FAILED"); http_die ("internal-error", msg.c_str ()); } @@ -203,7 +258,7 @@ UserInfo user; if (streq (msg_subject, "SENDMAIL-ERR")) { status = "Failed"; - log_error_audit ("Request CREATE email_test FAILED"); + log_error_audit ("Request CREATE email_vote FAILED"); http_die ("upstream-err", err_message.get ()); } else @@ -211,10 +266,10 @@ UserInfo user; status = "Failed"; log_fatal ("Error: message recieved with invalid subject."); std::string err = TRANSLATE_ME ("client->recv () invalid message subject"); - log_error_audit ("Request CREATE email_test FAILED"); + log_error_audit ("Request CREATE email_vote FAILED"); http_die ("internal-error", err.c_str ()); } - log_info_audit ("Request CREATE email_test SUCCESS"); + log_info_audit ("Request CREATE email_vote SUCCESS (using %s)", checked_to.c_str()); { From 4b708285df11068950f4cd233c7f900ba117fbb5 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 7 Sep 2022 12:50:33 +0200 Subject: [PATCH 7/7] Revert "Merge branch 'release/IPM-2.6.0' into release/IPM-2.4.0" This reverts commit 595d3ee57315adf788c430e3eedf2f01352088db, reversing changes made to 4ceb4bf8236f4b35feed3977b7ba72d683a74430. --- ecpp/alert_rules_list.ecpp | 488 ++++++++++++++++++------------------- ecpp/average.ecpp | 10 +- ecpp/license_POST.ecpp | 297 ++++++++-------------- ecpp/netcfg.ecpp | 176 ++++++------- ecpp/scan_progress.ecpp | 185 ++++++++------ ecpp/time.ecpp | 26 +- ecpp/uptime.ecpp | 18 +- src/shared/augtool.cc | 220 ++++++----------- src/shared/augtool.h | 36 ++- 9 files changed, 623 insertions(+), 833 deletions(-) diff --git a/ecpp/alert_rules_list.ecpp b/ecpp/alert_rules_list.ecpp index 661393abd..64fd1c9b4 100644 --- a/ecpp/alert_rules_list.ecpp +++ b/ecpp/alert_rules_list.ecpp @@ -30,11 +30,9 @@ #include #include #include +#include #include #include -#include -#include -#include #include #include #include @@ -51,7 +49,7 @@ bool database_ready; // verify server is ready if (!database_ready) { log_debug ("Database is not ready yet."); - std::string err = TRANSLATE_ME ("Database is not ready yet, please try again after a while."); + std::string err = TRANSLATE_ME ("Database is not ready yet, please try again after a while."); http_die ("internal-error", err.c_str ()); } @@ -62,300 +60,284 @@ bool database_ready; }; CHECK_USER_PERMISSIONS_OR_DIE (PERMISSIONS); - // free params - std::string checked_type = qparam.param("type"); - std::string checked_rule_class = qparam.param("rule_class"); // kept for compatibility - std::string checked_asset_type = qparam.param("asset_type"); - std::string checked_asset_sub_type = qparam.param("asset_sub_type"); - std::string checked_in = qparam.param("in"); // location - std::string checked_category = qparam.param("category"); - - // connect to malamute - MlmClientGuard client(mlm_client_new ()); - if (!client.get()) { - log_fatal ("mlm_client_new() failed."); - std::string err = TRANSLATE_ME ("mlm_client_new() failed."); - http_die ("internal-error", err.c_str ()); - } - - std::string client_name = utils::generate_mlm_client_id("web.alert_rules_list"); - log_debug ("malamute client name = '%s'.", client_name.c_str ()); - - int rv = mlm_client_connect (client, MLM_ENDPOINT, 1000, client_name.c_str ()); - if (rv == -1) { - log_fatal ("mlm_client_connect (endpoint = '%s', timeout = '%d', address = '%s') failed.", - MLM_ENDPOINT, 1000, client_name.c_str ()); - std::string err = TRANSLATE_ME ("mlm_client_connect() failed."); - http_die ("internal-error", err.c_str ()); - } - - // built json payload as rules filter (agent inputs) - std::string jsonInputs = "{"; + std::string checked_type; + std::string checked_rule_class; { - std::string sep; // empty - if (!checked_type.empty()) { - jsonInputs += sep + "\"type\":\"" + checked_type + "\""; - sep = ","; - } - if (!checked_rule_class.empty()) { - jsonInputs += sep + "\"rule_class\":\"" + checked_rule_class + "\""; - sep = ","; - } - if (!checked_asset_type.empty()) { - jsonInputs += sep + "\"asset_type\":\"" + checked_asset_type + "\""; - sep = ","; - } - if (!checked_asset_sub_type.empty()) { - jsonInputs += sep + "\"asset_sub_type\":\"" + checked_asset_sub_type + "\""; - sep = ","; + std::string type = qparam.param("type"); + std::string rule_class = qparam.param("rule_class"); + + // type + if (type.empty ()) { + type = "all"; } - if (!checked_in.empty()) { - jsonInputs += sep + "\"in\":\"" + checked_in + "\""; - sep = ","; + if (type != "threshold" && type != "single" && type != "pattern" && type != "all" && type != "flexible") { // unknown parameter 'type' + std::string expected = TRANSLATE_ME ("one of the following values ['threshold', 'single', 'pattern', 'all', 'flexible']"); + http_die ("request-param-bad", "type", std::string ("'").append (type).append ("'").c_str (), + expected.c_str ()); } - if (!checked_category.empty()) { - jsonInputs += sep + "\"category\":\"" + checked_category + "\""; - sep = ","; + checked_type = type; + + // rule_class + http_errors_t errors; + if (check_regex_text ("rule_class", rule_class, "^.{0,100}$", errors)) { + checked_rule_class = rule_class; + } else { + http_die_error (errors); } } - jsonInputs += "}"; - // set address destinations related to type ('all' induce multiple requests) - std::vector destinations; - if (checked_type.empty() || checked_type == "all") { - destinations.push_back("fty-alert-engine"); - destinations.push_back("fty-alert-flexible"); +// connect to malamute +MlmClientGuard client(mlm_client_new ()); +if (!client.get()) { + log_fatal ("mlm_client_new() failed."); + std::string err = TRANSLATE_ME ("mlm_client_new() failed."); + http_die ("internal-error", err.c_str ()); +} + +std::string client_name = utils::generate_mlm_client_id("web.alert_rules_list"); +log_debug ("malamute client name = '%s'.", client_name.c_str ()); + +int rv = mlm_client_connect (client, MLM_ENDPOINT, 1000, client_name.c_str ()); +if (rv == -1) { + log_fatal ("mlm_client_connect (endpoint = '%s', timeout = '%d', address = '%s') failed.", + MLM_ENDPOINT, 1000, client_name.c_str ()); + std::string err = TRANSLATE_ME ("mlm_client_connect() failed."); + http_die ("internal-error", err.c_str ()); +} + +// prepare rfc-evaluator-rules LIST message +zmsg_t *send_msg = zmsg_new (); +if (!send_msg) { + log_fatal ("zmsg_new() failed."); + std::string err = TRANSLATE_ME ("zmsg_new() failed."); + http_die ("internal-error", err.c_str ()); +} +zmsg_addstr (send_msg, "LIST"); +zmsg_addstr (send_msg, checked_type.c_str ()); +zmsg_addstr (send_msg, checked_rule_class.c_str ()); + +const char *dest = "fty-alert-engine"; +if (checked_type == "flexible") dest = "fty-alert-flexible"; + +// send it + if (mlm_client_sendto (client, dest, "rfc-evaluator-rules", NULL, 1000, &send_msg) != 0) { + log_debug ("mlm_client_sendto (address = '%s', subject = '%s', tracker = NULL, timeout = '%d') failed.", + dest, "rfc-evaluator-rules", 1000); + zmsg_destroy (&send_msg); + std::string err = TRANSLATE_ME ("mlm_client_sendto() failed."); + http_die ("internal-error", err.c_str ()); +} + +ZmsgGuard recv_msg; +zsock_t *pipe = mlm_client_msgpipe (client); +if (!pipe) { + log_fatal ("mlm_client_msgpipe() failed."); + std::string err = TRANSLATE_ME ("mlm_client_msgpipe() failed."); + http_die ("internal-error", err.c_str ()); +} +ZpollerGuard poller(zpoller_new (pipe, NULL)); +if (!poller) { + log_fatal ("zpoller_new() failed."); + std::string err = TRANSLATE_ME ("zpoller_new() failed."); + http_die ("internal-error", err.c_str ()); +} +while (true) { + zsock_t *which = static_cast(zpoller_wait(poller, 5000)); + if (which) { + recv_msg = mlm_client_recv (client); + } + if (!recv_msg) { + if (zpoller_expired (poller)) { + log_error ("zpoller_wait(timeout = 5000) timed out waiting for message."); + std::string err = TRANSLATE_ME ("Timed out waiting for message."); + http_die ("internal-error", err.c_str ()); + } + log_error ("mlm_client_recv() failed."); + std::string err = TRANSLATE_ME ("mlm_client_recv() failed."); + http_die ("internal-error", err.c_str ()); } - else if (checked_type == "flexible") { - destinations.push_back("fty-alert-flexible"); + if (streq (mlm_client_sender (client), dest)) + break; +} +// Got it +// Check subject +if (!streq (mlm_client_subject (client), "rfc-evaluator-rules")) { + log_error ("Unexpected reply from '%s'. Subject expected = '%s', received = '%s'.", + mlm_client_sender (client), "rfc-evaluator-rules", mlm_client_subject (client)); + std::string err = TRANSLATE_ME ("Bad message."); + http_die ("internal-error", err.c_str ()); +} + +// Check command. Can be LIST or ERROR +ZstrGuard part (zmsg_popstr (recv_msg)); +if (streq (part, "LIST")) { + part = zmsg_popstr (recv_msg); + // type received must be equal to type requested + if (checked_type.compare (part) != 0) { + log_error ("Unexpected reply from '%s'. Type expected = '%s', received = '%s' . Protocol: rfc-evaluator-rules; message: 1) LIST.", + mlm_client_sender (client), checked_type.c_str (), part.get()); + std::string err = TRANSLATE_ME ("Received type != expected one!"); + http_die ("internal-error", err.c_str ()); } - else { // default (threshold, single, pattern, ...) - destinations.push_back("fty-alert-engine"); + part = zmsg_popstr (recv_msg); + // class received must be equal to type requested + if (checked_rule_class.compare (part) != 0) { + log_error ("Unexpected reply from '%s'. rule_class expected = '%s', received = '%s' . Protocol: rfc-evaluator-rules; message: 1) LIST.", + mlm_client_sender (client), checked_rule_class.c_str (), part.get()); + std::string err = TRANSLATE_ME ("Received rule_class != expected one!"); + http_die ("internal-error", err.c_str ()); } + part = zmsg_popstr (recv_msg); + + cxxtools::SerializationInfo si; + si.setCategory(cxxtools::SerializationInfo::Category::Array); - // list of rules received from agents - cxxtools::SerializationInfo siReply; - siReply.setCategory(cxxtools::SerializationInfo::Category::Array); + //variables for assetLink and ename + std::string assetLink = ""; + std::string externalName = ""; - // request agents, append siReply - for (auto& destination : destinations) - { - const char* RULES_SUBJECT = "rfc-evaluator-rules"; - const char* COMMAND = "LIST2"; // LIST version 2 - - // Send message - { - // prepare rfc-evaluator-rules message - zmsg_t* msg = zmsg_new (); - if (!msg) { - log_fatal ("zmsg_new() failed."); - std::string err = TRANSLATE_ME ("zmsg_new() failed."); - http_die ("internal-error", err.c_str ()); - } - zmsg_addstr (msg, COMMAND); - zmsg_addstr (msg, jsonInputs.c_str ()); - - log_debug("send (%s, '%s') to %s agent (subject: %s)", - COMMAND, jsonInputs.c_str (), destination.c_str(), RULES_SUBJECT); - - // Send it - rv = mlm_client_sendto (client, destination.c_str(), RULES_SUBJECT, NULL, 1000, &msg); - zmsg_destroy (&msg); // useless - if (rv != 0) { - log_debug ("mlm_client_sendto (address = '%s', subject = '%s', tracker = NULL, timeout = '%d') failed.", - destination.c_str(), RULES_SUBJECT, 1000); - std::string err = TRANSLATE_ME ("mlm_client_sendto() failed."); - http_die ("internal-error", err.c_str ()); - } - } + log_debug ("Collecting the data:"); - // Get response - ZmsgGuard recv_msg; - { - zsock_t *pipe = mlm_client_msgpipe (client); - if (!pipe) { - log_fatal ("mlm_client_msgpipe() failed."); - std::string err = TRANSLATE_ME ("mlm_client_msgpipe() failed."); - http_die ("internal-error", err.c_str ()); - } - ZpollerGuard poller(zpoller_new (pipe, NULL)); - if (!poller) { - log_fatal ("zpoller_new() failed."); - std::string err = TRANSLATE_ME ("zpoller_new() failed."); - http_die ("internal-error", err.c_str ()); - } - while (true) { - zsock_t *which = static_cast(zpoller_wait(poller, 5000)); - if (which) { - recv_msg = mlm_client_recv (client); - } - if (!recv_msg) { - if (zpoller_expired (poller)) { - log_error ("zpoller_wait(timeout = 5000) timed out waiting for message."); - std::string err = TRANSLATE_ME ("Timed out waiting for message."); - http_die ("internal-error", err.c_str ()); - } - log_error ("mlm_client_recv() failed."); - std::string err = TRANSLATE_ME ("mlm_client_recv() failed."); - http_die ("internal-error", err.c_str ()); - } - if (streq (mlm_client_sender (client), destination.c_str())) - break; - } + while (part) { + + const char *internalCat = "\"CAT_INTERNAL\""; + const char *automationCat = "\"CAT_AUTOMATION\""; - // Got it. Check subject - if (!streq (mlm_client_subject (client), RULES_SUBJECT)) { - log_error ("Unexpected reply from '%s'. Subject expected = '%s', received = '%s'.", - mlm_client_sender (client), RULES_SUBJECT, mlm_client_subject (client)); - std::string err = TRANSLATE_ME ("Bad message."); - http_die ("internal-error", err.c_str ()); - } - } + //we need to serialize the section + if ((strstr(part, internalCat) == nullptr) && (strstr(part, automationCat) == nullptr)) { - // Check response - // //rule1/../ruleN - // ERROR/reason + std::string data(part.get()); + //log_debug ("Part: %s \n\n", data.c_str()); - ZstrGuard part (zmsg_popstr (recv_msg)); + //recover the ename JSON => \"ename\" : { \"value\" : \"test\", \"assetLink\" : \"datacenter-3\"} + // Be careful: the ename section is a JSON inside an JSON attribut. - if (streq (part, COMMAND)) { // success - part = zmsg_popstr (recv_msg); // pop replicated jsonInputs (not checked) + std::size_t found = data.find("\\\"ename\\\""); - log_debug ("Collecting the data:"); + if(found!=std::string::npos){ - // variables for assetLink and ename - std::string assetLink = ""; - std::string externalName = ""; + try + { + //we have an ename section. Extract { \"value\" : \"test\", \"assetLink\" : \"datacenter-3\"} + std::size_t start = data.find("{", found); + std::size_t end = data.find("}", found); - const char* CAT_INTERNAL = "\"CAT_INTERNAL\""; - const char* CAT_AUTOMATION = "\"CAT_AUTOMATION\""; + std::string currentEname = data.substr(start, (end - start +1)); - // loop on rules (json payload) - part = zmsg_popstr (recv_msg); // mv to first rule or null - while (part) - { - // we need to serialize the section - if (!strstr(part, CAT_INTERNAL) && !strstr(part, CAT_AUTOMATION)) { + log_debug("Current ename: %s", currentEname.c_str()); - std::string data(part.get()); - //log_debug ("Part: %s \n\n", data.c_str()); + //get a proper json { "value" : "test", "assetLink" : "datacenter-3"} + std::string enameData = std::regex_replace (currentEname ,std::regex("\\\\\""), "\""); - // recover the ename JSON => \"ename\" : { \"value\" : \"test\", \"assetLink\" : \"datacenter-3\"} - // Be careful: the ename section is a JSON inside a JSON attribut. + log_debug("Current in JSON format: %s", enameData.c_str()); - std::size_t found = data.find("\\\"ename\\\""); - if (found != std::string::npos) { - try { - //we have an ename section. Extract { \"value\" : \"test\", \"assetLink\" : \"datacenter-3\"} - std::size_t start = data.find("{", found); - std::size_t end = data.find("}", found); + //unserialize the section + cxxtools::SerializationInfo siEname; - std::string currentEname = data.substr(start, (end - start + 1)); + std::stringstream input(enameData); - log_debug("Current ename: %s", currentEname.c_str()); + cxxtools::JsonDeserializer deserializer(input); + deserializer.deserialize(siEname); - //get a proper json { "value" : "test", "assetLink" : "datacenter-3"} - std::string enameData = std::regex_replace (currentEname ,std::regex("\\\\\""), "\""); + const cxxtools::SerializationInfo & siAssetLink = siEname.getMember("assetLink"); - log_debug("Current in JSON format: %s", enameData.c_str()); + //recover assetLink + std::string tmpAssetLink; + siAssetLink.getValue(tmpAssetLink); - //unserialize the section - cxxtools::SerializationInfo siEname; - std::stringstream input(enameData); - cxxtools::JsonDeserializer deserializer(input); - deserializer.deserialize(siEname); + log_debug("Current assetLink: %s", tmpAssetLink.c_str()); - //recover assetLink - const cxxtools::SerializationInfo& siAssetLink = siEname.getMember("assetLink"); - std::string tmpAssetLink; - siAssetLink.getValue(tmpAssetLink); + //recover the data of the asset if needed + if(tmpAssetLink != assetLink) + { + DBAssets::name_to_extname(tmpAssetLink, externalName); + assetLink = tmpAssetLink; + } - log_debug("Current assetLink: %s", tmpAssetLink.c_str()); + //create it manualy to match the good format { \"value\" : \"test\", \"assetLink\" : \"datacenter-3\"} + std::string newEname = "{ \\\"value\\\" : \\\""+externalName+"\\\", \\\"assetLink\\\" : \\\""+assetLink+"\\\"}"; - //recover the data of the asset if needed - if (tmpAssetLink != assetLink) - { - DBAssets::name_to_extname(tmpAssetLink, externalName); - assetLink = tmpAssetLink; - } + log_debug("New ename: %s", newEname.c_str()); - //create it manualy to match the good format { \"value\" : \"test\", \"assetLink\" : \"datacenter-3\"} - std::string newEname = "{ \\\"value\\\" : \\\""+externalName+"\\\", \\\"assetLink\\\" : \\\""+assetLink+"\\\"}"; + //Replace the ename section by the correct one in the file + //make a copy first - log_debug("New ename: %s", newEname.c_str()); + std::string newData = data; - //Replace the ename section by the correct one in the file - //make a copy first + size_t index = 0; + while (true) + { + //Locate the section to replace. + index = newData.find(currentEname, index); - std::string newData{data}; - size_t index = 0; - while (true) { - //Locate the section to replace. - index = newData.find(currentEname, index); - if (index == std::string::npos) - break; // end of string + //end of string + if (index == std::string::npos) break; - // Make the replacement. - newData.replace(index, currentEname.size(), newEname); + // Make the replacement. + newData.replace(index, currentEname.size(), newEname); - //Advance index forward so the next iteration doesn't pick it up as well. - index += newEname.size(); - } + //Advance index forward so the next iteration doesn't pick it up as well. + index += newEname.size(); + } - log_debug("Ename has been replaced."); + log_debug("Ename has been replaced."); - //ensure we are able to unserialize newData - { - std::stringstream inputTmp(newData); - cxxtools::JsonDeserializer deserializerTmp(inputTmp); - cxxtools::SerializationInfo siTmp; - deserializerTmp.deserialize(siTmp); - } + //ensure we are able to unserialize newData + cxxtools::SerializationInfo siTmp; + std::stringstream inputTmp(newData); + cxxtools::JsonDeserializer deserializerTmp(inputTmp); + deserializerTmp.deserialize(siTmp); - data = newData; - } - catch (const std::exception& e) { - //ignore the change and continue - log_error ("Error during replacement of ename: %s", e.what()); - } - }//if(found) - - // deserialize the new data to add it in siReply - cxxtools::SerializationInfo& siRule = siReply.addMember(""); - std::stringstream input(data); - cxxtools::JsonDeserializer deserializer(input); - deserializer.deserialize(siRule); - } + data = newData; - part = zmsg_popstr (recv_msg); // next rule - }//while(part) - } - else if (streq (part, "ERROR")) { // error - part = zmsg_popstr (recv_msg); - if (!part) { - log_error ("Unexpected reply from '%s'. Expected ERROR/reason. Got ERROR/(null).", mlm_client_sender (client)); - std::string err = TRANSLATE_ME ("Bad message."); - http_die ("internal-error", err.c_str ()); + } + catch(const std::exception& e) + { + //ignore the change and continue + log_error ("Error during replacement of ename: %s", e.what()); + } } - std::stringstream quotedJsonIn; - quotedJsonIn << std::quoted(jsonInputs); + //deserialize the new data to add it in the answer json + cxxtools::SerializationInfo & siRule = si.addMember(""); - std::string reason = part.get(); - log_error ("rules list failed (inputs: '%s', reason: '%s')", jsonInputs.c_str(), reason.c_str()); - std::string err = TRANSLATE_ME ("Error while retrieving list of rules with '%s': %s.", quotedJsonIn.str().c_str(), reason.c_str()); - http_die ("internal-error", err.c_str ()); + std::stringstream input(data); + cxxtools::JsonDeserializer deserializer(input); + deserializer.deserialize(siRule); } - else { // unexpected - // Message does not conform to protocol - log_error ("Unexpected reply from '%s'. Does not conform to %s.", mlm_client_sender (client), RULES_SUBJECT); - std::string err = TRANSLATE_ME ("Bad message."); - http_die ("internal-error", err.c_str ()); - } - }//for(destination) - // Here all rules are stored in siReply. Serialize it in reply.out(). + part = zmsg_popstr (recv_msg); + } + cxxtools::JsonSerializer serializer(reply.out()); - serializer.beautify(false); - serializer.serialize(siReply).finish(); + serializer.beautify (true); + serializer.serialize(si).finish(); + + return HTTP_OK; +} + +if (streq (part, "ERROR")) { + part = zmsg_popstr (recv_msg); + if (!part) { + log_error ("Unexpected reply from '%s'. Expected ERROR/reason. Got ERROR/(null).", mlm_client_sender (client)); + std::string err = TRANSLATE_ME ("Bad message."); + http_die ("internal-error", err.c_str ()); + } + if (streq (part, "NOT_FOUND")) { + log_error ("Rule type '%s' does not exist.", checked_type.c_str ()); + std::string expected = TRANSLATE_ME ("one of the following values [ 'threshold', 'single', 'pattern', 'all' ] or empty"); + http_die ("request-param-bad", "type", std::string ("'").append (checked_type).append ("'").c_str (), + expected.c_str ()); + } + log_error ("%s", part.get()); + std::string reason = part.get(); + std::string err = TRANSLATE_ME ("Error while retrieving list of rules with type = '%s': %s.", checked_type.c_str (), reason.c_str ()); + http_die ("internal-error", err.c_str ()); +} + +// Message does not conform to protocol +log_error ("Unexptected reply from '%s'. Does not conform to rfc-evaluator-rules.", + mlm_client_sender (client)); +std::string err = TRANSLATE_ME ("Bad message."); +http_die ("internal-error", err.c_str ()); diff --git a/ecpp/average.ecpp b/ecpp/average.ecpp index cfd2ebf7b..bdfc4bacb 100644 --- a/ecpp/average.ecpp +++ b/ecpp/average.ecpp @@ -259,11 +259,10 @@ bool database_ready; http_die ("internal-error", err.c_str ()); } - const int RECV_TIMEOUT_S = 60; - zmsg_t *recv_msg = client->recv (zuuid_str_canonical (uuid), RECV_TIMEOUT_S); + zmsg_t *recv_msg = client->recv (zuuid_str_canonical (uuid), 30); zuuid_destroy (&uuid); if (!recv_msg) { - log_fatal ("client->recv (timeout = '%d') returned NULL", RECV_TIMEOUT_S); + log_fatal ("client->recv (timeout = '30') returned NULL"); std::string err = TRANSLATE_ME ("client->recv () returned NULL"); http_die ("internal-error", err.c_str ()); } @@ -391,11 +390,10 @@ bool database_ready; http_die ("internal-error", err.c_str ()); } - const int RECV_TIMEOUT_S = 60; - zmsg_t *recv_msg = client->recv (zuuid_str_canonical (uuid), RECV_TIMEOUT_S); + zmsg_t *recv_msg = client->recv (zuuid_str_canonical (uuid), 30); zuuid_destroy (&uuid); if (!recv_msg) { - log_fatal ("client->recv (timeout = '%d') returned NULL", RECV_TIMEOUT_S); + log_fatal ("client->recv (timeout = '30') returned NULL"); std::string err = TRANSLATE_ME ("client->recv () returned NULL"); http_die ("internal-error", err.c_str ()); } diff --git a/ecpp/license_POST.ecpp b/ecpp/license_POST.ecpp index 45b1022ea..53eb0ecd4 100644 --- a/ecpp/license_POST.ecpp +++ b/ecpp/license_POST.ecpp @@ -31,9 +31,7 @@ #include #include #include -#include #include -#include #include "shared/utils.h" #include "cleanup.h" @@ -132,239 +130,140 @@ bool database_ready; log_info ("Starting db service, timestamp=%" PRIu64 " ..." , tme_start); std::string proc_out, proc_err; - // Just a short sleep first: hopefully systemd would already - // pick up the appearance of touch-file and begin handling FLA: sleep(10); // due to some OS circumstances, the script can sometimes block // while calling the /bin/systemctl and is eventually killed, // even though the actual services of our product have started - // long before this; so even if it failed - we would try to use - // the DB opportunistically... Still, better to know we have - // all the backend services running that are needed for EULA - // wizard and beyond! + // long before this; so even if it failed - try to use the DB... fty::Process proc("/usr/libexec/fty/start-db-services", {}, fty::Capture::Out | fty::Capture::Err); if (auto ret = proc.run(); !ret) { log_error("Starting of start-db-services have failed. Consult system logs"); log_error(ret.error().c_str()); http_die("internal-error", ret.error().c_str()); } - - // The start-db-services script ensures that all FTY/IPM2 related - // services and targets become active, including that the database - // schema gets initialized and its chain of consumers starts, - // and we are blocked until we "know" there is now someone running - // that would actually handle the backend requests for the rest of - // EULA wizard. - // So as far as we are concerned here, the script runtime can last - // several minutes (even if the CPU/IO/Time load is mostly elsewhere). - // Normally tntnet would suicide with a 10-minute request processing, - // unless a deadman timer is tickled to confirm we are stoll alive. - // This can be tuned with maxRequestTime (0 to disable the watchdog) - // or handled safely with a loop sleeping, checking that the script - // still runs, and tickling tntnet::Worker::touch() in the known-long - // processing loop. It should suffice to catch `unexpected("timeout")` - // (child still running as of last check before time limit; no other - // errors) as per fty-utils::fty/process.h wording of Process::wait(). - // the wait() would end earlier if the child process finishes: - - // Use shorter loops to avoid long wait if script is stuck flushing - // its output: - int wait_cycle = 5000; - -#if 0 - // Override to longer waits... at a risk of longer unblocking of - // stuck write() calls in child process: - wait_cycle = 3 * 60000; - - // configured in seconds, waiting in milliseconds: - unsigned int mrt = tnt::TntConfig::it().maxRequestTime; - if (mrt > 0 && mrt < (INT_MAX / 1000)) { // if enabled at all - wait_cycle = static_cast(mrt) * 666 ; // convert to msec and trim 2/3 for overheads + auto rv_svc = proc.wait(15000); + proc_out = proc.readAllStandardOutput(); + proc_err = proc.readAllStandardError(); + proc.kill(); + + uint64_t tme_finish = uint64_t(::time (NULL)); + if (rv_svc) { + log_info ("Starting db service completed, timestamp=%" PRIu64 " (duration=%" PRIu64 "), exit-code=%i ..." , + tme_finish, (tme_finish - tme_start), *rv_svc); + } else { + log_info ("Starting db service completed, timestamp=%" PRIu64 " (duration=%" PRIu64 "), exit-code=%s ..." , + tme_finish, (tme_finish - tme_start), rv_svc.error().c_str()); } -#endif - log_debug ("Starting db service will refresh tntnet deadman timer every %u msec", wait_cycle); - - for (;;) { // repeat_start_db_services: - // Read the outputs (continuously, to avoid script blocking - // on write() here; repeated below for endspiel): - while (true) { - std::string strerr = proc.readAllStandardError(100); - std::string strout = proc.readAllStandardOutput(100); - if (strerr.empty() && strout.empty()) { - break; - } - if (!strerr.empty()) proc_err += strerr; - if (!strout.empty()) proc_out += strout; - } + if (!rv_svc || *rv_svc != 0) { + log_error ("Starting of start-db-services have failed. Consult system logs"); + log_error ("%s failed with error %s.\n===== STDOUT: =====\n%s\n===== STDERR: =====\n%s\n=====", + "/usr/libexec/fty/start-db-services", rv_svc.error().c_str(), proc_out.c_str (), proc_err.c_str ()); - // May finish before "wait_cycle" elapses: - auto rv_svc = proc.wait(wait_cycle); - uint64_t tme_finish = uint64_t(::time (NULL)); - - // Success? Fail? Keep waiting?.. - if (!rv_svc && "timeout" == rv_svc.error()) { - // ...keep waiting, script is not finished: - log_info ("Starting db service takes longer than expected on this system, waiting more, timestamp=%" PRIu64 " (duration=%" PRIu64 ")" , - tme_finish, (tme_finish - tme_start)); - request.touch(); - continue; // goto repeat_start_db_services; + if (rv_svc.error() != "timeout") { + std::string err = TRANSLATE_ME ("Starting of start-db-services have failed. Consult system logs"); + log_error_audit ("Request CREATE license FAILED"); + http_die ("internal-error", err.c_str ()); } + // Negative return means killed by signal, typically by our + // subprocess timeout handling - fall through to try using DB - // ...success or fail, script is finished - log_info ("Starting db service helper script has finished, collecting results"); - - // Read all of the buffers; note that fty-utils defaults the args - // for readFromFd() behind these methods in a way that it would - // only try twice. So much for "all" in the name. Loop until dry - // (otherwise after some traffic the script effectively blocks - // when it is not milked away): - while (true) { - std::string strerr = proc.readAllStandardError(100); - std::string strout = proc.readAllStandardOutput(100); - if (strerr.empty() && strout.empty()) { - break; - } - if (!strerr.empty()) proc_err += strerr; - if (!strout.empty()) proc_out += strout; - } + { + std::string proc_out1, proc_err1; + int lastResult = 0; + for (int i = 0; i < 10; ++i) { + fty::Process::Arguments proc_cmd = {"systemctl", "list-ipm-units", "--active", "fty-db-engine"}; + // This should get our "systemctl" wrapper via PATH - // Terminate child (if any) to avoid leaks: - proc.kill(); + auto rv = fty::Process::run("sudo", proc_cmd, proc_out1, proc_err1); - if (rv_svc) { - log_info ("Starting db service completed, timestamp=%" PRIu64 " (duration=%" PRIu64 "), exit-code=%i ..." , - tme_finish, (tme_finish - tme_start), *rv_svc); - } else { - log_info ("Starting db service completed, timestamp=%" PRIu64 " (duration=%" PRIu64 "), exit-code=%s ..." , - tme_finish, (tme_finish - tme_start), rv_svc.error().c_str()); - } + if (!rv) { + log_error(rv.error().c_str()); + log_error_audit ("Request CREATE license FAILED"); + http_die ("internal-error", TRANSLATE_ME ("Database software service is not running").c_str()); + } - // non-zero exit code or proc class exception: - if (!rv_svc || *rv_svc != 0) { - std::string rv_svc_err; - if (rv_svc) { - rv_svc_err = "code "; - rv_svc_err += std::to_string(*rv_svc); - } else { - rv_svc_err = rv_svc.error(); + lastResult = *rv; + if (*rv == 0) { + break; + } + sleep(5); } - log_error ("Starting of start-db-services have failed. Consult system logs"); - log_error ("%s failed with error %s.\n===== STDOUT: =====\n%s\n===== STDERR: =====\n%s\n=====", - "/usr/libexec/fty/start-db-services", - rv_svc_err.c_str(), proc_out.c_str(), proc_err.c_str()); - - if (!rv_svc && rv_svc.error() != "timeout") { - std::string err = TRANSLATE_ME ("Starting of start-db-services have failed. Consult system logs"); + if (lastResult != 0) { + std::string message; + message = "`sudo systemctl list-ipm-units --active fty-db-engine`" + " failed (service is not currently active). Return value = '" + + std::to_string (lastResult) + "', stderr = '" + proc_err1 + "'." ; + log_error ("%s", message.c_str ()); + std::string err = TRANSLATE_ME ("Database software service is not running"); log_error_audit ("Request CREATE license FAILED"); http_die ("internal-error", err.c_str ()); + } else { + // Not quite an error, but we want this message at the same logging level + log_error ("NOTE: Although start-db-services script was killed, the fty-db-engine service is okay"); } - // Negative return means killed by signal, typically by our - // subprocess timeout handling - fall through to try using DB - - { // scoping for error-handling: inspect fty-db-engine (mysqld etc) service unit status: - std::string proc_out1, proc_err1; - int lastResult = 0; - for (int i = 0; i < 10; ++i) { - fty::Process::Arguments proc_cmd = {"systemctl", "list-ipm-units", "--active", "fty-db-engine"}; - // This should get our "systemctl" wrapper via PATH - - auto rv = fty::Process::run("sudo", proc_cmd, proc_out1, proc_err1); - - if (!rv) { - log_error(rv.error().c_str()); - log_error_audit ("Request CREATE license FAILED"); - http_die ("internal-error", TRANSLATE_ME ("Database software service is not running").c_str()); - } - - lastResult = *rv; - if (*rv == 0) { - break; - } - sleep(5); - } - - if (lastResult != 0) { - std::string message; - message = "`sudo systemctl list-ipm-units --active fty-db-engine`" - " failed (service is not currently active). Return value = '" - + std::to_string (lastResult) + "', stderr = '" + proc_err1 + "'." ; - log_error ("%s", message.c_str ()); - std::string err = TRANSLATE_ME ("Database software service is not running"); - log_error_audit ("Request CREATE license FAILED"); - http_die ("internal-error", err.c_str ()); - } else { - // Not quite an error, but we want this message at the same logging level - log_error ("NOTE: Although start-db-services script was killed, the fty-db-engine service is okay"); - } - } - + } - { // scoping for error-handling: inspect fty-db-init (schema import) service unit status: - std::string proc_out1, proc_err1; - int lastResult = 0; - // try to increase timeout.. - for (int i = 0; i < 50; ++i) { - fty::Process::Arguments proc_cmd {"systemctl", "list-ipm-units", "--active", "fty-db-init"}; - // This should get our "systemctl" wrapper via PATH + { + std::string proc_out1, proc_err1; + int lastResult = 0; - auto rv = fty::Process::run("sudo", proc_cmd, proc_out1, proc_err1); + // try to increase timeout.. + for (int i = 0; i < 50; ++i) { + fty::Process::Arguments proc_cmd {"systemctl", "list-ipm-units", "--active", "fty-db-init"}; + // This should get our "systemctl" wrapper via PATH - if (!rv) { - log_error(rv.error().c_str()); - log_error_audit("--! %s", rv.error().c_str()); - log_error_audit ("Request CREATE license FAILED"); - http_die ("internal-error", TRANSLATE_ME ("Database schema was not installed or verified successfully").c_str()); - } + auto rv = fty::Process::run("sudo", proc_cmd, proc_out1, proc_err1); - lastResult = *rv; - if (*rv == 0) { - log_error_audit("is ok"); - break; - } - log_error_audit("next to check"); - sleep(5); + if (!rv) { + log_error(rv.error().c_str()); + log_error_audit("--! %s", rv.error().c_str()); + log_error_audit ("Request CREATE license FAILED"); + http_die ("internal-error", TRANSLATE_ME ("Database schema was not installed or verified successfully").c_str()); } - if (lastResult != 0) { - std::string message = "`sudo systemctl list-ipm-units --active fty-db-init`" - " failed (service is not currently active). Return value = '" - + std::to_string (lastResult) + "', stderr = '" + proc_err1 + "'." ; - log_error ("%s", message.c_str()); - log_error_audit("%s", message.c_str()); - std::string err = TRANSLATE_ME ("Database schema was not installed or verified successfully"); - log_error_audit ("Request CREATE license FAILED"); - http_die ("internal-error", err.c_str ()); - } else { - // Not quite an error, but we want this message at the same logging level - log_error ("NOTE: Although start-db-services script was killed, the fty-db-init service is okay"); + lastResult = *rv; + if (*rv == 0) { + log_error_audit("is ok"); + break; } + log_error_audit("next to check"); + sleep(5); } - } // handle if script failed - - // once done, check environment files for accessing the database - uint64_t tme_started = uint64_t(::time (NULL)); - log_info ("db services were started by timestamp=%" PRIu64 ", re-reading password ...", tme_started); - int rv_dbcred = DBConn::dbreadcredentials (); - if (!rv_dbcred) { - std::string message; - if (*rv_svc != 0) { - message = TRANSLATE_ME ("Database password file is missing AND failed to start start-db-services. Consult system logs"); + + if (lastResult != 0) { + std::string message = "`sudo systemctl list-ipm-units --active fty-db-init`" + " failed (service is not currently active). Return value = '" + + std::to_string (lastResult) + "', stderr = '" + proc_err1 + "'." ; + log_error ("%s", message.c_str()); + log_error_audit("%s", message.c_str()); + std::string err = TRANSLATE_ME ("Database schema was not installed or verified successfully"); + log_error_audit ("Request CREATE license FAILED"); + http_die ("internal-error", err.c_str ()); } else { - message = TRANSLATE_ME ("Database password file is missing"); + // Not quite an error, but we want this message at the same logging level + log_error ("NOTE: Although start-db-services script was killed, the fty-db-init service is okay"); } - log_error ("%s", message.c_str ()); - log_error_audit ("Request CREATE license FAILED"); - http_die ("internal-error", message.c_str ()); } + } - // we get here when script completed (well or not), - // so finish the infinite loop: - break; - } // for deadman timer around start-db-services - + // once done, check environment files for accessing the database + uint64_t tme_started = uint64_t(::time (NULL)); + log_info ("db services were started by timestamp=%" PRIu64 ", re-reading password ...", tme_started); + int rv_dbcred = DBConn::dbreadcredentials (); + if (!rv_dbcred) { + std::string message; + if (*rv_svc != 0) { + message = TRANSLATE_ME ("Database password file is missing AND failed to start start-db-services. Consult system logs"); + } else { + message = TRANSLATE_ME ("Database password file is missing"); + } + log_error ("%s", message.c_str ()); + log_error_audit ("Request CREATE license FAILED"); + http_die ("internal-error", message.c_str ()); + } } else { // enforce reload of credentials, e.g. if timely service startup // had failed previously but the system has caught up by now diff --git a/ecpp/netcfg.ecpp b/ecpp/netcfg.ecpp index 5b9db7de4..23c6de98f 100644 --- a/ecpp/netcfg.ecpp +++ b/ecpp/netcfg.ecpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -48,55 +47,37 @@ using namespace shared; UserInfo user; <%cpp> -// check user permissions -static const std::map PERMISSIONS = { - {BiosProfile::Admin, "RU"} - }; - -std::string audit_msg; -if (request.getMethod () == "PUT") - audit_msg = std::string ("Request CREATE OR UPDATE netcfg FAILED"); - -CHECK_USER_PERMISSIONS_OR_DIE_AUDIT (PERMISSIONS, audit_msg.empty () ? nullptr : audit_msg.c_str ()); - -const bool sudoer = (request.getMethod () != "GET"); // augtool require sudo privileges? -augtool* augeas = nullptr; -int counter = 0; - -while(augeas == nullptr) -{ - augeas = augtool::get_instance (sudoer); - counter++; - if(counter >= 10) - { - break; - } -} - + // check user permissions + static const std::map PERMISSIONS = { + {BiosProfile::Admin, "RU"} + }; + std::string audit_msg; + if (request.getMethod () == "PUT") + audit_msg = std::string ("Request CREATE OR UPDATE netcfg FAILED"); + CHECK_USER_PERMISSIONS_OR_DIE_AUDIT (PERMISSIONS, audit_msg.empty () ? nullptr : audit_msg.c_str ()); + +augtool* augeas = augtool::get_instance (); if (!augeas) { - std::string err = TRANSLATE_ME ("Cannot communicate with augtool. Is it installed or properly configured?"); + std::string err = TRANSLATE_ME ("Cannot communicate with augtool. Is it installed or properly configured?"); if (request.getMethod () == "PUT") { log_error_audit ("Request CREATE OR UPDATE netcfg FAILED"); } http_die ("internal-error", err.c_str ()); } - std::string nil; // Make sure we have data that works - nil = augeas->get_cmd_out ("ls /augeas/files/etc/network/interfaces/error"); if (!nil.empty ()) { - std::string err = TRANSLATE_ME ("Syntax error in /etc/network/interfaces config file"); + std::string err = TRANSLATE_ME ("Syntax error in /etc/network/interfaces config file"); if (request.getMethod () == "PUT") { log_error_audit ("Request CREATE OR UPDATE netcfg FAILED"); } http_die ("internal-error", err.c_str ()); } - nil = augeas->get_cmd_out ("ls /augeas/files/etc/resolv.conf/error"); if (!nil.empty ()) { - std::string err = TRANSLATE_ME ("Syntax error in /etc/resolv.conf config file"); + std::string err = TRANSLATE_ME ("Syntax error in /etc/resolv.conf config file"); if (request.getMethod () == "PUT") { log_error_audit ("Request CREATE OR UPDATE netcfg FAILED"); } @@ -104,42 +85,37 @@ if (!nil.empty ()) { } // Are we asked to just list possible configurations? - -static const cxxtools::Regex rex_ncfgs ("^.*/netcfgs$"); -if (rex_ncfgs.match (request.getUrl ())) { - - std::vector all_interfaces; - { - std::string all_interfaces_str = augeas->get_cmd_out ("match /files/etc/network/interfaces/iface[*]", - true, ",", - [](const std::string iface) -> bool { - return iface == "lo"; // ignore "lo" if - } - ); - if (all_interfaces_str.empty ()) { - std::string err = TRANSLATE_ME ("No configurable interfaces found"); - if (request.getMethod () == "PUT") { - log_error_audit ("Request CREATE OR UPDATE netcfg FAILED"); - } - http_die ("internal-error", err.c_str ()); +static cxxtools::Regex rex_ncfg ("^.*/netcfgs$"); +if (rex_ncfg.match (request.getUrl ())) { + std::string all_interfaces_str = augeas->get_cmd_out ("match /files/etc/network/interfaces/iface[*]", + true, ",", + [](const std::string iface) -> bool { + return iface == "lo"; + } + ); + if (all_interfaces_str.empty ()) { + std::string err = TRANSLATE_ME ("No configurable interfaces found"); + if (request.getMethod () == "PUT") { + log_error_audit ("Request CREATE OR UPDATE netcfg FAILED"); } - - cxxtools::split (",", all_interfaces_str, std::back_inserter (all_interfaces)); + http_die ("internal-error", err.c_str ()); } std::set active_interfaces = get_ifaces (); + std::vector all_interfaces; + cxxtools::split (",", all_interfaces_str, std::back_inserter (all_interfaces)); std::string out; for (std::string iface : all_interfaces) { if (active_interfaces.find (iface) != active_interfaces.end ()) { - out += (out.empty() ? "" : ",") + std::string{"\"" + iface + "\""}; + out += "\"" + iface + "\","; } } - + // remove trailing comma + out.pop_back (); { "netcfgs": [ <$$ out $> ] } <%cpp> - if (request.getMethod () == "PUT") { log_info_audit ("Request CREATE OR UPDATE netcfg SUCCESS"); } @@ -167,7 +143,7 @@ std::string checked_iface; // Where is the tree reflecting the interface? std::string address = augeas->get_cmd_out ( "match /files/etc/network/interfaces/iface[*] " + checked_iface, - false); + false); if (address.empty ()) { // Not perfect, but bad enough to create another message? if (request.getMethod () == "PUT") { @@ -184,10 +160,12 @@ AUG_GET ("method", method); // Modifications requested? if (request.getMethod () == "PUT") { + std::stringstream input (request.getBody (), std::ios_base::in); cxxtools::SerializationInfo si; + cxxtools::SerializationInfo rsi; + cxxtools::JsonDeserializer deserializer (input); + std::string val; try { - std::stringstream input (request.getBody (), std::ios_base::in); - cxxtools::JsonDeserializer deserializer (input); deserializer.deserialize (si); } catch (const std::exception& e) { std::string msg = TRANSLATE_ME ("Expected valid json document: %s", JSONIFY (e.what ()).c_str ()); @@ -195,7 +173,6 @@ if (request.getMethod () == "PUT") { http_die ("bad-request-document", msg.c_str ()); } - cxxtools::SerializationInfo rsi; try { rsi = si.getMember (checked_iface); } catch (const std::exception& e) { @@ -204,13 +181,16 @@ if (request.getMethod () == "PUT") { http_die ("bad-request-document", msg.c_str ()); } + // Gets configuration from json and sets it in config while verifying it matches regexp -#define AUG_SET(NAME, CHECK) \ +#define AUG_SET(NAME, CHECK) \ if (it.name () == NAME) { \ it.getValue (val); \ if (! CHECK ) { \ std::string msg = TRANSLATE_ME ("Wrong value for '%s' setting or setting not expected for method %s.", NAME, method.c_str ()); \ - log_error_audit ("Request CREATE OR UPDATE netcfg FAILED"); \ + if (request.getMethod () == "PUT") { \ + log_error_audit ("Request CREATE OR UPDATE netcfg FAILED"); \ + } \ http_die ("parameter-conflict", msg.c_str ()); \ } \ augeas->run_cmd ("set " + address + "/" NAME " " + val); \ @@ -219,13 +199,10 @@ if (request.getMethod () == "PUT") { // All the things we need to set for (auto it : rsi) { - static const cxxtools::Regex rex_method ("^(dhcp|static|none|manual)$"); - std::string val; //ZZZ set & used internally by AUG_SET bool handled = false; - - AUG_SET ("method", rex_method.match (method)); - AUG_GET ("method", method); //ZZZ how many times do we augtool::get 'method' (useless/timeless calls)!? - + static cxxtools::Regex rex_mtd ("^(dhcp|static|none|manual)$"); + AUG_SET ("method", rex_mtd.match (method)); + AUG_GET ("method", method); AUG_SET ("address", (CIDRAddress (val).valid () && method == "static")); AUG_SET ("netmask", (CIDRAddress (val).isNetmask () && method == "static")); @@ -240,8 +217,7 @@ if (request.getMethod () == "PUT") { it.getValue (val); if (val.empty ()){ augeas->run_cmd ("rm " + address + "/gateway"); - } - else { + }else{ if (! CIDRAddress (val).valid () ) { std::string msg = TRANSLATE_ME ("Wrong value for 'gateway' setting or setting not expected for method %s.", method.c_str ()); log_error_audit ("Request CREATE OR UPDATE netcfg FAILED"); @@ -255,100 +231,92 @@ if (request.getMethod () == "PUT") { //DNS is array, handle differently if (it.name () == "nameservers") { if (it.category () != cxxtools::SerializationInfo::Category::Array) { - std::string err = TRANSLATE_ME ("Wrong value for DNS setting - array expected"); + std::string err = TRANSLATE_ME ("Wrong value for DNS setting - array expected"); log_error_audit ("Request CREATE OR UPDATE netcfg FAILED"); http_die ("bad-request-document", err.c_str ()); } - // get all interface from /etc/network/interfaces std::vector interfaces; std::string in = augeas->get_cmd_out_raw ("match /files/etc/network/interfaces/iface[*]"); cxxtools::split ("\n", in, std::back_inserter (interfaces)); + std::string ns_list = ""; // Build the list of nameservers - std::string ns_list; for (auto i : it) { i.getValue (val); if (!CIDRAddress (val).valid ()) { - std::string err = TRANSLATE_ME ("Wrong value for DNS setting - array of IPs expected") ; + std::string err = TRANSLATE_ME ("Wrong value for DNS setting - array of IPs expected") ; log_error_audit ("Request CREATE OR UPDATE netcfg FAILED"); http_die ("bad-request-document", err.c_str ()); } - ns_list += (ns_list.empty() ? "" : " ") + val; + if (ns_list.length ()>0){ + ns_list += " " + val; + }else{ + ns_list = val; + } } log_debug ("Nameservers list = '%s'", ns_list.c_str ()); - // Now set nameservers for each interfaces for (auto iface : interfaces) { log_debug ("Processing interface %s", iface.c_str ()); //avoid lo interface and some augtool cli weird iface if (iface.find ("lo")==std::string::npos && iface.find ("*")==std::string::npos - && iface.find ("]")!=std::string::npos) - { + && iface.find ("]")!=std::string::npos) { std::size_t found = iface.find ("]"); - std::string sub_iface = iface.substr (0, found + 1); - char *path = NULL; - if (ns_list.empty ()) { + std::string sub_iface=iface.substr (0,found+1); + char *path; + if (ns_list.empty ()){ + log_debug ("rm %s/dns-nameservers",sub_iface.c_str ()); path = zsys_sprintf ("rm %s/dns-nameservers", sub_iface.c_str ()); - } - else { + }else{ + log_debug ("set %s/dns-nameservers '%s'", + sub_iface.c_str (), ns_list.c_str ()); path = zsys_sprintf ("set %s/dns-nameservers '%s'", sub_iface.c_str (), ns_list.c_str ()); - } - log_debug("%s", path); - augeas->run_cmd (std::string (path)); - zstr_free(&path); + } + augeas->run_cmd (std::string (path)); } } handled = true; } - if (!handled) { std::string msg = TRANSLATE_ME ("Invalid option '%s'", it.name ().c_str ()); log_error_audit ("Request CREATE OR UPDATE netcfg FAILED"); http_die ("bad-request-document", msg.c_str ()); } - } //for -#undef AUG_SET + } // Commit everything - augeas->save (); + augeas->save (); // Make sure we have data that works - - nil = augeas->get_cmd_out ("ls /augeas/files/etc/network/interfaces/error"); + nil = augeas->get_cmd_out ("ls /augeas/files/etc/network/interfaces/error"); if (!nil.empty ()) { - std::string err = TRANSLATE_ME ("Syntax error in /etc/network/interfaces config file"); + std::string err = TRANSLATE_ME ("Syntax error in /etc/network/interfaces config file"); log_error_audit ("Request CREATE OR UPDATE netcfg FAILED"); http_die ("internal-error", err.c_str ()); } - - nil = augeas->get_cmd_out ("ls /augeas/files/etc/resolv.conf/error"); + nil = augeas->get_cmd_out ("ls /augeas/files/etc/resolv.conf/error"); if (!nil.empty ()) { - std::string err = TRANSLATE_ME ("Syntax error in /etc/resolv.conf config file"); + std::string err = TRANSLATE_ME ("Syntax error in /etc/resolv.conf config file"); log_error_audit ("Request CREATE OR UPDATE netcfg FAILED"); http_die ("internal-error", err.c_str ()); } log_info_audit ("Request CREATE OR UPDATE netcfg SUCCESS"); -} //"PUT" +} -std::string ip, netmask, gateway; +std::string ip, netmask, gateway, dns; AUG_GET ("method", method); AUG_GET ("address", ip); AUG_GET ("netmask", netmask); AUG_GET ("gateway", gateway); -#undef AUG_GET - -std::string dns; if (request.getMethod() == "PUT") { - std::string dnsRawData = augeas->get_cmd_out ( - "match /files/etc/network/interfaces/iface[*]/dns-nameservers", - true, " "); + std::string dnsRawData = augeas->get_cmd_out ("match /files/etc/network/interfaces/iface[*]/dns-nameservers", true, " "); std::set dnsSet; cxxtools::split (" ", dnsRawData, std::inserter (dnsSet, dnsSet.end())); diff --git a/ecpp/scan_progress.ecpp b/ecpp/scan_progress.ecpp index 35027c16c..de0f4dd7c 100644 --- a/ecpp/scan_progress.ecpp +++ b/ecpp/scan_progress.ecpp @@ -29,97 +29,113 @@ #include #include -// request progress to the fty-discovery service -// returns 0 if ok, else <0 -// if ok, output is set as the response (json payload) -static int get_scan_progress (std::string& output) +// progress request +static zmsg_t * +req_progress (zuuid_t *uuid) +{ + zmsg_t *msg = zmsg_new (); + zmsg_addstr (msg, "PROGRESS"); + zmsg_addstr (msg, zuuid_str_canonical (uuid)); + return msg; +} + +int +get_scan_progress (std::string &output) { output.clear(); // connect to mlm client auto client = mlm_pool.get(); - if (!client) { - log_error ("scan_progress: mlm_pool.get () failed."); + if (!client) + { + log_fatal ("scan_progress: mlm_pool.get () failed."); return -1; } - // send request zuuid_t *uuid = zuuid_new (); - zmsg_t *request = zmsg_new (); - zmsg_addstr (request, "PROGRESS"); - zmsg_addstr (request, zuuid_str_canonical (uuid)); + zmsg_t *request = req_progress (uuid); int rv = client->sendto ("fty-discovery", "progress", 1000, &request); - zmsg_destroy (&request); - if (rv == -1) { + if (rv == -1) + { zuuid_destroy (&uuid); - log_error ("scan_progress: client->sendto (address = '%s') failed.", "fty-discovery"); + log_fatal ("scan_progress: client->sendto (address = '%s') failed.", "fty-discovery"); return -2; } ZmsgGuard resp(client->recv (zuuid_str_canonical (uuid), 20)); zuuid_destroy (&uuid); - - if (!resp) { - log_error ("scan_progress: client->recv (timeout = '20') returned NULL"); + if (!resp) + { + log_fatal ("info: client->recv (timeout = '20') returned NULL"); return -3; } ZstrGuard result (zmsg_popstr (resp)); - if (!result) { - log_error("scan_progress: received unexpected NULL response"); - return -4; + if (result) { + if (streq (result, "OK")) { + output.append ("{"); + + ZstrGuard status (zmsg_popstr (resp)); + if(!status) return -5; + output.append ("\"status\" : \""); + output.append(status); + if(streq(status, "-1")) { + output.append("\"}"); + return 0; + } + output.append("\","); + + ZstrGuard progress (zmsg_popstr (resp)); + if (!progress) return -5; + output.append ("\"progress\" : \""); + output.append (progress); + output.append ("%\", "); + + ZstrGuard discovered (zmsg_popstr (resp)); + if(!discovered) return -5; + output.append ("\"discovered\" : \""); + output.append(discovered); + output.append("\","); + + ZstrGuard discovered_ups (zmsg_popstr (resp)); + if(!discovered_ups) return -5; + output.append ("\"ups-discovered\" : \""); + output.append(discovered_ups); + output.append("\","); + + ZstrGuard discovered_epdu (zmsg_popstr (resp)); + if(!discovered_epdu) return -5; + output.append ("\"epdu-discovered\" : \""); + output.append(discovered_epdu); + output.append("\","); + + ZstrGuard discovered_sts (zmsg_popstr (resp)); + if(!discovered_sts) return -5; + output.append ("\"sts-discovered\" : \""); + output.append(discovered_sts); + output.append("\","); + + ZstrGuard discovered_sensors (zmsg_popstr (resp)); + if(!discovered_sensors) return -5; + output.append ("\"sensors-discovered\" : \""); + output.append(discovered_sensors); + output.append("\""); + + output.append("}"); + } + else if (streq (result, "ERROR")) { + return -4; + } + else { + return -5; + } } - if (streq (result, "ERROR")) { - log_error("scan_progress: received ERROR response"); + else return -5; - } - if (!streq (result, "OK")) { - log_error("scan_progress: received unexpected response"); - return -6; - } - - // here result == "OK" - - ZstrGuard status (zmsg_popstr (resp)); - if (!status) { - log_error("scan_progress: status is NULL"); - return -7; - } - - output.append("{"); - - output.append("\"status\": \"").append(status).append("\""); - - if (!streq(status, "-1")) { - // scan details - ZstrGuard progress (zmsg_popstr (resp)); - ZstrGuard discovered (zmsg_popstr (resp)); - ZstrGuard discovered_ups (zmsg_popstr (resp)); - ZstrGuard discovered_epdu (zmsg_popstr (resp)); - ZstrGuard discovered_sts (zmsg_popstr (resp)); - ZstrGuard discovered_sensors (zmsg_popstr (resp)); - - if (!progress) { log_error("scan_progress: progress is NULL"); return -7; } - if (!discovered) { log_error("scan_progress: discovered is NULL"); return -7; } - if (!discovered_ups) { log_error("scan_progress: discovered_ups is NULL"); return -7; } - if (!discovered_epdu) { log_error("scan_progress: discovered_epdu is NULL"); return -7; } - if (!discovered_sts) { log_error("scan_progress: discovered_sts is NULL"); return -7; } - if (!discovered_sensors) { log_error("scan_progress: discovered_sensors is NULL"); return -7; } - - output.append(", \"progress\": \"").append(progress).append("%\""); - output.append(", \"discovered\": \"").append(discovered).append("\""); - output.append(", \"ups-discovered\": \"").append(discovered_ups).append("\""); - output.append(", \"epdu-discovered\": \"").append(discovered_epdu).append("\""); - output.append(", \"sts-discovered\": \"").append(discovered_sts).append("\""); - output.append(", \"sensors-discovered\": \"").append(discovered_sensors).append("\""); - } - - output.append("}"); return 0; } - <%request scope="global"> UserInfo user; @@ -127,19 +143,38 @@ UserInfo user; <%cpp> // permission check static const std::map PERMISSIONS = { - {BiosProfile::Anonymous, "R"}, - {BiosProfile::Dashboard, "R"}, - {BiosProfile::Admin, "R"} - }; + {BiosProfile::Anonymous, "R"}, + {BiosProfile::Dashboard, "R"}, + {BiosProfile::Admin, "R"} + }; CHECK_USER_PERMISSIONS_OR_DIE (PERMISSIONS); std::string output; int rv = get_scan_progress (output); - if (rv != 0) { - log_error("get_scan_progress() failed (rv: %d)", rv); - std::string err = TRANSLATE_ME("Automatic discovery service not available"); - http_die ("internal-error", err.c_str()); + if (rv == -1) { + log_error("mlmpool.get () failed."); + http_die ("internal-error", TRANSLATE_ME("Automatic discovery service not available")); } - - reply.out () << output; + else + if (rv == -2) { + log_error("client->sendto () failed."); + http_die ("internal-error", TRANSLATE_ME("Automatic discovery service not available")); + } + else + if (rv == -3) { + log_error("client->recv () failed."); + http_die ("internal-error", TRANSLATE_ME("Automatic discovery service not available")); + } + else + if (rv == -4) { + log_error("fty-discovery returned error."); + http_die ("internal-error", TRANSLATE_ME("Automatic discovery service not available")); + } + else + if (rv == -5) { + log_error("fty-discovery returned malformed or unexpected message."); + http_die ("internal-error", TRANSLATE_ME("Automatic discovery service not available")); + } + else +<$$ output $> diff --git a/ecpp/time.ecpp b/ecpp/time.ecpp index 3a59772b7..3f98c8264 100644 --- a/ecpp/time.ecpp +++ b/ecpp/time.ecpp @@ -117,22 +117,7 @@ UserInfo user; } // input arguments sanitization end - const bool sudoer = (request.getMethod () != "GET"); // augtool require sudo privileges? - augtool* augeas = nullptr; - int counter = 0; - - while(augeas == nullptr) - { - augeas = augtool::get_instance (sudoer); - counter++; - if(counter >= 10) - { - break; - } - } - - - + augtool* augeas = augtool::get_instance (); if (!augeas) { std::string err = TRANSLATE_ME ("Cannot communicate with augtool. Is it installed or properly configured?"); if (request.getMethod () == "POST") @@ -210,21 +195,18 @@ UserInfo user; } } - char time_out[32]; - memset(time_out, 0, sizeof(time_out)); - if (calendar_to_datetime (time (NULL), time_out, sizeof(time_out)) == -1) { + char buff[32]; + if (calendar_to_datetime (time (NULL), buff, 32) == -1) { std::string err = TRANSLATE_ME ("calendar_to_datetime () failed."); if (request.getMethod () == "POST") log_error_audit ("Request CREATE time %s %s FAILED", checked_time.c_str (), checked_ntp.c_str ()); http_die ("internal-error", err.c_str ()); } - std::string ntp_out = augeas->get_cmd_out ("get /files/etc/ntp.conf/server[1]"); - if (request.getMethod () == "POST") log_info_audit ("Request CREATE time %s %s SUCCESS", checked_time.c_str (), checked_ntp.c_str ()); { - <$$ utils::json::jsonify ("time", time_out) $>, + <$$ utils::json::jsonify ("time", buff) $>, <$$ utils::json::jsonify ("ntp", ntp_out) $> } diff --git a/ecpp/uptime.ecpp b/ecpp/uptime.ecpp index d20b3935a..b8c73d427 100644 --- a/ecpp/uptime.ecpp +++ b/ecpp/uptime.ecpp @@ -99,15 +99,11 @@ bool database_ready; // Sanity check end std::stringstream json; - mlm_client_t *client = NULL; - zpoller_t* poller = NULL; + mlm_client_t *client; try { client = mlm_client_new (); if (!client) throw std::runtime_error ("Can't allocate malamute client"); - poller = zpoller_new (mlm_client_msgpipe(client), NULL); - if (!poller) - throw std::runtime_error ("Can't allocate malamute poller"); std::string client_name = utils::generate_mlm_client_id("web.uptime"); mlm_client_connect (client, MLM_ENDPOINT, 1000, client_name.c_str()); @@ -125,13 +121,9 @@ bool database_ready; DCNames[D].c_str(), NULL); int r; - char *subject = NULL, *command = NULL, *total = NULL, *offline = NULL; - if (zpoller_wait(poller, 5000)) { - r = mlm_client_recvx (client, &subject, &command, &total, &offline, NULL); - } - else { - r = -1; - } + char *subject, *command, *total, *offline; + // blocking!!! + r = mlm_client_recvx (client, &subject, &command, &total, &offline, NULL); if (r == -1) throw std::runtime_error ("Can't send the request"); zstr_free(&subject); @@ -164,11 +156,9 @@ bool database_ready; } json << "\t]\n}\n"; - zpoller_destroy (&poller); mlm_client_destroy (&client); } catch (const std::exception& e) { - zpoller_destroy (&poller); mlm_client_destroy (&client); log_error ("%s", e.what ()); http_die ("internal-error", ""); diff --git a/src/shared/augtool.cc b/src/shared/augtool.cc index ad9c2497c..6f5353861 100644 --- a/src/shared/augtool.cc +++ b/src/shared/augtool.cc @@ -23,178 +23,118 @@ * \author Michal Hrusecky * \brief Not yet documented file */ -#include -#include #include -#include #include #include +#include #include #include #include +#include #include "shared/augtool.h" -// execute cmd, returns raw output -std::string augtool::get_cmd_out_raw(const std::string& cmd) -{ - std::lock_guard lock(m_cmd_mutex); - - std::string ret; // empty, default - - if (m_process) { - auto command{cmd}; - if (command.empty() || (command.back() != '\n')) { - command += "\n"; - } - - /// execute the actual command - auto writeSuccess = m_process->write(command); - ret = m_process->readAllStandardOutput(500); - if (!writeSuccess) { - ret.clear(); +//using namespace shared; + +std::string augtool::get_cmd_out(std::string cmd, bool key_value, + std::string sep, + std::function filter) { + std::string in = get_cmd_out_raw(cmd); + std::vector spl = fty::split(in, "\n"); + bool not_first = false; + std::string out; + if(spl.size() >= 3) { + spl.erase(spl.begin()); + spl.pop_back(); + } else { + return out; + } + for(auto i : spl) { + auto pos = i.find_first_of("="); + if(pos == std::string::npos) { + if(key_value) + continue; + if(not_first) + out += sep; + if(filter(i)) + continue; + out += i; + } else { + if(not_first) + out += sep; + if(filter(i.substr(pos+2))) + continue; + out += i.substr(pos+2); } + not_first = true; } - return ret; + return out; } -// execute cmd, returns post-processed output -std::string augtool::get_cmd_out( - const std::string& cmd, - bool key_value, // process only ' = ' output - const std::string& sep, // list separator - std::function filter // returns true to exclude value -) -{ - logDebug("augtool: '{}', key_value: {}, sep: '{}'", cmd, key_value, sep); - - // execute the command - std::string cmdOutput = get_cmd_out_raw(cmd); - // split output into lines - std::vector lines = fty::split(cmdOutput, "\n"); +std::string augtool::get_cmd_out_raw(std::string command) { + std::string ret; + bool err = false; - std::string ret; // empty, default - - // expect at least 3 lines - if (lines.size() >= 3) { - // remove first and last prompts lines ("match...", "augtool>") - lines.erase(lines.begin()); - lines.pop_back(); - - // built ret - for (auto line : lines) { - auto pos = line.find_first_of("="); - if (pos != std::string::npos) { - // extract value (right of '=' leaving 1st space) - line = line.substr(pos + 2); - } - else { - if (key_value) { // key_value only? - continue; // ignore not ' = ' line - } - } - - // apply exclusion filter - if (filter(line)) { - continue; // ignore line - } - - if (line.empty()) { // inconsistent? - logDebug("adding an empty line!"); - } + std::lock_guard lock(mux); + if (init()) { + if(command.empty() || command.back() != '\n') { + command += "\n"; + } - ret += (ret.empty() ? "" : sep) + line; + if(!prc->write(command)) { + err = true; } + + ret = prc->readAllStandardOutput(500); + return err ? "" : ret; } + return ""; +} - logDebug("augtool: '{}', ret: '{}'", cmd, ret); - return ret; +void augtool::run_cmd(std::string cmd) { + get_cmd_out_raw(cmd); } -// returns process instance -augtool* augtool::get_instance(bool sudoer) -{ - static augtool instance_sudoer(true); // privileges escalation - static augtool instance_nopriv(false); // no escalation - - logInfo("Augtool get_instance(), sudoer: {}", sudoer); - - //get the correct instance () - auto instance = sudoer ? &instance_sudoer : &instance_nopriv; - - //Run the init - logInfo("Check that Augeas is initialise"); - - if(!instance->init(sudoer)) { - logError("Augeas could not be initilized"); - return nullptr; - } - - /// refresh instance before returning it - instance->load(); +static std::mutex clear_mux; - return instance; +void augtool::clear() { + std::lock_guard lock(clear_mux); + run_cmd(""); + run_cmd("load"); } -augtool::augtool(bool sudoer) noexcept -{ - init(sudoer); +augtool* augtool::get_instance() { + static augtool inst; + return &inst; } -bool augtool::init(bool sudoer) noexcept +augtool::augtool() { - try { - if (m_process) { // once - return true; - } + logDebug("new Process"); + init(); +} - m_process = (sudoer) - ? new fty::Process("sudo", {"augtool", "-S", "-I/usr/share/fty/lenses", "-e"}) - : new fty::Process("augtool", { "-S", "-I/usr/share/fty/lenses", "-e"}); +bool augtool::init() +{ + if (prc) { + return true; + } - if (!m_process) { - throw std::runtime_error("new failed"); - } + prc = new fty::Process("sudo", {"augtool", "-S", "-I/usr/share/fty/lenses", "-e"}); - if (!m_process->exists()) { - if (auto ret = m_process->run()) { - std::string output = get_cmd_out_raw("help"); - if (output.find("match") == output.npos) { - throw std::runtime_error("unexpected help payload"); - } - } - else { - throw std::runtime_error("run failed (" + ret.error() + ")"); + if(!prc->exists()) { + if (prc->run()) { + std::string nil = get_cmd_out_raw("help"); + if(nil.find("match") == nil.npos) { + logError("augtool returned unexpected output {}", nil); + delete prc; + prc = NULL; + return false; } } - - load(); - - logDebug("augtool init succeeded (sudoer: {})", sudoer); - return true; - } - catch (const std::exception& e) { - logFatal("augtool init (sudoer: {}) caught exception (e: {})", sudoer, e.what()); - } - - if (m_process) { - delete m_process; - m_process = nullptr; } - return false; -} - -bool augtool::initialized() -{ - return (m_process != nullptr); -} - -void augtool::load() -{ - static std::mutex load_mutex; - std::lock_guard lock(load_mutex); - run_cmd(""); - run_cmd("load"); + clear(); + return true; } diff --git a/src/shared/augtool.h b/src/shared/augtool.h index 99df8a20d..843ab9844 100644 --- a/src/shared/augtool.h +++ b/src/shared/augtool.h @@ -32,8 +32,11 @@ class augtool { public: - /// Singleton get_instance method (w/ privileges escalation option) - static augtool* get_instance(bool sudoer); + /// Singleton get_instance method + static augtool* get_instance(); + + /// Runs command without returning anything + void run_cmd(std::string cmd); /// Method returning parsed output of augtool /// @@ -45,20 +48,15 @@ class augtool /// @param sep used to separate individual values /// @param filter values for which it returns true are omitted std::string get_cmd_out( - const std::string& cmd, + std::string cmd, bool key_value = true, - const std::string& sep = "", - std::function filter = [](const std::string) -> bool { return false; } - ); + std::string sep = "", + std::function filter = [](const std::string) -> bool { + return false; + }); /// Return string directly as returned from augtool - std::string get_cmd_out_raw(const std::string& cmd); - - /// Runs command without returning anything - void run_cmd(const std::string& cmd) - { - get_cmd_out_raw(cmd); - } + std::string get_cmd_out_raw(std::string cmd); /// Saves current state void save() @@ -67,13 +65,11 @@ class augtool } protected: - std::mutex m_cmd_mutex; //!< Shared mutex, to protect cmd overlap - fty::Process* m_process{nullptr}; //!< Subprocess itself + std::mutex mux; //!< Shared mutex + fty::Process* prc; //!< Subprocess itself /// Ensures we are in reasonably clean state - augtool(bool sudoer) noexcept; - bool init(bool sudoer) noexcept; - bool initialized(); - - void load(); + void clear(); + augtool(); + bool init(); };