diff options
| author | Karel Klic <kklic@redhat.com> | 2009-11-09 13:55:20 +0100 |
|---|---|---|
| committer | Karel Klic <kklic@redhat.com> | 2009-11-09 13:55:20 +0100 |
| commit | 19f406b4930c931a932dc6930762e8e12e29ce8b (patch) | |
| tree | b08468bf9a010cbce0ecd000a0279222f0d4b4e7 /lib | |
| parent | 801ab58f32f2cb7dca3352b721a07c83705a0287 (diff) | |
| parent | 8fa9a6ecd247454ab758efecf818d8067455c778 (diff) | |
| download | abrt-19f406b4930c931a932dc6930762e8e12e29ce8b.tar.gz abrt-19f406b4930c931a932dc6930762e8e12e29ce8b.tar.xz abrt-19f406b4930c931a932dc6930762e8e12e29ce8b.zip | |
merge
Diffstat (limited to 'lib')
55 files changed, 2943 insertions, 1520 deletions
diff --git a/lib/Plugins/Bugzilla.cpp b/lib/Plugins/Bugzilla.cpp index 48c5eb5..ecd4dd6 100644 --- a/lib/Plugins/Bugzilla.cpp +++ b/lib/Plugins/Bugzilla.cpp @@ -1,456 +1,590 @@ - -#include <xmlrpc-c/base.hpp> +#include <xmlrpc-c/base.h> +#include <xmlrpc-c/client.h> #include "abrtlib.h" #include "Bugzilla.h" #include "CrashTypes.h" #include "DebugDump.h" #include "ABRTException.h" #include "CommLayerInner.h" +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif #define XML_RPC_SUFFIX "/xmlrpc.cgi" -CReporterBugzilla::CReporterBugzilla() : - m_pXmlrpcTransport(NULL), - m_pXmlrpcClient(NULL), - m_pCarriageParm(NULL), - m_sBugzillaURL("https://bugzilla.redhat.com"), - m_sBugzillaXMLRPC("https://bugzilla.redhat.com" + std::string(XML_RPC_SUFFIX)), - m_bNoSSLVerify(false), - m_bLoggedIn(false) -{} -CReporterBugzilla::~CReporterBugzilla() -{} - -void CReporterBugzilla::NewXMLRPCClient() -{ - m_pXmlrpcTransport = new xmlrpc_c::clientXmlTransport_curl( - xmlrpc_c::clientXmlTransport_curl::constrOpt() - .no_ssl_verifyhost(m_bNoSSLVerify) - .no_ssl_verifypeer(m_bNoSSLVerify) - ); - m_pXmlrpcClient = new xmlrpc_c::client_xml(m_pXmlrpcTransport); - m_pCarriageParm = new xmlrpc_c::carriageParm_curl0(m_sBugzillaXMLRPC); -} - -void CReporterBugzilla::DeleteXMLRPCClient() +static void get_product_and_version(const std::string& pRelease, + std::string& pProduct, + std::string& pVersion) { - if (m_pCarriageParm != NULL) + if (pRelease.find("Rawhide") != std::string::npos) { - delete m_pCarriageParm; - m_pCarriageParm = NULL; + pProduct = "Fedora"; + pVersion = "rawhide"; + return; } - if (m_pXmlrpcClient != NULL) + if (pRelease.find("Fedora") != std::string::npos) { - delete m_pXmlrpcClient; - m_pXmlrpcClient = NULL; + pProduct = "Fedora"; } - if (m_pXmlrpcTransport != NULL) + else if (pRelease.find("Red Hat Enterprise Linux") != std::string::npos) { - delete m_pXmlrpcTransport; - m_pXmlrpcTransport = NULL; + pProduct = "Red Hat Enterprise Linux "; } -} - -PRInt32 CReporterBugzilla::Base64Encode_cb(void *arg, const char *obuf, PRInt32 size) -{ - CReporterBugzilla* bz = static_cast<CReporterBugzilla*>(arg); - int ii; - for (ii = 0; ii < size; ii++) + std::string::size_type pos = pRelease.find("release"); + pos = pRelease.find(" ", pos) + 1; + while (pRelease[pos] != ' ') { - if (isprint(obuf[ii])) + pVersion += pRelease[pos]; + if (pProduct == "Red Hat Enterprise Linux ") { - bz->m_sAttchmentInBase64 += obuf[ii]; + pProduct += pRelease[pos]; } + pos++; } - return 1; } -void CReporterBugzilla::Login() +static void create_new_bug_description(const map_crash_report_t& pCrashReport, std::string& pDescription) { - xmlrpc_c::paramList paramList; - map_xmlrpc_params_t loginParams; - map_xmlrpc_params_t ret; - loginParams["login"] = xmlrpc_c::value_string(m_sLogin); - loginParams["password"] = xmlrpc_c::value_string(m_sPassword); - paramList.add(xmlrpc_c::value_struct(loginParams)); - xmlrpc_c::rpcPtr rpc(new xmlrpc_c::rpc("User.login", paramList)); - try - { - if( (m_sLogin == "") && (m_sPassword=="") ) - { - log("Empty login and password"); - throw std::string(_("Empty login and password. Please check Bugzilla.conf")); - } - rpc->call(m_pXmlrpcClient, m_pCarriageParm); - ret = xmlrpc_c::value_struct(rpc->getResult()); - std::stringstream ss; - ss << xmlrpc_c::value_int(ret["id"]); - log("Login id: %s", ss.str().c_str()); - } - catch (std::exception& e) - { - throw CABRTException(EXCEP_PLUGIN, std::string("CReporterBugzilla::Login(): ") + e.what()); - } - catch (std::string& s) - { - throw CABRTException(EXCEP_PLUGIN, s); - } + pDescription = "abrt detected a crash.\n\n"; + pDescription += make_description_bz(pCrashReport); } -void CReporterBugzilla::Logout() +// FIXME: we still leak memory if this function detects a fault: +// many instances when we leave non-freed or non-xmlrpc_DECREF'ed data behind. +static void throw_if_xml_fault_occurred(xmlrpc_env *env) { - xmlrpc_c::paramList paramList; - paramList.add(xmlrpc_c::value_string("")); - xmlrpc_c::rpcPtr rpc(new xmlrpc_c::rpc("User.logout", paramList)); - try - { - rpc->call(m_pXmlrpcClient, m_pCarriageParm); - } - catch (std::exception& e) + if (env->fault_occurred) { - throw CABRTException(EXCEP_PLUGIN, std::string("CReporterBugzilla::Logout(): ") + e.what()); + std::string errmsg = ssprintf("XML-RPC Fault: %s(%d)", env->fault_string, env->fault_code); + xmlrpc_env_clean(env); // this is needed ONLY if fault_occurred + xmlrpc_env_init(env); // just in case user catches ex and _continues_ to use env + error_msg("%s", errmsg.c_str()); // show error in daemon log + throw CABRTException(EXCEP_PLUGIN, errmsg); } } -bool CReporterBugzilla::CheckCCAndReporter(const std::string& pBugId) + +/* + * Static namespace for xmlrpc stuff. + * Used mainly to ensure we always destroy xmlrpc client and server_info. + */ + +namespace { + +struct ctx { + xmlrpc_client* client; + xmlrpc_server_info* server_info; + + ctx(const char* url, bool no_ssl_verify) { new_xmlrpc_client(url, no_ssl_verify); } + ~ctx() { destroy_xmlrpc_client(); } + + void new_xmlrpc_client(const char* url, bool no_ssl_verify); + void destroy_xmlrpc_client(); + + void login(const char* login, const char* passwd); + void logout(); + int32_t check_uuid_in_bugzilla(const char* component, const char* UUID); + bool check_cc_and_reporter(uint32_t bug_id, const char* login); + void add_plus_one_cc(uint32_t bug_id, const char* login); + uint32_t new_bug(const map_crash_report_t& pCrashReport); + void add_attachments(const char* bug_id_str, const map_crash_report_t& pCrashReport); +}; + +void ctx::new_xmlrpc_client(const char* url, bool no_ssl_verify) { - xmlrpc_c::paramList paramList; - map_xmlrpc_params_t ret; + xmlrpc_env env; + xmlrpc_env_init(&env); + + /* This should be done at program startup, once. + * We do it in abrtd's main */ + /* xmlrpc_client_setup_global_const(&env); */ + + struct xmlrpc_curl_xportparms curlParms; + memset(&curlParms, 0, sizeof(curlParms)); + /* curlParms.network_interface = NULL; - done by memset */ + curlParms.no_ssl_verifypeer = no_ssl_verify; + curlParms.no_ssl_verifyhost = no_ssl_verify; +#ifdef VERSION + curlParms.user_agent = PACKAGE_NAME"/"VERSION; +#else + curlParms.user_agent = "abrt"; +#endif + + struct xmlrpc_clientparms clientParms; + memset(&clientParms, 0, sizeof(clientParms)); + clientParms.transport = "curl"; + clientParms.transportparmsP = &curlParms; + clientParms.transportparm_size = XMLRPC_CXPSIZE(user_agent); + + client = NULL; + xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS, + PACKAGE_NAME, VERSION, + &clientParms, XMLRPC_CPSIZE(transportparm_size), + &client); + throw_if_xml_fault_occurred(&env); + + server_info = xmlrpc_server_info_new(&env, url); + if (env.fault_occurred) + { + xmlrpc_client_destroy(client); + client = NULL; + } + throw_if_xml_fault_occurred(&env); +} - paramList.add(xmlrpc_c::value_string(pBugId)); - xmlrpc_c::rpcPtr rpc(new xmlrpc_c::rpc("bugzilla.getBug", paramList)); - try - { - rpc->call(m_pXmlrpcClient, m_pCarriageParm); - ret = xmlrpc_c::value_struct(rpc->getResult()); - } - catch (std::exception& e) +void ctx::destroy_xmlrpc_client() +{ + if (server_info) { - throw CABRTException(EXCEP_PLUGIN, std::string("CReporterBugzilla::CheckCCAndReporter(): ") + e.what()); + xmlrpc_server_info_free(server_info); + server_info = NULL; } - std::string reporter = xmlrpc_c::value_string(ret["reporter"]); - if (reporter == m_sLogin) + if (client) { - return true; + xmlrpc_client_destroy(client); + client = NULL; } - std::vector<xmlrpc_c::value> ccs = xmlrpc_c::value_array(ret["cc"]).vectorValueValue(); - int ii; - for (ii = 0; ii < ccs.size(); ii++) - { - std::string cc = xmlrpc_c::value_string(ccs[ii]); - if (cc == m_sLogin) - { - return true; - } - } - return false; } -void CReporterBugzilla::AddPlusOneCC(const std::string& pBugId) +void ctx::login(const char* login, const char* passwd) { - xmlrpc_c::paramList paramList; - map_xmlrpc_params_t addCCParams; - map_xmlrpc_params_t ret; - map_xmlrpc_params_t updates; + xmlrpc_env env; + xmlrpc_env_init(&env); - std::vector<xmlrpc_c::value> CCList; - CCList.push_back(xmlrpc_c::value_string(m_sLogin)); - updates["add_cc"] = xmlrpc_c::value_array(CCList); + xmlrpc_value* param = xmlrpc_build_value(&env, "({s:s,s:s})", "login", login, "password", passwd); + throw_if_xml_fault_occurred(&env); - addCCParams["ids"] = xmlrpc_c::value_int(atoi(pBugId.c_str())); - addCCParams["updates"] = xmlrpc_c::value_struct(updates); + xmlrpc_value* result = NULL; + xmlrpc_client_call2(&env, client, server_info, "User.login", param, &result); + xmlrpc_DECREF(param); + if (result) + xmlrpc_DECREF(result); - paramList.add(xmlrpc_c::value_struct(addCCParams)); - xmlrpc_c::rpcPtr rpc(new xmlrpc_c::rpc("Bug.update", paramList)); - try - { - rpc->call(m_pXmlrpcClient, m_pCarriageParm); - } - catch (std::exception& e) + if (env.fault_occurred) { - throw CABRTException(EXCEP_PLUGIN, std::string("CReporterBugzilla::AddPlusOneComment(): ") + e.what()); + std::string errmsg = ssprintf("Can't login. Check Edit->Plugins->Bugzilla and /etc/abrt/plugins/Bugzilla.conf. Server said: %s", env.fault_string); + xmlrpc_env_clean(&env); + error_msg("%s", errmsg.c_str()); // show error in daemon log + throw CABRTException(EXCEP_PLUGIN, errmsg); } - ret = xmlrpc_c::value_struct(rpc->getResult()); } -std::string CReporterBugzilla::CheckUUIDInBugzilla(const std::string& pComponent, const std::string& pUUID) +void ctx::logout() { - xmlrpc_c::paramList paramList; - map_xmlrpc_params_t searchParams; - map_xmlrpc_params_t ret; - std::string quicksearch = "ALL component:\""+ pComponent +"\" statuswhiteboard:\""+ pUUID + "\""; - searchParams["quicksearch"] = xmlrpc_c::value_string(quicksearch.c_str()); - paramList.add(xmlrpc_c::value_struct(searchParams)); - xmlrpc_c::rpcPtr rpc(new xmlrpc_c::rpc("Bug.search", paramList)); - try - { - rpc->call(m_pXmlrpcClient, m_pCarriageParm); - } - catch (std::exception& e) - { - throw CABRTException(EXCEP_PLUGIN, std::string("CReporterBugzilla::CheckUUIDInBugzilla(): ") + e.what()); - } - ret = xmlrpc_c::value_struct(rpc->getResult()); - std::vector<xmlrpc_c::value> bugs = xmlrpc_c::value_array(ret["bugs"]).vectorValueValue(); - if (bugs.size() > 0) - { - map_xmlrpc_params_t bug; - std::stringstream ss; + xmlrpc_env env; + xmlrpc_env_init(&env); + + xmlrpc_value* param = xmlrpc_build_value(&env, "(s)", ""); + throw_if_xml_fault_occurred(&env); + + xmlrpc_value* result = NULL; + xmlrpc_client_call2(&env, client, server_info, "User.logout", param, &result); + xmlrpc_DECREF(param); + if (result) + xmlrpc_DECREF(result); + throw_if_xml_fault_occurred(&env); +} - bug = xmlrpc_c::value_struct(bugs[0]); - ss << xmlrpc_c::value_int(bug["bug_id"]); +bool ctx::check_cc_and_reporter(uint32_t bug_id, const char* login) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); - log("Bug is already reported: %s", ss.str().c_str()); - update_client(_("Bug is already reported: ") + ss.str()); + xmlrpc_value* param = xmlrpc_build_value(&env, "(s)", to_string(bug_id).c_str()); + throw_if_xml_fault_occurred(&env); - return ss.str(); - } - return ""; -} + xmlrpc_value* result = NULL; + xmlrpc_client_call2(&env, client, server_info, "bugzilla.getBug", param, &result); + throw_if_xml_fault_occurred(&env); + xmlrpc_DECREF(param); -void CReporterBugzilla::CreateNewBugDescription(const map_crash_report_t& pCrashReport, std::string& pDescription) -{ - std::string howToReproduce; - std::string comment; + xmlrpc_value* reporter_member = NULL; + xmlrpc_struct_find_value(&env, result, "reporter", &reporter_member); + throw_if_xml_fault_occurred(&env); - if (pCrashReport.find(CD_REPRODUCE) != pCrashReport.end()) + if (reporter_member) { - howToReproduce = "\n\nHow to reproduce\n" - "-----\n" + - pCrashReport.find(CD_REPRODUCE)->second[CD_CONTENT]; - } - if (pCrashReport.find(CD_COMMENT) != pCrashReport.end()) - { - comment = "\n\nComment\n" - "-----\n" + - pCrashReport.find(CD_COMMENT)->second[CD_CONTENT]; + const char* reporter = NULL; + xmlrpc_read_string(&env, reporter_member, &reporter); + throw_if_xml_fault_occurred(&env); + + bool eq = (strcmp(reporter, login) == 0); + free((void*)reporter); + xmlrpc_DECREF(reporter_member); + if (eq) + { + xmlrpc_DECREF(result); + return true; + } } - pDescription = "\nabrt detected a crash.\n" + - howToReproduce + - comment + - "\n\nAdditional information\n" - "======\n"; - - map_crash_report_t::const_iterator it; - for (it = pCrashReport.begin(); it != pCrashReport.end(); it++) + + xmlrpc_value* cc_member = NULL; + xmlrpc_struct_find_value(&env, result, "cc", &cc_member); + throw_if_xml_fault_occurred(&env); + + if (cc_member) { - if (it->second[CD_TYPE] == CD_TXT) + uint32_t array_size = xmlrpc_array_size(&env, cc_member); + + for (uint32_t i = 0; i < array_size; i++) { - if (it->first != CD_UUID && - it->first != FILENAME_ARCHITECTURE && - it->first != FILENAME_RELEASE && - it->first != CD_REPRODUCE && - it->first != CD_COMMENT) + xmlrpc_value* item = NULL; + xmlrpc_array_read_item(&env, cc_member, i, &item); // Correct + throw_if_xml_fault_occurred(&env); + + const char* cc = NULL; + xmlrpc_read_string(&env, item, &cc); + throw_if_xml_fault_occurred(&env); + + bool eq = (strcmp(cc, login) == 0); + free((void*)cc); + xmlrpc_DECREF(item); + if (eq) { - pDescription += "\n" + it->first + "\n"; - pDescription += "-----\n"; - pDescription += it->second[CD_CONTENT] + "\n\n"; + xmlrpc_DECREF(cc_member); + xmlrpc_DECREF(result); + return true; } } - else if (it->second[CD_TYPE] == CD_ATT) - { - pDescription += "\n\nAttached files\n" - "----\n"; - pDescription += it->first + "\n"; - } - else if (it->second[CD_TYPE] == CD_BIN) - { - char buffer[1024]; - snprintf(buffer, 1024, _("Binary file %s will not be reported."), it->first.c_str()); - warn_client(std::string(buffer)); - //update_client(_("Binary file ")+it->first+_(" will not be reported.")); - } + xmlrpc_DECREF(cc_member); } + + xmlrpc_DECREF(result); + return false; } -void CReporterBugzilla::GetProductAndVersion(const std::string& pRelease, - std::string& pProduct, - std::string& pVersion) +void ctx::add_plus_one_cc(uint32_t bug_id, const char* login) { - if (pRelease.find("Rawhide") != std::string::npos) - { - pProduct = "Fedora"; - pVersion = "rawhide"; - return; - } - if (pRelease.find("Fedora") != std::string::npos) - { - pProduct = "Fedora"; - } - else if (pRelease.find("Red Hat Enterprise Linux") != std::string::npos) - { - pProduct = "Red Hat Enterprise Linux "; - } - std::string::size_type pos = pRelease.find("release"); - pos = pRelease.find(" ", pos) + 1; - while (pRelease[pos] != ' ') + xmlrpc_env env; + xmlrpc_env_init(&env); + + xmlrpc_value* param = xmlrpc_build_value(&env, "({s:i,s:{s:(s)}})", "ids", bug_id, "updates", "add_cc", login); + throw_if_xml_fault_occurred(&env); + + xmlrpc_value* result = NULL; + xmlrpc_client_call2(&env, client, server_info, "Bug.update", param, &result); + throw_if_xml_fault_occurred(&env); + + xmlrpc_DECREF(result); + xmlrpc_DECREF(param); +} + +int32_t ctx::check_uuid_in_bugzilla(const char* component, const char* UUID) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + + std::string query = ssprintf("ALL component:\"%s\" statuswhiteboard:\"%s\"", component, UUID); + + xmlrpc_value* param = xmlrpc_build_value(&env, "({s:s})", "quicksearch", query.c_str()); + throw_if_xml_fault_occurred(&env); + + xmlrpc_value* result = NULL; + xmlrpc_client_call2(&env, client, server_info, "Bug.search", param, &result); + throw_if_xml_fault_occurred(&env); + xmlrpc_DECREF(param); + + xmlrpc_value* bugs_member = NULL; + xmlrpc_struct_find_value(&env, result, "bugs", &bugs_member); + throw_if_xml_fault_occurred(&env); + + if (bugs_member) { - pVersion += pRelease[pos]; - if (pProduct == "Red Hat Enterprise Linux ") + // when array size is equal 0 that means no bug reported + uint32_t array_size = xmlrpc_array_size(&env, bugs_member); + throw_if_xml_fault_occurred(&env); + if (array_size == 0) { - pProduct += pRelease[pos]; + xmlrpc_DECREF(bugs_member); + xmlrpc_DECREF(result); + return -1; } - pos++; + + xmlrpc_value* item = NULL; + xmlrpc_array_read_item(&env, bugs_member, 0, &item); // Correct + throw_if_xml_fault_occurred(&env); + xmlrpc_value* bug = NULL; + xmlrpc_struct_find_value(&env, item, "bug_id", &bug); + throw_if_xml_fault_occurred(&env); + + if (bug) + { + xmlrpc_int bug_id; + xmlrpc_read_int(&env, bug, &bug_id); + log("Bug is already reported: %i", (int)bug_id); + update_client(_("Bug is already reported: %i"), (int)bug_id); + + xmlrpc_DECREF(bug); + xmlrpc_DECREF(item); + xmlrpc_DECREF(bugs_member); + xmlrpc_DECREF(result); + return bug_id; + } + xmlrpc_DECREF(item); + xmlrpc_DECREF(bugs_member); } + + xmlrpc_DECREF(result); + return -1; } -std::string CReporterBugzilla::NewBug(const map_crash_report_t& pCrashReport) +uint32_t ctx::new_bug(const map_crash_report_t& pCrashReport) { - xmlrpc_c::paramList paramList; - map_xmlrpc_params_t bugParams; - map_xmlrpc_params_t ret; + xmlrpc_env env; + xmlrpc_env_init(&env); + std::string package = pCrashReport.find(FILENAME_PACKAGE)->second[CD_CONTENT]; std::string component = pCrashReport.find(FILENAME_COMPONENT)->second[CD_CONTENT]; + std::string release = pCrashReport.find(FILENAME_RELEASE)->second[CD_CONTENT]; + std::string arch = pCrashReport.find(FILENAME_ARCHITECTURE)->second[CD_CONTENT]; + std::string uuid = pCrashReport.find(CD_UUID)->second[CD_CONTENT]; + + std::string summary = "[abrt] crash detected in " + package; + std::string status_whiteboard = "abrt_hash:" + uuid; + std::string description; - std::string release = pCrashReport.find(FILENAME_RELEASE)->second[CD_CONTENT];; + create_new_bug_description(pCrashReport, description); + std::string product; std::string version; - std::stringstream bugId; - CreateNewBugDescription(pCrashReport, description); - GetProductAndVersion(release, product, version); - - bugParams["product"] = xmlrpc_c::value_string(product); - bugParams["component"] = xmlrpc_c::value_string(component); - bugParams["version"] = xmlrpc_c::value_string(version); - //bugParams["op_sys"] = xmlrpc_c::value_string("Linux"); - bugParams["summary"] = xmlrpc_c::value_string("[abrt] crash detected in " + package); - bugParams["description"] = xmlrpc_c::value_string(description); - bugParams["status_whiteboard"] = xmlrpc_c::value_string("abrt_hash:" + pCrashReport.find(CD_UUID)->second[CD_CONTENT]); - bugParams["platform"] = xmlrpc_c::value_string(pCrashReport.find(FILENAME_ARCHITECTURE)->second[CD_CONTENT]); - paramList.add(xmlrpc_c::value_struct(bugParams)); - - xmlrpc_c::rpcPtr rpc(new xmlrpc_c::rpc("Bug.create", paramList)); - try - { - rpc->call(m_pXmlrpcClient, m_pCarriageParm); - ret = xmlrpc_c::value_struct(rpc->getResult()); - bugId << xmlrpc_c::value_int(ret["id"]); - log("New bug id: %s", bugId.str().c_str()); - update_client(_("New bug id: ") + bugId.str()); - } - catch (std::exception& e) - { - throw CABRTException(EXCEP_PLUGIN, std::string("CReporterBugzilla::NewBug(): ") + e.what()); - } - return bugId.str(); + get_product_and_version(release, product, version); + + xmlrpc_value* param = xmlrpc_build_value(&env, "({s:s,s:s,s:s,s:s,s:s,s:s,s:s})", + "product", product.c_str(), + "component", component.c_str(), + "version", version.c_str(), + "summary", summary.c_str(), + "description", description.c_str(), + "status_whiteboard", status_whiteboard.c_str(), + "platform", arch.c_str() + ); + throw_if_xml_fault_occurred(&env); + + xmlrpc_value* result; + xmlrpc_client_call2(&env, client, server_info, "Bug.create", param, &result); + throw_if_xml_fault_occurred(&env); + + xmlrpc_value* id; + xmlrpc_struct_find_value(&env, result, "id", &id); + throw_if_xml_fault_occurred(&env); + + xmlrpc_int bug_id = -1; + if (id) + { + xmlrpc_read_int(&env, id, &bug_id); + throw_if_xml_fault_occurred(&env); + log("New bug id: %i", bug_id); + update_client(_("New bug id: %i"), bug_id); + } + + xmlrpc_DECREF(result); + xmlrpc_DECREF(param); + xmlrpc_DECREF(id); + return bug_id; } -void CReporterBugzilla::AddAttachments(const std::string& pBugId, const map_crash_report_t& pCrashReport) +void ctx::add_attachments(const char* bug_id_str, const map_crash_report_t& pCrashReport) { - xmlrpc_c::paramList paramList; - map_xmlrpc_params_t attachmentParams; - std::vector<xmlrpc_c::value> ret; - NSSBase64Encoder* base64; + xmlrpc_env env; + xmlrpc_env_init(&env); - map_crash_report_t::const_iterator it; - for (it = pCrashReport.begin(); it != pCrashReport.end(); it++) + xmlrpc_value* result = NULL; + + map_crash_report_t::const_iterator it = pCrashReport.begin(); + for (; it != pCrashReport.end(); it++) { if (it->second[CD_TYPE] == CD_ATT) { - m_sAttchmentInBase64 = ""; - base64 = NSSBase64Encoder_Create(Base64Encode_cb, this); - if (!base64) - { - throw CABRTException(EXCEP_PLUGIN, "CReporterBugzilla::AddAttachemnt(): cannot initialize base64."); - } - - NSSBase64Encoder_Update(base64, - reinterpret_cast<const unsigned char*>(it->second[CD_CONTENT].c_str()), - it->second[CD_CONTENT].length()); - NSSBase64Encoder_Destroy(base64, PR_FALSE); - - paramList.add(xmlrpc_c::value_string(pBugId)); - attachmentParams["description"] = xmlrpc_c::value_string("File: " + it->first); - attachmentParams["filename"] = xmlrpc_c::value_string(it->first); - attachmentParams["contenttype"] = xmlrpc_c::value_string("text/plain"); - attachmentParams["data"] = xmlrpc_c::value_string(m_sAttchmentInBase64); - paramList.add(xmlrpc_c::value_struct(attachmentParams)); - xmlrpc_c::rpcPtr rpc(new xmlrpc_c::rpc("bugzilla.addAttachment", paramList)); - try - { - rpc->call(m_pXmlrpcClient, m_pCarriageParm); - ret = xmlrpc_c::value_array(rpc->getResult()).vectorValueValue(); - std::stringstream ss; - ss << xmlrpc_c::value_int(ret[0]); - log("New attachment id: %s", ss.str().c_str()); - } - catch (std::exception& e) - { - throw CABRTException(EXCEP_PLUGIN, std::string("CReporterBugzilla::AddAttachemnt(): ") + e.what()); - } + std::string description = "File: " + it->first; + const std::string& to_encode = it->second[CD_CONTENT]; + char *encoded64 = encode_base64(to_encode.c_str(), to_encode.length()); + xmlrpc_value* param = xmlrpc_build_value(&env,"(s{s:s,s:s,s:s,s:s})", + bug_id_str, + "description", description.c_str(), + "filename", it->first.c_str(), + "contenttype", "text/plain", + "data", encoded64 + ); + free(encoded64); + throw_if_xml_fault_occurred(&env); + + xmlrpc_client_call2(&env, client, server_info, "bugzilla.addAttachment", param, &result); + throw_if_xml_fault_occurred(&env); + xmlrpc_DECREF(result); + xmlrpc_DECREF(param); } } } -std::string CReporterBugzilla::Report(const map_crash_report_t& pCrashReport, const std::string& pArgs) -{ - std::string package = pCrashReport.find(FILENAME_PACKAGE)->second[CD_CONTENT]; - std::string component = pCrashReport.find(FILENAME_COMPONENT)->second[CD_CONTENT]; - std::string uuid = pCrashReport.find(CD_UUID)->second[CD_CONTENT]; - std::string bugId; +} /* namespace */ - NewXMLRPCClient(); +/* + * CReporterBugzilla + */ - m_bLoggedIn = false; +CReporterBugzilla::CReporterBugzilla() : + m_bNoSSLVerify(false), + m_sBugzillaURL("https://bugzilla.redhat.com"), + m_sBugzillaXMLRPC("https://bugzilla.redhat.com"XML_RPC_SUFFIX) +{} + +CReporterBugzilla::~CReporterBugzilla() +{} + +std::string CReporterBugzilla::Report(const map_crash_report_t& pCrashReport, + const map_plugin_settings_t& pSettings, + const std::string& pArgs) +{ + int32_t bug_id = -1; + std::string Login; + std::string Password; + std::string BugzillaXMLRPC; + std::string BugzillaURL; + bool NoSSLVerify; + map_plugin_settings_t settings = parse_settings(pSettings); + /* if parse_settings fails it returns an empty map so we need to use defaults*/ + if(!settings.empty()) + { + Login = settings["Login"]; + Password = settings["Password"]; + BugzillaXMLRPC = settings["BugzillaXMLRPC"]; + BugzillaURL = settings["BugzillaURL"]; + NoSSLVerify = settings["NoSSLVerify"] == "yes"; + } + else + { + Login = m_sLogin; + Password = m_sPassword; + BugzillaXMLRPC = m_sBugzillaXMLRPC; + BugzillaURL = m_sBugzillaURL; + NoSSLVerify = m_bNoSSLVerify; + } + + std::string component = pCrashReport.find(FILENAME_COMPONENT)->second[CD_CONTENT]; + std::string uuid = pCrashReport.find(CD_UUID)->second[CD_CONTENT]; try { + ctx bz_server(BugzillaXMLRPC.c_str(), NoSSLVerify); + update_client(_("Checking for duplicates...")); - bugId = CheckUUIDInBugzilla(component, uuid); - if ( bugId != "" ) { - update_client(_("Logging into bugzilla...")); - Login(); - m_bLoggedIn = true; + bug_id = bz_server.check_uuid_in_bugzilla(component.c_str(), uuid.c_str()); + + update_client(_("Logging into bugzilla...")); + if ((Login == "") && (Password == "")) + { + VERB3 log("Empty login and password"); + throw CABRTException(EXCEP_PLUGIN, std::string(_("Empty login and password. Please check Bugzilla.conf"))); + } + bz_server.login(Login.c_str(), Password.c_str()); + + if (bug_id > 0) + { update_client(_("Checking CC...")); - if (!CheckCCAndReporter(bugId) && m_bLoggedIn) + if (!bz_server.check_cc_and_reporter(bug_id, Login.c_str())) { - AddPlusOneCC(bugId); + bz_server.add_plus_one_cc(bug_id, Login.c_str()); } - DeleteXMLRPCClient(); - return m_sBugzillaURL + "/show_bug.cgi?id=" + bugId; + bz_server.logout(); + return BugzillaURL + "/show_bug.cgi?id=" + to_string(bug_id); } - update_client(_("Logging into bugzilla...")); - Login(); - m_bLoggedIn = true; + + update_client(_("Creating new bug...")); + bug_id = bz_server.new_bug(pCrashReport); + bz_server.add_attachments(to_string(bug_id).c_str(), pCrashReport); + + update_client(_("Logging out...")); + bz_server.logout(); } catch (CABRTException& e) { - DeleteXMLRPCClient(); - throw CABRTException(EXCEP_PLUGIN, std::string("CReporterBugzilla::Report(): ") + e.what()); - return ""; + throw CABRTException(EXCEP_PLUGIN, e.what()); } + if (bug_id > 0) + { + return BugzillaURL + "/show_bug.cgi?id=" + to_string(bug_id); + } - update_client(_("Creating new bug...")); - try + return BugzillaURL + "/show_bug.cgi?id="; +} + +map_plugin_settings_t CReporterBugzilla::parse_settings(const map_plugin_settings_t& pSettings) +{ + map_plugin_settings_t plugin_settings; + map_plugin_settings_t::const_iterator it; + map_plugin_settings_t::const_iterator end = pSettings.end(); + + it = pSettings.find("BugzillaURL"); + if (it != end) { - bugId = NewBug(pCrashReport); - AddAttachments(bugId, pCrashReport); - update_client(_("Logging out...")); - Logout(); + std::string BugzillaURL = it->second; + //remove the /xmlrpc.cgi part from old settings + //FIXME: can be removed after users are informed about new config format + std::string::size_type pos = BugzillaURL.find(XML_RPC_SUFFIX); + if (pos != std::string::npos) + { + BugzillaURL.erase(pos); + } + //remove the trailing '/' + while (BugzillaURL[BugzillaURL.length() - 1] == '/') + { + BugzillaURL.erase(BugzillaURL.length() - 1); + } + plugin_settings["BugzillaXMLRPC"] = BugzillaURL + XML_RPC_SUFFIX; + plugin_settings["BugzillaURL"] = BugzillaURL; } - catch (CABRTException& e) + + it = pSettings.find("Login"); + if (it == end) { - DeleteXMLRPCClient(); - throw CABRTException(EXCEP_PLUGIN, std::string("CReporterBugzilla::Report(): ") + e.what()); + /* if any of the option is not set we use the defaults for everything */ + plugin_settings.clear(); + return plugin_settings; } + plugin_settings["Login"] = it->second; + it = pSettings.find("Password"); + if (it == end) + { + plugin_settings.clear(); + return plugin_settings; + } + plugin_settings["Password"] = it->second; - DeleteXMLRPCClient(); - return m_sBugzillaURL + "/show_bug.cgi?id=" + bugId; + it = pSettings.find("NoSSLVerify"); + if (it == end) + { + plugin_settings.clear(); + return plugin_settings; + } + plugin_settings["NoSSLVerify"] = it->second; + VERB1 log("User settings ok, using them instead of defaults"); + return plugin_settings; } void CReporterBugzilla::SetSettings(const map_plugin_settings_t& pSettings) { - if (pSettings.find("BugzillaURL") != pSettings.end()) +//BUG! This gets called when user's keyring contains login data, +//then it takes precedence over /etc/abrt/plugins/Bugzilla.conf. +//I got a case when keyring had a STALE password, and there was no way +//for me to know that it is being used. Moreover, when I discovered it +//(by hacking abrt source!), I don't know how to purge it from the keyring. +//At the very least, log("SOMETHING") here. + + map_plugin_settings_t::const_iterator it; + map_plugin_settings_t::const_iterator end = pSettings.end(); + + it = pSettings.find("BugzillaURL"); + if (it != end) { - m_sBugzillaURL = pSettings.find("BugzillaURL")->second; + m_sBugzillaURL = it->second; //remove the /xmlrpc.cgi part from old settings //FIXME: can be removed after users are informed about new config format std::string::size_type pos = m_sBugzillaURL.find(XML_RPC_SUFFIX); - if(pos != std::string::npos) + if (pos != std::string::npos) { m_sBugzillaURL.erase(pos); } @@ -465,40 +599,41 @@ void CReporterBugzilla::SetSettings(const map_plugin_settings_t& pSettings) m_sBugzillaURL.erase(--m_sBugzillaURL.end()); } */ - m_sBugzillaXMLRPC = m_sBugzillaURL + std::string(XML_RPC_SUFFIX); + m_sBugzillaXMLRPC = m_sBugzillaURL + XML_RPC_SUFFIX; } - if (pSettings.find("Login") != pSettings.end()) + it = pSettings.find("Login"); + if (it != end) { - m_sLogin = pSettings.find("Login")->second; + m_sLogin = it->second; } - if (pSettings.find("Password") != pSettings.end()) + it = pSettings.find("Password"); + if (it != end) { - m_sPassword = pSettings.find("Password")->second; + m_sPassword = it->second; } - if (pSettings.find("NoSSLVerify") != pSettings.end()) + it = pSettings.find("NoSSLVerify"); + if (it != end) { - m_bNoSSLVerify = pSettings.find("NoSSLVerify")->second == "yes"; + m_bNoSSLVerify = (it->second == "yes"); } } -map_plugin_settings_t CReporterBugzilla::GetSettings() +const map_plugin_settings_t& CReporterBugzilla::GetSettings() { - map_plugin_settings_t ret; - - ret["BugzillaURL"] = m_sBugzillaURL; - ret["Login"] = m_sLogin; - ret["Password"] = m_sPassword; - ret["NoSSLVerify"] = m_bNoSSLVerify ? "yes" : "no"; + m_pSettings["BugzillaURL"] = m_sBugzillaURL; + m_pSettings["Login"] = m_sLogin; + m_pSettings["Password"] = m_sPassword; + m_pSettings["NoSSLVerify"] = m_bNoSSLVerify ? "yes" : "no"; - return ret; + return m_pSettings; } PLUGIN_INFO(REPORTER, CReporterBugzilla, "Bugzilla", - "0.0.3", + "0.0.4", "Check if a bug isn't already reported in a bugzilla " "and if not, report it.", - "zprikryl@redhat.com", + "npajkovs@redhat.com", "https://fedorahosted.org/abrt/wiki", PLUGINS_LIB_DIR"/Bugzilla.GTKBuilder"); diff --git a/lib/Plugins/Bugzilla.h b/lib/Plugins/Bugzilla.h index f6a8976..55a5f5f 100644 --- a/lib/Plugins/Bugzilla.h +++ b/lib/Plugins/Bugzilla.h @@ -3,48 +3,26 @@ #include "Plugin.h" #include "Reporter.h" -#include <xmlrpc-c/client.hpp> - -#include <nssb64.h> class CReporterBugzilla : public CReporter { private: - typedef std::map<std::string, xmlrpc_c::value> map_xmlrpc_params_t; - - void NewXMLRPCClient(); - void DeleteXMLRPCClient(); - static PRInt32 Base64Encode_cb(void *arg, const char *obuf, PRInt32 size); - void Login(); - void Logout(); - bool CheckCCAndReporter(const std::string& pBugId); - void AddPlusOneCC(const std::string& pBugId); - std::string CheckUUIDInBugzilla(const std::string& pComponent, const std::string& pUUID); - std::string NewBug(const map_crash_report_t& pCrashReport); - void AddAttachments(const std::string& pBugId, const map_crash_report_t& pCrashReport); - void CreateNewBugDescription(const map_crash_report_t& pCrashReport, - std::string& pDescription); - void GetProductAndVersion(const std::string& pRelease, - std::string& pProduct, - std::string& pVersion); - - xmlrpc_c::clientXmlTransport_curl* m_pXmlrpcTransport; - xmlrpc_c::client_xml* m_pXmlrpcClient; - xmlrpc_c::carriageParm_curl0 *m_pCarriageParm; + bool m_bNoSSLVerify; std::string m_sBugzillaURL; std::string m_sBugzillaXMLRPC; std::string m_sLogin; std::string m_sPassword; std::string m_sAttchmentInBase64; - bool m_bNoSSLVerify; - bool m_bLoggedIn; + + map_plugin_settings_t parse_settings(const map_plugin_settings_t& pSettings); public: CReporterBugzilla(); virtual ~CReporterBugzilla(); virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual map_plugin_settings_t GetSettings(); + virtual const map_plugin_settings_t& GetSettings(); virtual std::string Report(const map_crash_report_t& pCrashReport, + const map_plugin_settings_t& pSettings, const std::string& pArgs); }; diff --git a/lib/Plugins/CCpp.conf b/lib/Plugins/CCpp.conf index 02c6c1a..988ddf3 100644 --- a/lib/Plugins/CCpp.conf +++ b/lib/Plugins/CCpp.conf @@ -1,14 +1,17 @@ # Configuration file for CCpp add-on -# NONE OF THESE OPTIONS IS SUPPORTED -# generate memory map too +# generate memory map too (IGNORED FOR NOW) MemoryMap = no # how to get debug-info: install, mount ## install - download and install debug-info packages ## mount - mount fedora NFS with debug info +## (IGNORED FOR NOW) DebugInfo = install -# With this option set to "yes" -# will be installed debuginfo -InstallDebuginfo = yes +# If this option is set to "yes", +# debuginfos will be installed to @@LOCALSTATEDIR@@/cache/abrt-di +InstallDebugInfo = yes +# Keep @@LOCALSTATEDIR@@/cache/abrt-di +# from growing out-of-bounds. +DebugInfoCacheMB = 4000 diff --git a/lib/Plugins/CCpp.cpp b/lib/Plugins/CCpp.cpp index 0e0eb3b..6d6edd5 100644 --- a/lib/Plugins/CCpp.cpp +++ b/lib/Plugins/CCpp.cpp @@ -33,28 +33,21 @@ #include "CommLayerInner.h" #include "Polkit.h" -#define CORE_PATTERN_IFACE "/proc/sys/kernel/core_pattern" -#define CORE_PATTERN "|"CCPP_HOOK_PATH" "DEBUG_DUMPS_DIR" %p %s %u" +#define CORE_PATTERN_IFACE "/proc/sys/kernel/core_pattern" +#define CORE_PATTERN "|"CCPP_HOOK_PATH" "DEBUG_DUMPS_DIR" %p %s %u" #define FILENAME_COREDUMP "coredump" #define FILENAME_BACKTRACE "backtrace" #define FILENAME_MEMORYMAP "memorymap" +#define DEBUGINFO_CACHE_DIR LOCALSTATEDIR"/cache/abrt-di" + CAnalyzerCCpp::CAnalyzerCCpp() : - m_bMemoryMap(false), m_bInstallDebuginfo(true) + m_bMemoryMap(false), + m_bInstallDebugInfo(true), + m_nDebugInfoCacheMB(4000) {} -static bool is_hexstr(const char* str) -{ - while (*str) - { - if (!isxdigit(*str)) - return false; - str++; - } - return true; -} - static std::string CreateHash(const std::string& pInput) { std::string ret = ""; @@ -102,7 +95,6 @@ static std::string concat_str_vector(char **strings) static pid_t ExecVP(char** pArgs, uid_t uid, std::string& pOutput) { int pipeout[2]; - char buff[1024]; pid_t child; struct passwd* pw = getpwuid(uid); @@ -144,6 +136,7 @@ static pid_t ExecVP(char** pArgs, uid_t uid, std::string& pOutput) close(pipeout[1]); /* write side of the pipe */ int r; + char buff[1024]; while ((r = read(pipeout[0], buff, sizeof(buff) - 1)) > 0) { buff[r] = '\0'; @@ -156,7 +149,96 @@ static pid_t ExecVP(char** pArgs, uid_t uid, std::string& pOutput) return 0; } -static void GetBacktrace(const std::string& pDebugDumpDir, std::string& pBacktrace) +enum LineRating +{ + // RATING EXAMPLE + MissingEverything = 0, // #0 0x0000dead in ?? () + MissingFunction = 1, // #0 0x0000dead in ?? () from /usr/lib/libfoobar.so.4 + MissingLibrary = 2, // #0 0x0000dead in foobar() + MissingSourceFile = 3, // #0 0x0000dead in FooBar::FooBar () from /usr/lib/libfoobar.so.4 + Good = 4, // #0 0x0000dead in FooBar::crash (this=0x0) at /home/user/foobar.cpp:204 + BestRating = Good, +}; + +static LineRating rate_line(const char *line) +{ +#define FOUND(x) (strstr(line, x) != NULL) + /* see the "enum LineRating" comments for possible combinations */ + const char *function = strstr(line, " in "); + if (function) + { + if (function[4] == '?') /* " in ??" does not count */ + { + function = NULL; + } + else + { + bool source_file = FOUND(" at "); + if (source_file) + return Good; + } + } + bool library = FOUND(" from "); + if (function && library) + return MissingSourceFile; + if (function) + return MissingLibrary; + if (library) + return MissingFunction; + + return MissingEverything; +#undef FOUND +} + +/* returns number of "stars" to show */ +static int rate_backtrace(const char *backtrace) +{ + int i, len; + int multiplier = 0; + int rating = 0; + int best_possible_rating = 0; + + /* We look at the frames in reversed order, since: + * - rate_line() checks starting from the first line of the frame + * (note: it may need to look at more than one line!) + * - we increase weight (multiplier) for every frame, + * so that topmost frames end up most important + */ + len = 0; + for (i = strlen(backtrace) - 1; i >= 0; i--) + { + if (backtrace[i] == '#') /* this separates frames from each other */ + { + std::string s(backtrace + i + 1, len); + multiplier++; + rating += rate_line(s.c_str()) * multiplier; + best_possible_rating += BestRating * multiplier; + len = 0; /* starting new line */ + } + else + { + len++; + } + } + + /* Bogus "backtrace" with zero frames? */ + if (best_possible_rating == 0) + return 0; + + /* Returning number of "stars" to show */ + if (rating*10 >= best_possible_rating*8) /* >= 0.8 */ + return 4; + if (rating*10 >= best_possible_rating*6) + return 3; + if (rating*10 >= best_possible_rating*4) + return 2; + if (rating*10 >= best_possible_rating*2) + return 1; + + return 0; +} + +static void GetBacktrace(const char *pDebugDumpDir, std::string& pBacktrace) { update_client(_("Getting backtrace...")); @@ -174,13 +256,13 @@ static void GetBacktrace(const std::string& pDebugDumpDir, std::string& pBacktra unsetenv("TERM"); putenv((char*)"TERM=dumb"); - char* args[9]; + char* args[11]; args[0] = (char*)"gdb"; args[1] = (char*)"-batch"; // when/if gdb supports it: // (https://bugzilla.redhat.com/show_bug.cgi?id=528668): - //args[2] = (char*)"-ex"; - //args[3] = "set debug-file-directory /usr/lib/debug/.build-id:/var/cache/abrt-di/usr/lib/debug/.build-id"; + args[2] = (char*)"-ex"; + args[3] = (char*)"set debug-file-directory /usr/lib/debug:" DEBUGINFO_CACHE_DIR"/usr/lib/debug"; /* * Unfortunately, "file BINARY_FILE" doesn't work well if BINARY_FILE * was deleted (as often happens during system updates): @@ -188,18 +270,18 @@ static void GetBacktrace(const std::string& pDebugDumpDir, std::string& pBacktra * even if it is completely unrelated to the coredump * See https://bugzilla.redhat.com/show_bug.cgi?id=525721 */ - args[2] = (char*)"-ex"; - args[3] = xasprintf("file %s", executable.c_str()); args[4] = (char*)"-ex"; - args[5] = xasprintf("core-file %s/"FILENAME_COREDUMP, pDebugDumpDir.c_str()); + args[5] = xasprintf("file %s", executable.c_str()); args[6] = (char*)"-ex"; - args[7] = (char*)"thread apply all backtrace full"; - args[8] = NULL; + args[7] = xasprintf("core-file %s/"FILENAME_COREDUMP, pDebugDumpDir); + args[8] = (char*)"-ex"; + args[9] = (char*)"thread apply all backtrace full"; + args[10] = NULL; ExecVP(args, atoi(UID.c_str()), pBacktrace); - free(args[3]); free(args[5]); + free(args[7]); } static std::string GetIndependentBacktrace(const std::string& pBacktrace) @@ -353,7 +435,7 @@ static void GetIndependentBuildIdPC(const std::string& pBuildIdPC, std::string& } } -static std::string run_unstrip_n(const std::string& pDebugDumpDir) +static std::string run_unstrip_n(const char *pDebugDumpDir) { std::string UID; { @@ -364,7 +446,7 @@ static std::string run_unstrip_n(const std::string& pDebugDumpDir) char* args[4]; args[0] = (char*)"eu-unstrip"; - args[1] = xasprintf("--core=%s/"FILENAME_COREDUMP, pDebugDumpDir.c_str()); + args[1] = xasprintf("--core=%s/"FILENAME_COREDUMP, pDebugDumpDir); args[2] = (char*)"-n"; args[3] = NULL; @@ -376,7 +458,19 @@ static std::string run_unstrip_n(const std::string& pDebugDumpDir) return output; } -static void InstallDebugInfos(const std::string& pDebugDumpDir, std::string& build_ids) +#if 0 +/* older code */ +static bool is_hexstr(const char* str) +{ + while (*str) + { + if (!isxdigit(*str)) + return false; + str++; + } + return true; +} +static void InstallDebugInfos(const char *pDebugDumpDir, std::string& build_ids) { log("Getting module names, file names, build IDs from core file"); std::string unstrip_list = run_unstrip_n(pDebugDumpDir); @@ -531,8 +625,8 @@ Another application is holding the yum lock, cannot continue if (last >= 0 && buff[last] == '\n') buff[last] = '\0'; - /* log(buff); - update_client logs it too */ - update_client(buff); /* maybe only if buff != ""? */ + log("%s", buff); + update_client("%s", buff); /* maybe only if buff != ""? */ #ifdef COMPLAIN_IF_NO_DEBUGINFO if (already_installed == false) @@ -565,43 +659,44 @@ Another application is holding the yum lock, cannot continue fclose(pipeout_fp); wait(NULL); } -#if 0 -/* Needs gdb feature from here: https://bugzilla.redhat.com/show_bug.cgi?id=528668 */ -static void InstallDebugInfos(const std::string& pDebugDumpDir, std::string& build_ids) +#endif +/* Needs gdb feature from here: https://bugzilla.redhat.com/show_bug.cgi?id=528668 + * It is slated to be in F12/RHEL6. + */ +static void InstallDebugInfos(const char *pDebugDumpDir, std::string& build_ids) { update_client(_("Searching for debug-info packages...")); - int pipein[2], pipeout[2]; //TODO: get rid of pipein. Can we use ExecVP? - xpipe(pipein); + int pipeout[2]; //TODO: can we use ExecVP? xpipe(pipeout); pid_t child = fork(); if (child < 0) { - /*close(pipein[0]); close(pipeout[0]); - why bother */ - /*close(pipein[1]); close(pipeout[1]); */ + /*close(pipeout[0]); - why bother */ + /*close(pipeout[1]); */ perror_msg_and_die("fork"); } if (child == 0) { - close(pipein[1]); close(pipeout[0]); - xmove_fd(pipein[0], STDIN_FILENO); xmove_fd(pipeout[1], STDOUT_FILENO); + close(STDIN_FILENO); + xopen("/dev/null", O_RDONLY); /* Not a good idea, we won't see any error messages */ /*close(STDERR_FILENO);*/ setsid(); - char *coredump = xasprintf("%s/"FILENAME_COREDUMP, pDebugDumpDir.c_str()); - char *tempdir = xasprintf("/tmp/abrt-%u-%lu", (int)getpid(), (long)time(NULL)); + char *coredump = xasprintf("%s/"FILENAME_COREDUMP, pDebugDumpDir); + /* SELinux guys are not happy with /tmp, using /var/run/abrt */ + char *tempdir = xasprintf(LOCALSTATEDIR"/run/abrt/tmp-%u-%lu", (int)getpid(), (long)time(NULL)); /* log() goes to stderr/syslog, it's ok to use it here */ - VERB1 log("Executing: %s %s %s %s", "abrt-debuginfo-install", coredump, tempdir, "/var/cache/abrt-di"); - execlp("abrt-debuginfo-install", "abrt-debuginfo-install", coredump, tempdir, "/var/cache/abrt-di", NULL); + VERB1 log("Executing: %s %s %s %s", "abrt-debuginfo-install", coredump, tempdir, DEBUGINFO_CACHE_DIR); + execlp("abrt-debuginfo-install", "abrt-debuginfo-install", coredump, tempdir, DEBUGINFO_CACHE_DIR, NULL); exit(1); } - close(pipein[0]); close(pipeout[1]); update_client(_("Downloading and installing debug-info packages...")); @@ -635,17 +730,79 @@ static void InstallDebugInfos(const std::string& pDebugDumpDir, std::string& bui } if (*p) { - /* log(buff); - update_client logs it too */ - update_client(buff); + log("%s", buff); + update_client("%s", buff); } } fclose(pipeout_fp); wait(NULL); } -#endif -std::string CAnalyzerCCpp::GetLocalUUID(const std::string& pDebugDumpDir) +static double get_dir_size(const char *dirname, std::string *worst_file, double *maxsz) +{ + DIR *dp = opendir(dirname); + if (dp == NULL) + return 0; + + struct dirent *ep; + struct stat stats; + double size = 0; + while ((ep = readdir(dp)) != NULL) + { + if (dot_or_dotdot(ep->d_name)) + continue; + std::string dname = concat_path_file(dirname, ep->d_name); + if (lstat(dname.c_str(), &stats) != 0) + continue; + if (S_ISDIR(stats.st_mode)) + { + double sz = get_dir_size(dname.c_str(), worst_file, maxsz); + size += sz; + } + else if (S_ISREG(stats.st_mode)) + { + double sz = stats.st_size; + size += sz; + + if (worst_file) + { + /* Calculate "weighted" size and age + * w = sz_kbytes * age_mins */ + sz /= 1024; + long age = (time(NULL) - stats.st_mtime) / 60; + if (age > 0) + sz *= age; + + if (sz > *maxsz) + { + *maxsz = sz; + *worst_file = dname; + } + } + } + } + closedir(dp); + return size; +} + +static void trim_debuginfo_cache(unsigned max_mb) +{ + while (1) + { + std::string worst_file; + double maxsz = 0; + double cache_sz = get_dir_size(DEBUGINFO_CACHE_DIR, &worst_file, &maxsz); + if (cache_sz / (1024 * 1024) < max_mb) + break; + VERB1 log("%s is %.0f bytes (over %u MB), deleting '%s'", + DEBUGINFO_CACHE_DIR, cache_sz, max_mb, worst_file.c_str()); + if (unlink(worst_file.c_str()) != 0) + perror_msg("Can't unlink '%s'", worst_file.c_str()); + } +} + +std::string CAnalyzerCCpp::GetLocalUUID(const char *pDebugDumpDir) { log(_("Getting local universal unique identification...")); @@ -664,7 +821,7 @@ std::string CAnalyzerCCpp::GetLocalUUID(const std::string& pDebugDumpDir) return CreateHash(package + executable + independentBuildIdPC); } -std::string CAnalyzerCCpp::GetGlobalUUID(const std::string& pDebugDumpDir) +std::string CAnalyzerCCpp::GetGlobalUUID(const char *pDebugDumpDir) { log(_("Getting global universal unique identification...")); @@ -709,7 +866,7 @@ static bool DebuginfoCheckPolkit(int uid) return false; } -void CAnalyzerCCpp::CreateReport(const std::string& pDebugDumpDir, int force) +void CAnalyzerCCpp::CreateReport(const char *pDebugDumpDir, int force) { update_client(_("Starting report creation...")); @@ -734,10 +891,9 @@ void CAnalyzerCCpp::CreateReport(const std::string& pDebugDumpDir, int force) dd.Close(); /* do not keep dir locked longer than needed */ std::string build_ids; - map_plugin_settings_t settings = GetSettings(); - if (settings["InstallDebuginfo"] == "yes" && - DebuginfoCheckPolkit(atoi(UID.c_str())) ) - { + if (m_bInstallDebugInfo && DebuginfoCheckPolkit(atoi(UID.c_str()))) { + if (m_nDebugInfoCacheMB > 0) + trim_debuginfo_cache(m_nDebugInfoCacheMB); InstallDebugInfos(pDebugDumpDir, build_ids); } else @@ -748,12 +904,13 @@ void CAnalyzerCCpp::CreateReport(const std::string& pDebugDumpDir, int force) GetBacktrace(pDebugDumpDir, backtrace); dd.Open(pDebugDumpDir); - dd.SaveText(FILENAME_BACKTRACE, build_ids + backtrace); -log("BACKTRACE:'%s'", (build_ids + backtrace).c_str()); + dd.SaveText(FILENAME_BACKTRACE, (build_ids + backtrace).c_str()); if (m_bMemoryMap) { dd.SaveText(FILENAME_MEMORYMAP, "memory map of the crashed C/C++ application, not implemented yet"); } + dd.SaveText(FILENAME_RATING, to_string(rate_backtrace(backtrace.c_str())).c_str()); + dd.Close(); } void CAnalyzerCCpp::Init() @@ -805,8 +962,11 @@ void CAnalyzerCCpp::DeInit() void CAnalyzerCCpp::SetSettings(const map_plugin_settings_t& pSettings) { + m_pSettings = pSettings; + map_plugin_settings_t::const_iterator end = pSettings.end(); - map_plugin_settings_t::const_iterator it = pSettings.find("MemoryMap"); + map_plugin_settings_t::const_iterator it; + it = pSettings.find("MemoryMap"); if (it != end) { m_bMemoryMap = it->second == "yes"; @@ -816,22 +976,28 @@ void CAnalyzerCCpp::SetSettings(const map_plugin_settings_t& pSettings) { m_sDebugInfo = it->second; } - it = pSettings.find("InstallDebuginfo"); + it = pSettings.find("DebugInfoCacheMB"); if (it != end) { - m_bInstallDebuginfo = it->second == "yes"; + m_nDebugInfoCacheMB = atoi(it->second.c_str()); + } + it = pSettings.find("InstallDebugInfo"); + if (it == end) //compat, remove after 0.0.11 + it = pSettings.find("InstallDebuginfo"); + if (it != end) + { + m_bInstallDebugInfo = it->second == "yes"; } } -map_plugin_settings_t CAnalyzerCCpp::GetSettings() +const map_plugin_settings_t& CAnalyzerCCpp::GetSettings() { - map_plugin_settings_t ret; - - ret["MemoryMap"] = m_bMemoryMap ? "yes" : "no"; - ret["DebugInfo"] = m_sDebugInfo; - ret["InstallDebuginfo"] = m_bInstallDebuginfo ? "yes" : "no"; + m_pSettings["MemoryMap"] = m_bMemoryMap ? "yes" : "no"; + m_pSettings["DebugInfo"] = m_sDebugInfo; + m_pSettings["DebugInfoCacheMB"] = to_string(m_nDebugInfoCacheMB); + m_pSettings["InstallDebugInfo"] = m_bInstallDebugInfo ? "yes" : "no"; - return ret; + return m_pSettings; } PLUGIN_INFO(ANALYZER, diff --git a/lib/Plugins/CCpp.h b/lib/Plugins/CCpp.h index 3fbe0b9..e2abdec 100644 --- a/lib/Plugins/CCpp.h +++ b/lib/Plugins/CCpp.h @@ -31,18 +31,20 @@ class CAnalyzerCCpp : public CAnalyzer { private: bool m_bMemoryMap; - bool m_bInstallDebuginfo; + bool m_bInstallDebugInfo; + unsigned m_nDebugInfoCacheMB; std::string m_sOldCorePattern; std::string m_sDebugInfo; + public: CAnalyzerCCpp(); - virtual std::string GetLocalUUID(const std::string& pDebugDumpDir); - virtual std::string GetGlobalUUID(const std::string& pDebugDumpDir); - virtual void CreateReport(const std::string& pDebugDumpDir, int force); + virtual std::string GetLocalUUID(const char *pDebugDumpDir); + virtual std::string GetGlobalUUID(const char *pDebugDumpDir); + virtual void CreateReport(const char *pDebugDumpDir, int force); virtual void Init(); virtual void DeInit(); virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual map_plugin_settings_t GetSettings(); + virtual const map_plugin_settings_t& GetSettings(); }; #endif /* CCPP */ diff --git a/lib/Plugins/Catcut.GTKBuilder b/lib/Plugins/Catcut.GTKBuilder new file mode 100644 index 0000000..b8c7c31 --- /dev/null +++ b/lib/Plugins/Catcut.GTKBuilder @@ -0,0 +1,184 @@ +<?xml version="1.0"?> +<interface> + <requires lib="gtk+" version="2.16"/> + <!-- interface-naming-policy project-wide --> + <object class="GtkDialog" id="PluginDialog"> + <property name="border_width">5</property> + <property name="modal">True</property> + <property name="window_position">center-on-parent</property> + <property name="type_hint">normal</property> + <property name="has_separator">False</property> + <child internal-child="vbox"> + <object class="GtkVBox" id="dialog-vbox3"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">2</property> + <child> + <object class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Catcut plugin configuration</property> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="n_rows">4</property> + <property name="n_columns">2</property> + <child> + <object class="GtkLabel" id="lCatcutURL"> + <property name="visible">True</property> + <property name="label" translatable="yes">Catcut URL</property> + </object> + </child> + <child> + <object class="GtkLabel" id="lLogin"> + <property name="visible">True</property> + <property name="label" translatable="yes">Login</property> + </object> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="lPassword"> + <property name="visible">True</property> + <property name="label" translatable="yes">Password</property> + </object> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_CatcutURL"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_Login"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_Password"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="visibility">False</property> + <property name="invisible_char">●</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="cb_Password"> + <property name="label" translatable="yes">Show password</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + </packing> + </child> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="conf_NoSSLVerify"> + <property name="label" translatable="yes">No SSL verify</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <object class="GtkHButtonBox" id="dialog-action_area3"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="bApply"> + <property name="label">gtk-apply</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="button2"> + <property name="label">gtk-cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-10">bApply</action-widget> + <action-widget response="-6">button2</action-widget> + </action-widgets> + </object> +</interface> diff --git a/lib/Plugins/Catcut.conf b/lib/Plugins/Catcut.conf new file mode 100644 index 0000000..456d7f8 --- /dev/null +++ b/lib/Plugins/Catcut.conf @@ -0,0 +1,8 @@ +# Catcut URL +CatcutURL = http://127.0.0.1:8080/catcut/xmlrpc +# yes means that ssl certificates will not be checked +NoSSLVerify = no +# your login has to exist, if you don have any, please create one +Login = gavin +# your password +Password = junk diff --git a/lib/Plugins/Catcut.cpp b/lib/Plugins/Catcut.cpp new file mode 100644 index 0000000..13fa8a4 --- /dev/null +++ b/lib/Plugins/Catcut.cpp @@ -0,0 +1,368 @@ +#include <xmlrpc-c/base.h> +#include <xmlrpc-c/client.h> + +#include "abrtlib.h" +#include "Catcut.h" +#include "CrashTypes.h" +#include "DebugDump.h" +#include "ABRTException.h" +#include "CommLayerInner.h" +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +using namespace std; + +static xmlrpc_env env; +static xmlrpc_client* client = NULL; +static struct xmlrpc_clientparms clientParms; +static struct xmlrpc_curl_xportparms curlParms; +static xmlrpc_server_info* server_info = NULL; + + +static string login(const char* login, const char* passwd); +//static void logout(); +static void new_xmlrpc_client(const char* url, bool no_ssl_verify); +static void destroy_xmlrpc_client(); +static void create_new_bug_description(const map_crash_report_t& pCrashReport, string& pDescription); +static void get_product_and_version(const string& pRelease, + string& pProduct, + string& pVersion); + + +static void throw_if_xml_fault_occurred() +{ + if (env.fault_occurred) + { + string errmsg = ssprintf("XML-RPC Fault: %s(%d)", env.fault_string, env.fault_code); + error_msg("%s", errmsg.c_str()); // show error in daemon log + throw CABRTException(EXCEP_PLUGIN, errmsg); + } +} + +static void new_xmlrpc_client(const char* url, bool no_ssl_verify) +{ + xmlrpc_env_init(&env); + + /* This should be done at program startup, once. + * We do it in abrtd's main */ + /* xmlrpc_client_setup_global_const(&env); */ + + curlParms.network_interface = NULL; + curlParms.no_ssl_verifypeer = no_ssl_verify; + curlParms.no_ssl_verifyhost = no_ssl_verify; +#ifdef VERSION + curlParms.user_agent = PACKAGE_NAME"/"VERSION; +#else + curlParms.user_agent = "abrt"; +#endif + + clientParms.transport = "curl"; + clientParms.transportparmsP = &curlParms; + clientParms.transportparm_size = XMLRPC_CXPSIZE(user_agent); + + xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS, PACKAGE_NAME, VERSION, &clientParms, XMLRPC_CPSIZE(transportparm_size), + &client); + throw_if_xml_fault_occurred(); + + server_info = xmlrpc_server_info_new(&env, url); + throw_if_xml_fault_occurred(); +} + +static void destroy_xmlrpc_client() +{ + xmlrpc_server_info_free(server_info); + xmlrpc_env_clean(&env); + xmlrpc_client_destroy(client); +} + +static string login(const char* login, const char* passwd) +{ + xmlrpc_value* param = xmlrpc_build_value(&env, "(ss)", login, passwd); + throw_if_xml_fault_occurred(); + + xmlrpc_value* result; + xmlrpc_client_call2(&env, client, server_info, "Catcut.auth", param, &result); + throw_if_xml_fault_occurred(); + xmlrpc_DECREF(param); + + xmlrpc_value *cookie_xml; + const char *cookie; + string cookie_str; + xmlrpc_struct_find_value(&env, result, "cookie", &cookie_xml); + throw_if_xml_fault_occurred(); + xmlrpc_read_string(&env, cookie_xml, &cookie); + throw_if_xml_fault_occurred(); + cookie_str = cookie; + /* xmlrpc_read_string returns *malloc'ed ptr*. + * doc is not very clear on it, but I looked in xmlrpc sources. */ + free((void*)cookie); + xmlrpc_DECREF(cookie_xml); + + xmlrpc_DECREF(result); + + return cookie_str; +} + +// catcut does not have it (yet?) +//static void logout() +//{ +// xmlrpc_value* param = xmlrpc_build_value(&env, "(s)", ""); +// throw_if_xml_fault_occurred(); +// +// xmlrpc_value* result = NULL; /* paranoia */ +// xmlrpc_client_call2(&env, client, server_info, "User.logout", param, &result); +// throw_if_xml_fault_occurred(); +//} + +static void create_new_bug_description(const map_crash_report_t& pCrashReport, string& pDescription) +{ + string howToReproduce; + string comment; + + if (pCrashReport.find(CD_REPRODUCE) != pCrashReport.end()) + { + howToReproduce = "\n\nHow to reproduce\n" + "-----\n" + + pCrashReport.find(CD_REPRODUCE)->second[CD_CONTENT]; + } + if (pCrashReport.find(CD_COMMENT) != pCrashReport.end()) + { + comment = "\n\nComment\n" + "-----\n" + + pCrashReport.find(CD_COMMENT)->second[CD_CONTENT]; + } + pDescription = "\nabrt detected a crash.\n" + + howToReproduce + + comment + + "\n\nAdditional information\n" + "======\n"; + + map_crash_report_t::const_iterator it; + for (it = pCrashReport.begin(); it != pCrashReport.end(); it++) + { + if (it->second[CD_TYPE] == CD_TXT) + { + if (it->first != CD_UUID && + it->first != FILENAME_ARCHITECTURE && + it->first != FILENAME_RELEASE && + it->first != CD_REPRODUCE && + it->first != CD_COMMENT) + { + pDescription += "\n" + it->first + "\n"; + pDescription += "-----\n"; + pDescription += it->second[CD_CONTENT] + "\n\n"; + } + } + else if (it->second[CD_TYPE] == CD_ATT) + { + pDescription += "\n\nAttached files\n" + "----\n"; + pDescription += it->first + "\n"; + } + else if (it->second[CD_TYPE] == CD_BIN) + { + error_msg(_("Binary file %s will not be reported"), it->first.c_str()); + } + } +} + +static void get_product_and_version(const string& pRelease, + string& pProduct, + string& pVersion) +{ + if (pRelease.find("Rawhide") != string::npos) + { + pProduct = "Fedora"; + pVersion = "rawhide"; + return; + } + if (pRelease.find("Fedora") != string::npos) + { + pProduct = "Fedora"; + } + else if (pRelease.find("Red Hat Enterprise Linux") != string::npos) + { + pProduct = "Red Hat Enterprise Linux "; + } + string::size_type pos = pRelease.find("release"); + pos = pRelease.find(" ", pos) + 1; + while (pRelease[pos] != ' ') + { + pVersion += pRelease[pos]; + if (pProduct == "Red Hat Enterprise Linux ") + { + pProduct += pRelease[pos]; + } + pos++; + } +} + +static string new_bug(const char *auth_cookie, const map_crash_report_t& pCrashReport) +{ + string package = pCrashReport.find(FILENAME_PACKAGE)->second[CD_CONTENT]; + string component = pCrashReport.find(FILENAME_COMPONENT)->second[CD_CONTENT]; + string release = pCrashReport.find(FILENAME_RELEASE)->second[CD_CONTENT]; + string arch = pCrashReport.find(FILENAME_ARCHITECTURE)->second[CD_CONTENT]; + string uuid = pCrashReport.find(CD_UUID)->second[CD_CONTENT]; + + string summary = "[abrt] crash detected in " + package; + string status_whiteboard = "abrt_hash:" + uuid; + + string description; + create_new_bug_description(pCrashReport, description); + + string product; + string version; + get_product_and_version(release, product, version); + + xmlrpc_value *param = xmlrpc_build_value(&env, "(s{s:s,s:s,s:s,s:s,s:s,s:s,s:s})", + auth_cookie, + "product", product.c_str(), + "component", component.c_str(), + "version", version.c_str(), + "summary", summary.c_str(), + "description", description.c_str(), + "status_whiteboard", status_whiteboard.c_str(), + "platform", arch.c_str() + ); + throw_if_xml_fault_occurred(); + + xmlrpc_value *result; + xmlrpc_client_call2(&env, client, server_info, "Catcut.createTicket", param, &result); + throw_if_xml_fault_occurred(); + xmlrpc_DECREF(param); + + xmlrpc_value *bug_id_xml; + const char *bug_id; + string bug_id_str; + xmlrpc_struct_find_value(&env, result, "ticket", &bug_id_xml); + throw_if_xml_fault_occurred(); + xmlrpc_read_string(&env, bug_id_xml, &bug_id); + throw_if_xml_fault_occurred(); + bug_id_str = bug_id; + log("New bug id: %s", bug_id); + update_client(_("New bug id: %s"), bug_id); + free((void*)bug_id); + xmlrpc_DECREF(bug_id_xml); + + xmlrpc_DECREF(result); + + return bug_id_str; +} + +//static +//void add_attachments(const string& pBugId, const map_crash_report_t& pCrashReport) +//{ +// xmlrpc_value* result = NULL; +// +// map_crash_report_t::const_iterator it = pCrashReport.begin(); +// for (; it != pCrashReport.end(); it++) +// { +// if (it->second[CD_TYPE] == CD_ATT) +// { +// string description = "File: " + it->first; +// const string& to_encode = it->second[CD_CONTENT]; +// char *encoded64 = encode_base64(to_encode.c_str(), to_encode.length()); +// xmlrpc_value* param = xmlrpc_build_value(&env,"(s{s:s,s:s,s:s,s:s})", +// pBugId.c_str(), +// "description", description.c_str(), +// "filename", it->first.c_str(), +// "contenttype", "text/plain", +// "data", encoded64 +// ); +// free(encoded64); +// throw_if_xml_fault_occurred(); +// +//// catcut has this API: +//// struct response requestUpload(string cookie, string ticket, string filename, string description) +////response MUST include "errno", "errmsg" members; if an upload is approved, +////a "URL" MUST be returned in the response. The description string +////should include a brief description of the file. +//// +////The client should upload the file via HTTP PUT to the provided +////URL. The provided URL may be absolute or relative, if relative it must +////be combined with the base URL of the XML-RPC server using the usual +////rules for relative URL's (RFC 3986). +// xmlrpc_client_call2(&env, client, server_info, "catcut.addAttachment", param, &result); +// throw_if_xml_fault_occurred(); +// } +// } +//} + +CReporterCatcut::CReporterCatcut() : + m_sCatcutURL("http://127.0.0.1:8080/catcut/xmlrpc"), + m_bNoSSLVerify(false) +{} + +CReporterCatcut::~CReporterCatcut() +{} + +string CReporterCatcut::Report(const map_crash_report_t& pCrashReport, + const map_plugin_settings_t& pSettings, const string& pArgs) +{ + update_client(_("Creating new bug...")); + try + { + new_xmlrpc_client(m_sCatcutURL.c_str(), m_bNoSSLVerify); + string auth_cookie = login(m_sLogin.c_str(), m_sPassword.c_str()); + string bug_id = (auth_cookie != "") ? new_bug(auth_cookie.c_str(), pCrashReport) : ""; +// add_attachments(to_string(bug_id), pCrashReport); +// update_client(_("Logging out...")); +// logout(); + destroy_xmlrpc_client(); + return "New catcut bug ID: " + bug_id; + + } + catch (CABRTException& e) + { + destroy_xmlrpc_client(); + throw CABRTException(EXCEP_PLUGIN, string("CReporterCatcut::Report(): ") + e.what()); + } +} + +void CReporterCatcut::SetSettings(const map_plugin_settings_t& pSettings) +{ + map_plugin_settings_t::const_iterator it; + map_plugin_settings_t::const_iterator end = pSettings.end(); + + it = pSettings.find("CatcutURL"); + if (it != end) + { + m_sCatcutURL = it->second; + } + it = pSettings.find("Login"); + if (it != end) + { + m_sLogin = it->second; + } + it = pSettings.find("Password"); + if (it != end) + { + m_sPassword = it->second; + } + it = pSettings.find("NoSSLVerify"); + if (it != end) + { + m_bNoSSLVerify = it->second == "yes"; + } +} + +const map_plugin_settings_t& CReporterCatcut::GetSettings() +{ + m_pSettings["CatcutURL"] = m_sCatcutURL; + m_pSettings["Login"] = m_sLogin; + m_pSettings["Password"] = m_sPassword; + m_pSettings["NoSSLVerify"] = m_bNoSSLVerify ? "yes" : "no"; + + return m_pSettings; +} + +PLUGIN_INFO(REPORTER, + CReporterCatcut, + "Catcut", + "0.0.1", + "Test plugin to report bugs to catcut and if not, report it.", + "dvlasenk@redhat.com", + "https://fedorahosted.org/abrt/wiki", + PLUGINS_LIB_DIR"/Catcut.GTKBuilder"); diff --git a/lib/Plugins/Catcut.h b/lib/Plugins/Catcut.h new file mode 100644 index 0000000..15efdc1 --- /dev/null +++ b/lib/Plugins/Catcut.h @@ -0,0 +1,27 @@ +#ifndef CATCUT_H_ +#define CATCUT_H_ + +#include "Plugin.h" +#include "Reporter.h" + +class CReporterCatcut : public CReporter +{ + private: + std::string m_sCatcutURL; + std::string m_sLogin; + std::string m_sPassword; + bool m_bNoSSLVerify; + + public: + CReporterCatcut(); + virtual ~CReporterCatcut(); + + virtual void SetSettings(const map_plugin_settings_t& pSettings); + virtual const map_plugin_settings_t& GetSettings(); + + virtual std::string Report(const map_crash_report_t& pCrashReport, + const map_plugin_settings_t& pSettings, + const std::string& pArgs); +}; + +#endif /* CATCUT_H_ */ diff --git a/lib/Plugins/FileTransfer.cpp b/lib/Plugins/FileTransfer.cpp index 3492156..72b3b16 100644 --- a/lib/Plugins/FileTransfer.cpp +++ b/lib/Plugins/FileTransfer.cpp @@ -42,8 +42,8 @@ #include "ABRTException.h" #include "CommLayerInner.h" - using namespace std; + #define HBLEN 255 #define FILETRANSFER_DIRLIST DEBUG_DUMPS_DIR "/FileTransferDirlist.txt" @@ -55,75 +55,51 @@ CFileTransfer::CFileTransfer() { } -void CFileTransfer::SendFile(const std::string& pURL, - const std::string& pFilename) +void CFileTransfer::SendFile(const char *pURL, const char *pFilename) { - if (pURL == "") + int len = strlen(pURL); + if (len == 0) { - warn_client(_("FileTransfer: URL not specified")); + error_msg(_("FileTransfer: URL not specified")); return; } - int len = pURL.length(); - int i = 0; - std::string protocol; - while (pURL[i] != ':') - { - protocol += pURL[i]; - i++; - if (i == len) - { - throw CABRTException(EXCEP_PLUGIN, "CFileTransfer::SendFile(): malformed URL, does not contain protocol"); - } - } + update_client(_("Sending archive %s to %s"), pFilename, pURL); - char buffer[1024]; - snprintf(buffer, 1024, _("Sending archive %s via %s"), pFilename.c_str(), protocol.c_str()); - update_client(buffer); - - std::string wholeURL; - if (pURL[len-1] == '/') - { - wholeURL = pURL + pFilename; - } - else - { - wholeURL = pURL + "/" + pFilename; - } + string wholeURL = concat_path_file(pURL, pFilename); int result; int count = m_nRetryCount; do { - FILE * f; + FILE *f; struct stat buf; - CURL * curl; + CURL *curl; - f = fopen(pFilename.c_str(), "r"); + f = fopen(pFilename, "r"); if (!f) { - throw CABRTException(EXCEP_PLUGIN, "CFileTransfer::SendFile(): cannot open archive file "+pFilename); + throw CABRTException(EXCEP_PLUGIN, ssprintf("Can't open archive file '%s'", pFilename)); } if (fstat(fileno(f), &buf) == -1) { - throw CABRTException(EXCEP_PLUGIN, "CFileTransfer::SendFile(): cannot stat archive file "+pFilename); + fclose(f); + throw CABRTException(EXCEP_PLUGIN, ssprintf("Can't stat archive file '%s'", pFilename)); } curl = curl_easy_init(); if (!curl) { - throw CABRTException(EXCEP_PLUGIN, "CFileTransfer::SendFile(): Curl library error."); + throw CABRTException(EXCEP_PLUGIN, "Curl library init error"); } /* enable uploading */ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); /* specify target */ curl_easy_setopt(curl, CURLOPT_URL, wholeURL.c_str()); - /*file handle: passed to the default callback, it will fread() it*/ + /* FILE handle: passed to the default callback, it will fread() it */ curl_easy_setopt(curl, CURLOPT_READDATA, f); - /*get file size*/ curl_easy_setopt(curl, CURLOPT_INFILESIZE, buf.st_size); - /*everything is done here; result 0 means success*/ + /* everything is done here; result 0 means success */ result = curl_easy_perform(curl); - /*goodbye*/ curl_easy_cleanup(curl); fclose(f); } @@ -223,7 +199,7 @@ static void create_targz(const char * archive_name, const char * directory) if (gz == NULL) { fclose(f); - free(name_without_gz); + free(name_without_gz); return; } @@ -286,84 +262,81 @@ static void create_tarbz2(const char * archive_name, const char * directory) free(name_without_bz2); } -void CFileTransfer::CreateArchive(const std::string& pArchiveName, - const std::string& pDir) +void CFileTransfer::CreateArchive(const char *pArchiveName, const char *pDir) { if (m_sArchiveType == ".tar") { - create_tar(pArchiveName.c_str(), pDir.c_str()); + create_tar(pArchiveName, pDir); } else if (m_sArchiveType == ".tar.gz") { - create_targz(pArchiveName.c_str(), pDir.c_str()); + create_targz(pArchiveName, pDir); } else if (m_sArchiveType == ".tar.bz2") { - create_tarbz2(pArchiveName.c_str(), pDir.c_str()); + create_tarbz2(pArchiveName, pDir); } else if (m_sArchiveType == ".zip") { - create_zip(pArchiveName.c_str(), pDir.c_str()); + create_zip(pArchiveName, pDir); } else { - throw CABRTException(EXCEP_PLUGIN, "CFileTransfer::CreateArchive(): unknown/unsupported archive type "+m_sArchiveType); + throw CABRTException(EXCEP_PLUGIN, "Unknown/unsupported archive type " + m_sArchiveType); } } -/*returns the last component of the directory path*/ -static std::string DirBase(const std::string& pStr) +/* Returns the last component of the directory path. + * Careful to not return "" on "/path/path2/", but "path2". + */ +static string DirBase(const char *pStr) { - int i = pStr.length() - 1; - if (i > 0 && pStr[i] == '/') + int end = strlen(pStr); + if (end > 1 && pStr[end-1] == '/') { - i--; + end--; } - std::string result; - for (; i >= 0 && pStr[i] != '/'; i--) + int beg = end; + while (beg > 0 && pStr[beg-1] != '/') { - result = pStr[i] + result; + beg--; } - return result; + return string(pStr + beg, end - beg); } -void CFileTransfer::Run(const std::string& pActiveDir, const std::string& pArgs) +void CFileTransfer::Run(const char *pActionDir, const char *pArgs) { - fstream dirlist; - std::string dirname, archivename; - char hostname[HBLEN]; - update_client(_("File Transfer: Creating a report...")); - if (pArgs == "store") + char hostname[HBLEN]; + gethostname(hostname, HBLEN-1); + hostname[HBLEN-1] = '\0'; + + fstream dirlist; + if (strcmp(pArgs, "store") == 0) { /* store pActiveDir for later sending */ - dirlist.open(FILETRANSFER_DIRLIST, fstream::out | fstream::app ); - dirlist << pActiveDir << endl; + dirlist.open(FILETRANSFER_DIRLIST, fstream::out | fstream::app); + dirlist << pActionDir << endl; dirlist.close(); } - else if (pArgs == "one") + else if (strcmp(pArgs, "one") == 0) { /* just send one archive */ - gethostname(hostname, HBLEN); - archivename = std::string(hostname) + "-" - + DirBase(pActiveDir) + m_sArchiveType; + string archivename = ssprintf("%s-%s%s", hostname, DirBase(pActionDir).c_str(), m_sArchiveType.c_str()); try { - CreateArchive(archivename, pActiveDir); - SendFile(m_sURL, archivename); + CreateArchive(archivename.c_str(), pActionDir); + SendFile(m_sURL.c_str(), archivename.c_str()); } catch (CABRTException& e) { - warn_client(_("CFileTransfer::Run(): Cannot create and send an archive: ") + e.what()); - //update_client("CFileTransfer::Run(): Cannot create and send an archive: " + e.what()); + error_msg(_("Can't create and send an archive: %s"), e.what()); } unlink(archivename.c_str()); } else { - gethostname(hostname, HBLEN); - dirlist.open(FILETRANSFER_DIRLIST, fstream::in); if (dirlist.fail()) { @@ -372,19 +345,18 @@ void CFileTransfer::Run(const std::string& pActiveDir, const std::string& pArgs) return; } + string dirname; while (getline(dirlist, dirname), dirlist.good()) { - archivename = std::string(hostname) + "-" - + DirBase(dirname) + m_sArchiveType; + string archivename = ssprintf("%s-%s%s", hostname, DirBase(dirname.c_str()).c_str(), m_sArchiveType.c_str()); try { - CreateArchive(archivename, dirname); - SendFile(m_sURL, archivename); + CreateArchive(archivename.c_str(), dirname.c_str()); + SendFile(m_sURL.c_str(), archivename.c_str()); } catch (CABRTException& e) { - warn_client(_("CFileTransfer::Run(): Cannot create and send an archive: ") + e.what()); -// update_client("CFileTransfer::Run(): Cannot create and send an archive: " + e.what()); + error_msg(_("Can't create and send an archive %s"), e.what()); } unlink(archivename.c_str()); } @@ -406,7 +378,7 @@ void CFileTransfer::SetSettings(const map_plugin_settings_t& pSettings) } else { - warn_client(_("FileTransfer: URL not specified")); + error_msg(_("FileTransfer: URL not specified")); } it = pSettings.find("RetryCount"); @@ -428,24 +400,19 @@ void CFileTransfer::SetSettings(const map_plugin_settings_t& pSettings) m_sArchiveType = it->second; if (m_sArchiveType[0] != '.') { - m_sArchiveType = "." + m_sArchiveType; + m_sArchiveType = "." + m_sArchiveType; } } } -map_plugin_settings_t CFileTransfer::GetSettings() +const map_plugin_settings_t& CFileTransfer::GetSettings() { - map_plugin_settings_t ret; - std::stringstream ss; - ret["URL"] = m_sURL; - ss << m_nRetryCount; - ret["RetryCount"] = ss.str(); - ss.str(""); - ss << m_nRetryDelay; - ret["RetryDelay"] = ss.str(); - ret["ArchiveType"] = m_sArchiveType; - - return ret; + m_pSettings["URL"] = m_sURL; + m_pSettings["RetryCount"] = to_string(m_nRetryCount); + m_pSettings["RetryDelay"] = to_string(m_nRetryDelay); + m_pSettings["ArchiveType"] = m_sArchiveType; + + return m_pSettings; } PLUGIN_INFO(ACTION, diff --git a/lib/Plugins/FileTransfer.h b/lib/Plugins/FileTransfer.h index 2f230c6..9caa256 100644 --- a/lib/Plugins/FileTransfer.h +++ b/lib/Plugins/FileTransfer.h @@ -35,18 +35,14 @@ class CFileTransfer : public CAction int m_nRetryCount; int m_nRetryDelay; - void CreateArchive(const std::string& pArchiveName, - const std::string& pDir); - - void SendFile(const std::string& pURL, - const std::string& pFilename); + void CreateArchive(const char *pArchiveName, const char *pDir); + void SendFile(const char *pURL, const char *pFilename); public: CFileTransfer(); virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual map_plugin_settings_t GetSettings(); - virtual void Run(const std::string& pActiveDir, - const std::string& pArgs); + virtual const map_plugin_settings_t& GetSettings(); + virtual void Run(const char *pActionDir, const char *pArgs); }; #endif /* FILETRANSFER_H_ */ diff --git a/lib/Plugins/Kerneloops.conf b/lib/Plugins/Kerneloops.conf new file mode 100644 index 0000000..47b242f --- /dev/null +++ b/lib/Plugins/Kerneloops.conf @@ -0,0 +1,2 @@ +# compatibility with kerneloops.org tool +InformAllUsers = yes diff --git a/lib/Plugins/Kerneloops.cpp b/lib/Plugins/Kerneloops.cpp index e01bb42..b99183c 100644 --- a/lib/Plugins/Kerneloops.cpp +++ b/lib/Plugins/Kerneloops.cpp @@ -32,7 +32,7 @@ #define FILENAME_KERNELOOPS "kerneloops" -std::string CAnalyzerKerneloops::GetLocalUUID(const std::string& pDebugDumpDir) +std::string CAnalyzerKerneloops::GetLocalUUID(const char *pDebugDumpDir) { log(_("Getting local universal unique identification")); @@ -58,7 +58,7 @@ std::string CAnalyzerKerneloops::GetLocalUUID(const std::string& pDebugDumpDir) return to_string(hash); } -std::string CAnalyzerKerneloops::GetGlobalUUID(const std::string& pDebugDumpDir) +std::string CAnalyzerKerneloops::GetGlobalUUID(const char *pDebugDumpDir) { return GetLocalUUID(pDebugDumpDir); } diff --git a/lib/Plugins/Kerneloops.h b/lib/Plugins/Kerneloops.h index 13fb009..9e2010c 100644 --- a/lib/Plugins/Kerneloops.h +++ b/lib/Plugins/Kerneloops.h @@ -29,17 +29,14 @@ #include "Plugin.h" #include "Analyzer.h" - #include <string> -#include "KerneloopsSysLog.h" - class CAnalyzerKerneloops : public CAnalyzer { public: - virtual std::string GetLocalUUID(const std::string& pDebugDumpDir); - virtual std::string GetGlobalUUID(const std::string& pDebugDumpDir); - virtual void CreateReport(const std::string& pDebugDumpDir, int force) {} + virtual std::string GetLocalUUID(const char *pDebugDumpDir); + virtual std::string GetGlobalUUID(const char *pDebugDumpDir); + virtual void CreateReport(const char *pDebugDumpDir, int force) {} }; #endif diff --git a/lib/Plugins/KerneloopsReporter.cpp b/lib/Plugins/KerneloopsReporter.cpp index e9967fd..cfb4e05 100644 --- a/lib/Plugins/KerneloopsReporter.cpp +++ b/lib/Plugins/KerneloopsReporter.cpp @@ -93,15 +93,15 @@ CKerneloopsReporter::CKerneloopsReporter() : m_sSubmitURL("http://submit.kerneloops.org/submitoops.php") {} -std::string CKerneloopsReporter::Report(const map_crash_report_t& pCrashReport, const std::string& pArgs) +std::string CKerneloopsReporter::Report(const map_crash_report_t& pCrashReport, + const map_plugin_settings_t& pSettings, + const std::string& pArgs) { int ret = -1; - map_crash_report_t::const_iterator it; update_client(_("Creating and submitting a report...")); - it = pCrashReport.begin(); - it = pCrashReport.find(FILENAME_KERNELOOPS); + map_crash_report_t::const_iterator it = pCrashReport.find(FILENAME_KERNELOOPS); if (it != pCrashReport.end()) { ret = http_post_to_kerneloops_site( m_sSubmitURL.c_str(), @@ -109,29 +109,26 @@ std::string CKerneloopsReporter::Report(const map_crash_report_t& pCrashReport, ); } - if (ret) - { + if (ret) { /* FIXME: be more informative */ - throw CABRTException(EXCEP_PLUGIN, std::string("CKerneloopsReporter::Report(): Report has not been sent...")); + throw CABRTException(EXCEP_PLUGIN, std::string("CKerneloopsReporter::Report(): Report has not been sent...")); } - return "Kernel oops report was uploaded to :" + m_sSubmitURL; + return "Kernel oops report was uploaded to: " + m_sSubmitURL; } void CKerneloopsReporter::SetSettings(const map_plugin_settings_t& pSettings) { - if (pSettings.find("SubmitURL") != pSettings.end()) - { - m_sSubmitURL = pSettings.find("SubmitURL")->second; + map_plugin_settings_t::const_iterator it = pSettings.find("SubmitURL"); + if (it != pSettings.end()) { + m_sSubmitURL = it->second; } } -map_plugin_settings_t CKerneloopsReporter::GetSettings() +const map_plugin_settings_t& CKerneloopsReporter::GetSettings() { - map_plugin_settings_t ret; + m_pSettings["SubmitURL"] = m_sSubmitURL; - ret["SubmitURL"] = m_sSubmitURL; - - return ret; + return m_pSettings; } PLUGIN_INFO(REPORTER, diff --git a/lib/Plugins/KerneloopsReporter.h b/lib/Plugins/KerneloopsReporter.h index f1b427f..7f6ab8c 100644 --- a/lib/Plugins/KerneloopsReporter.h +++ b/lib/Plugins/KerneloopsReporter.h @@ -41,8 +41,9 @@ class CKerneloopsReporter : public CReporter CKerneloopsReporter(); virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual map_plugin_settings_t GetSettings(); + virtual const map_plugin_settings_t& GetSettings(); virtual std::string Report(const map_crash_report_t& pCrashReport, + const map_plugin_settings_t& pSettings, const std::string& pArgs); }; diff --git a/lib/Plugins/KerneloopsScanner.cpp b/lib/Plugins/KerneloopsScanner.cpp index 60fa92e..8c8cd4b 100644 --- a/lib/Plugins/KerneloopsScanner.cpp +++ b/lib/Plugins/KerneloopsScanner.cpp @@ -31,8 +31,10 @@ #include "DebugDump.h" #include "ABRTException.h" #include "CommLayerInner.h" +#include "KerneloopsSysLog.h" #include "KerneloopsScanner.h" +#include <limits.h> #define FILENAME_KERNELOOPS "kerneloops" @@ -41,7 +43,6 @@ CKerneloopsScanner::CKerneloopsScanner() { int cnt_FoundOopses; - m_sSysLogFile = "/var/log/messages"; /* Scan dmesg, on first call only */ cnt_FoundOopses = ScanDmesg(); @@ -49,19 +50,23 @@ CKerneloopsScanner::CKerneloopsScanner() SaveOopsToDebugDump(); } -void CKerneloopsScanner::Run(const std::string& pActionDir, - const std::string& pArgs) +void CKerneloopsScanner::Run(const char *pActionDir, const char *pArgs) { - int cnt_FoundOopses; + const char *syslog_file = "/var/log/messages"; + map_plugin_settings_t::const_iterator it = m_pSettings.find("SysLogFile"); + if (it != m_pSettings.end()) + { + syslog_file = it->second.c_str(); + } - cnt_FoundOopses = ScanSysLogFile(m_sSysLogFile.c_str()); + int cnt_FoundOopses = ScanSysLogFile(syslog_file); if (cnt_FoundOopses > 0) { SaveOopsToDebugDump(); /* * This marker in syslog file prevents us from * re-parsing old oopses (any oops before it is * ignored by ScanSysLogFile()). The only problem - * is that we can't be sure here that m_sSysLogFile + * is that we can't be sure here that syslog_file * is the file where syslog(xxx) stuff ends up. */ openlog("abrt", 0, LOG_KERN); @@ -78,26 +83,29 @@ void CKerneloopsScanner::SaveOopsToDebugDump() { update_client(_("Creating kernel oops crash reports...")); + int countdown = 16; /* do not report hundreds of oopses */ time_t t = time(NULL); - std::list<COops> oopsList = m_pSysLog.GetOopsList(); - m_pSysLog.ClearOopsList(); - - while (!oopsList.empty()) { - char path[PATH_MAX]; - snprintf(path, sizeof(path), "%s/kerneloops-%lu-%lu", - DEBUG_DUMPS_DIR, (long)t, (long)oopsList.size()); - - COops oops = oopsList.back(); - + vector_string_t oopsList = m_pOopsList; + m_pOopsList.clear(); + + while (!oopsList.empty() && --countdown != 0) { + char path[sizeof(DEBUG_DUMPS_DIR"/kerneloops-%lu-%lu") + 2 * sizeof(long)*3]; + sprintf(path, DEBUG_DUMPS_DIR"/kerneloops-%lu-%lu", + (long)t, (long)oopsList.size()); + + std::string oops = oopsList.back(); + const char *first_line = oops.c_str(); + char *second_line = (char*)strchr(first_line, '\n'); /* never NULL */ + *second_line++ = '\0'; try { CDebugDump debugDump; debugDump.Create(path, 0); debugDump.SaveText(FILENAME_ANALYZER, "Kerneloops"); debugDump.SaveText(FILENAME_EXECUTABLE, "kernel"); - debugDump.SaveText(FILENAME_KERNEL, oops.m_sVersion); + debugDump.SaveText(FILENAME_KERNEL, first_line); debugDump.SaveText(FILENAME_PACKAGE, "not_applicable"); - debugDump.SaveText(FILENAME_KERNELOOPS, oops.m_sData); + debugDump.SaveText(FILENAME_KERNELOOPS, second_line); } catch (CABRTException& e) { @@ -118,7 +126,8 @@ int CKerneloopsScanner::ScanDmesg() buffer = (char*)xzalloc(pagesz + 1); syscall(__NR_syslog, 3, buffer, pagesz); - cnt_FoundOopses = m_pSysLog.ExtractOops(buffer, strlen(buffer)); + m_pOopsList.clear(); + cnt_FoundOopses = extract_oopses(m_pOopsList, buffer, strlen(buffer)); free(buffer); return cnt_FoundOopses; @@ -162,30 +171,15 @@ int CKerneloopsScanner::ScanSysLogFile(const char *filename) close(fd); cnt_FoundOopses = 0; - if (sz > 0) - cnt_FoundOopses = m_pSysLog.ExtractOops(buffer, sz); + if (sz > 0) { + m_pOopsList.clear(); + cnt_FoundOopses = extract_oopses(m_pOopsList, buffer, sz); + } free(buffer); return cnt_FoundOopses; } -void CKerneloopsScanner::SetSettings(const map_plugin_settings_t& pSettings) -{ - if (pSettings.find("SysLogFile") != pSettings.end()) - { - m_sSysLogFile = pSettings.find("SysLogFile")->second; - } -} - -map_plugin_settings_t CKerneloopsScanner::GetSettings() -{ - map_plugin_settings_t ret; - - ret["SysLogFile"] = m_sSysLogFile; - - return ret; -} - PLUGIN_INFO(ACTION, CKerneloopsScanner, "KerneloopsScanner", @@ -195,7 +189,7 @@ PLUGIN_INFO(ACTION, "http://people.redhat.com/aarapov", ""); -/* for dumpoops tool */ +/* For "dumpoops" tool */ extern "C" { int scan_syslog_file(CKerneloopsScanner *This, const char *filename) diff --git a/lib/Plugins/KerneloopsScanner.h b/lib/Plugins/KerneloopsScanner.h index 981f187..9f00df2 100644 --- a/lib/Plugins/KerneloopsScanner.h +++ b/lib/Plugins/KerneloopsScanner.h @@ -27,28 +27,26 @@ #ifndef KERNELOOPSSCANNER_H_ #define KERNELOOPSSCANNER_H_ -#include "KerneloopsSysLog.h" +#include "abrt_types.h" #include "Plugin.h" #include "Action.h" class CKerneloopsScanner : public CAction { - private: - std::string m_sSysLogFile; - CSysLog m_pSysLog; + /* For "dumpoops" tool */ + public: + vector_string_t m_pOopsList; + /* For "dumpoops" tool */ public: - /* For standalone oops processor */ void SaveOopsToDebugDump(); int ScanDmesg(); int ScanSysLogFile(const char *filename); /* Plugin interface */ + public: CKerneloopsScanner(); - virtual void Run(const std::string& pActionDir, - const std::string& pArgs); - virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual map_plugin_settings_t GetSettings(); + virtual void Run(const char *pActionDir, const char *pArgs); }; -#endif /* KERNELOOPSSCANNER_H_ */ +#endif diff --git a/lib/Plugins/KerneloopsSysLog.cpp b/lib/Plugins/KerneloopsSysLog.cpp index b1171cf..d28ce39 100644 --- a/lib/Plugins/KerneloopsSysLog.cpp +++ b/lib/Plugins/KerneloopsSysLog.cpp @@ -26,91 +26,70 @@ #include "abrtlib.h" #include "KerneloopsSysLog.h" - -#include <list> #include <assert.h> -/* - * This limits the number of oopses we'll submit per session; - * it's important that this is bounded to avoid feedback loops - * for the scenario where submitting an oopses causes a warning/oops - */ -#define MAX_OOPS 16 - -struct line_info { - char *ptr; - char level; -}; - -static struct line_info *lines_info; -static int lines_info_alloc; -static int linecount; - -#define REALLOC_CHUNK 1000 - -static int set_line_info(int index, char *linepointer, char linelevel) +static void queue_oops(vector_string_t &vec, const char *data, const char *version) { - if (index >= lines_info_alloc) { - struct line_info *new_info; - new_info = (line_info*)realloc(lines_info, - (lines_info_alloc + REALLOC_CHUNK) * sizeof(struct line_info)); - if (!new_info) - return -1; - lines_info_alloc += REALLOC_CHUNK; - lines_info = new_info; - } - - lines_info[index].ptr = linepointer; - lines_info[index].level = linelevel; - return 0; + vec.push_back(ssprintf("%s\n%s", version, data)); } -CSysLog::CSysLog() : - m_nFoundOopses(0) -{} - -void CSysLog::QueueOops(char *data, char *version) +/* + * extract_version tries to find the kernel version in given data + */ +static int extract_version(const char *linepointer, char *version) { - COops m_NewOops; - - if (m_nFoundOopses > MAX_OOPS) - return; + int ret; - m_NewOops.m_sData = data; - m_NewOops.m_sVersion = version; + ret = 0; + if ((strstr(linepointer, "Pid") != NULL) + || (strstr(linepointer, "comm") != NULL) + || (strstr(linepointer, "CPU") != NULL) + || (strstr(linepointer, "REGS") != NULL) + || (strstr(linepointer, "EFLAGS") != NULL) + ) { + char* start; + char* end; - m_OopsQueue.push_back(m_NewOops); - m_nFoundOopses++; -} + start = strstr((char*)linepointer, "2.6."); + if (start) { + end = strchrnul(start, ' '); + strncpy(version, start, end-start); + ret = 1; + } + } -void CSysLog::ClearOopsList() -{ - m_OopsQueue.clear(); -} + if (!ret) + strncpy(version, "undefined", 9); -const std::list<COops>& CSysLog::GetOopsList() -{ - return m_OopsQueue; + return ret; } /* - * This function splits the dmesg buffer data into lines - * (null terminated). + * extract_oops tries to find oops signatures in a log */ -int CSysLog::FillLinePointers(char *buffer, size_t buflen) +struct line_info { + char *ptr; + char level; +}; +#define REALLOC_CHUNK 1000 +int extract_oopses(vector_string_t &oopses, char *buffer, size_t buflen) { - char *c, *linepointer, linelevel; + char *c; enum { maybe, no, yes } syslog_format = maybe; - linecount = 0; + int linecount = 0; + int lines_info_alloc = 0; + struct line_info *lines_info = NULL; + + /* Split buffer into lines */ - if (!buflen) - return 0; - buffer[buflen - 1] = '\n'; /* the buffer usually ends with \n, but let's make sure */ + if (buflen != 0) + buffer[buflen - 1] = '\n'; /* the buffer usually ends with \n, but let's make sure */ c = buffer; while (c < buffer + buflen) { - char v; + char v, linelevel; int len = 0; char *c9; + char *linepointer; c9 = (char*)memchr(c, '\n', buffer + buflen - c); /* a \n will always be found */ assert(c9); @@ -183,68 +162,32 @@ int CSysLog::FillLinePointers(char *buffer, size_t buflen) /* if we see our own marker, we know we submitted everything upto here already */ if (len >= 4 && memmem(linepointer, len, "Abrt", 4)) { linecount = 0; - lines_info[0].ptr = NULL; + lines_info_alloc = 0; + free(lines_info); + lines_info = NULL; + } + + if (linecount >= lines_info_alloc) { + lines_info_alloc += REALLOC_CHUNK; + lines_info = (line_info*)xrealloc(lines_info, + lines_info_alloc * sizeof(struct line_info)); } - if (set_line_info(linecount, linepointer, linelevel) < 0) - return -1; + lines_info[linecount].ptr = linepointer; + lines_info[linecount].level = linelevel; linecount++; next_line: c = c9 + 1; } - return 0; -} - -/* - * extract_version tries to find the kernel version in given data - */ -int CSysLog::ExtractVersion(char *linepointer, char *version) -{ - int ret; - - ret = 0; - if ((strstr(linepointer, "Pid") != NULL) || - (strstr(linepointer, "comm") != NULL) || - (strstr(linepointer, "CPU") != NULL) || - (strstr(linepointer, "REGS") != NULL) || - (strstr(linepointer, "EFLAGS") != NULL)) - { - char* start; - char* end; - - start = strstr(linepointer, "2.6."); - if (start) { - end = strchrnul(start, ' '); - strncpy(version, start, end-start); - ret = 1; - } - } - - if (!ret) - strncpy(version, "undefined", 9); - return ret; -} + /* Analyze lines */ -/* - * extract_oops tries to find oops signatures in a log - */ -int CSysLog::ExtractOops(char *buffer, size_t buflen) -{ int i; char prevlevel = 0; int oopsstart = -1; - int oopsend; + int oopsend = linecount; int inbacktrace = 0; int oopsesfound = 0; - lines_info = NULL; - lines_info_alloc = 0; - - if (FillLinePointers(buffer, buflen) < 0) - goto fail; - - oopsend = linecount; - i = 0; while (i < linecount) { char *c = lines_info[i].ptr; @@ -257,36 +200,36 @@ int CSysLog::ExtractOops(char *buffer, size_t buflen) /* find start-of-oops markers */ if (strstr(c, "general protection fault:")) oopsstart = i; - if (strstr(c, "BUG:")) + else if (strstr(c, "BUG:")) oopsstart = i; - if (strstr(c, "kernel BUG at")) + else if (strstr(c, "kernel BUG at")) oopsstart = i; - if (strstr(c, "do_IRQ: stack overflow:")) + else if (strstr(c, "do_IRQ: stack overflow:")) oopsstart = i; - if (strstr(c, "RTNL: assertion failed")) + else if (strstr(c, "RTNL: assertion failed")) oopsstart = i; - if (strstr(c, "Eeek! page_mapcount(page) went negative!")) + else if (strstr(c, "Eeek! page_mapcount(page) went negative!")) oopsstart = i; - if (strstr(c, "near stack overflow (cur:")) + else if (strstr(c, "near stack overflow (cur:")) oopsstart = i; - if (strstr(c, "double fault:")) + else if (strstr(c, "double fault:")) oopsstart = i; - if (strstr(c, "Badness at")) + else if (strstr(c, "Badness at")) oopsstart = i; - if (strstr(c, "NETDEV WATCHDOG")) + else if (strstr(c, "NETDEV WATCHDOG")) oopsstart = i; - if (strstr(c, "WARNING:") && - !strstr(c, "appears to be on the same physical disk")) + else if (strstr(c, "WARNING:") && + !strstr(c, "appears to be on the same physical disk")) oopsstart = i; - if (strstr(c, "Unable to handle kernel")) + else if (strstr(c, "Unable to handle kernel")) oopsstart = i; - if (strstr(c, "sysctl table check failed")) + else if (strstr(c, "sysctl table check failed")) oopsstart = i; - if (strstr(c, "------------[ cut here ]------------")) + else if (strstr(c, "------------[ cut here ]------------")) oopsstart = i; - if (strstr(c, "list_del corruption.")) + else if (strstr(c, "list_del corruption.")) oopsstart = i; - if (strstr(c, "list_add corruption.")) + else if (strstr(c, "list_add corruption.")) oopsstart = i; if (strstr(c, "Oops:") && i >= 3) oopsstart = i-3; @@ -323,26 +266,33 @@ int CSysLog::ExtractOops(char *buffer, size_t buflen) c1 = strstr(lines_info[i].ptr, ">]"); c2 = strstr(lines_info[i].ptr, "+0x"); c3 = strstr(lines_info[i].ptr, "/0x"); - if (lines_info[i].ptr[0] == ' ' && lines_info[i].ptr[1] == '[' && lines_info[i].ptr[2] == '<' && c1 && c2 && c3) + if (lines_info[i].ptr[0] == ' ' + && lines_info[i].ptr[1] == '[' + && lines_info[i].ptr[2] == '<' + && c1 && c2 && c3 + ) { inbacktrace = 1; - } else + } + } /* try to see if we're at the end of an oops */ - if (oopsstart >= 0 && inbacktrace > 0) { + else if (oopsstart >= 0 && inbacktrace > 0) { char c2, c3; c2 = lines_info[i].ptr[0]; c3 = lines_info[i].ptr[1]; /* line needs to start with " [" or have "] ["*/ - if ((c2 != ' ' || c3 != '[') && - strstr(lines_info[i].ptr, "] [") == NULL && - strstr(lines_info[i].ptr, "--- Exception") == NULL && - strstr(lines_info[i].ptr, " LR =") == NULL && - strstr(lines_info[i].ptr, "<#DF>") == NULL && - strstr(lines_info[i].ptr, "<IRQ>") == NULL && - strstr(lines_info[i].ptr, "<EOI>") == NULL && - strstr(lines_info[i].ptr, "<<EOE>>") == NULL) + if ((c2 != ' ' || c3 != '[') + && strstr(lines_info[i].ptr, "] [") == NULL + && strstr(lines_info[i].ptr, "--- Exception") == NULL + && strstr(lines_info[i].ptr, " LR =") == NULL + && strstr(lines_info[i].ptr, "<#DF>") == NULL + && strstr(lines_info[i].ptr, "<IRQ>") == NULL + && strstr(lines_info[i].ptr, "<EOI>") == NULL + && strstr(lines_info[i].ptr, "<<EOE>>") == NULL + ) { oopsend = i-1; + } /* oops lines are always more than 8 long */ if (strlen(lines_info[i].ptr) < 8) @@ -381,13 +331,15 @@ int CSysLog::ExtractOops(char *buffer, size_t buflen) is_version = 0; for (q = oopsstart; q <= oopsend; q++) { if (!is_version) - is_version = ExtractVersion(lines_info[q].ptr, version); - strcat(oops, lines_info[q].ptr); - strcat(oops, "\n"); + is_version = extract_version(lines_info[q].ptr, version); + if (lines_info[q].ptr[0]) { + strcat(oops, lines_info[q].ptr); + strcat(oops, "\n"); + } } /* too short oopses are invalid */ if (strlen(oops) > 100) { - QueueOops(oops, version); + queue_oops(oopses, oops, version); oopsesfound++; } oopsstart = -1; @@ -431,13 +383,13 @@ int CSysLog::ExtractOops(char *buffer, size_t buflen) is_version = 0; for (q = oopsstart; q <= oopsend; q++) { if (!is_version) - is_version = ExtractVersion(lines_info[q].ptr, version); + is_version = extract_version(lines_info[q].ptr, version); strcat(oops, lines_info[q].ptr); strcat(oops, "\n"); } /* too short oopses are invalid */ if (strlen(oops) > 100) { - QueueOops(oops, version); + queue_oops(oopses, oops, version); oopsesfound++; } oopsstart = -1; @@ -446,8 +398,7 @@ int CSysLog::ExtractOops(char *buffer, size_t buflen) free(oops); free(version); } -fail: + free(lines_info); - lines_info = NULL; return oopsesfound; } diff --git a/lib/Plugins/KerneloopsSysLog.h b/lib/Plugins/KerneloopsSysLog.h index c2e8c2d..a67b33d 100644 --- a/lib/Plugins/KerneloopsSysLog.h +++ b/lib/Plugins/KerneloopsSysLog.h @@ -27,30 +27,8 @@ #ifndef __INCLUDE_GUARD_KERNELOOPSSYSLOG_H_ #define __INCLUDE_GUARD_KERNELOOPSSYSLOG_H_ -#include <string> -#include <list> +#include "abrt_types.h" -class COops -{ - public: - std::string m_sData; - std::string m_sVersion; -}; - -class CSysLog -{ - private: - void QueueOops(char *data, char *version); - int ExtractVersion(char *linepointer, char *version); - int FillLinePointers(char *buffer, size_t buflen); - std::list<COops> m_OopsQueue; - int m_nFoundOopses; - - public: - CSysLog(); - const std::list<COops>& GetOopsList(); - void ClearOopsList(); - int ExtractOops(char *buffer, size_t buflen); -}; +int extract_oopses(vector_string_t &oopses, char *buffer, size_t buflen); #endif diff --git a/lib/Plugins/Logger.cpp b/lib/Plugins/Logger.cpp index ff7bbb8..b2ac1ad 100644 --- a/lib/Plugins/Logger.cpp +++ b/lib/Plugins/Logger.cpp @@ -43,99 +43,40 @@ void CLogger::SetSettings(const map_plugin_settings_t& pSettings) } } -map_plugin_settings_t CLogger::GetSettings() +const map_plugin_settings_t& CLogger::GetSettings() { - map_plugin_settings_t ret; + m_pSettings["LogPath"] = m_sLogPath; + m_pSettings["AppendLogs"] = m_bAppendLogs ? "yes" : "no"; - ret["LogPath"] = m_sLogPath; - ret["AppendLogs"] = m_bAppendLogs ? "yes" : "no"; - - return ret; + return m_pSettings; } -std::string CLogger::Report(const map_crash_report_t& pCrashReport, const std::string& pArgs) +std::string CLogger::Report(const map_crash_report_t& pCrashReport, + const map_plugin_settings_t& pSettings, const std::string& pArgs) { update_client(_("Creating a report...")); - std::stringstream binaryFiles, commonFiles, bigTextFiles, additionalFiles, UUIDFile; - std::ofstream fOut; - - map_crash_report_t::const_iterator it; - for (it = pCrashReport.begin(); it != pCrashReport.end(); it++) - { - if (it->second[CD_TYPE] == CD_TXT) - { - if (it->first != CD_UUID && - it->first != FILENAME_ARCHITECTURE && - it->first != FILENAME_KERNEL && - it->first != FILENAME_PACKAGE) - { - additionalFiles << it->first << std::endl; - additionalFiles << "-----" << std::endl; - additionalFiles << it->second[CD_CONTENT] << std::endl << std::endl; - } - else if (it->first == CD_UUID) - { - UUIDFile << it->first << std::endl; - UUIDFile << "-----" << std::endl; - UUIDFile << it->second[CD_CONTENT] << std::endl << std::endl; - } - else - { - commonFiles << it->first << std::endl; - commonFiles << "-----" << std::endl; - commonFiles << it->second[CD_CONTENT] << std::endl << std::endl; - } - } - if (it->second[CD_TYPE] == CD_ATT) - { - bigTextFiles << it->first << std::endl; - bigTextFiles << "-----" << std::endl; - bigTextFiles << it->second[CD_CONTENT] << std::endl << std::endl; - } - if (it->second[CD_TYPE] == CD_BIN) - { - binaryFiles << it->first << std::endl; - binaryFiles << "-----" << std::endl; - binaryFiles << it->second[CD_CONTENT] << std::endl << std::endl; - } - } - + std::string description = make_description_logger(pCrashReport); + description += "\n\n\n"; + FILE *fOut; if (m_bAppendLogs) { - fOut.open(m_sLogPath.c_str(), std::ios::app); + fOut = fopen(m_sLogPath.c_str(), "a"); } else { - fOut.open(m_sLogPath.c_str()); + fOut = fopen(m_sLogPath.c_str(), "w"); } - if (fOut.is_open()) - { - fOut << "Duplicity check" << std::endl; - fOut << "======" << std::endl << std::endl; - fOut << UUIDFile.str() << std::endl; - fOut << "Common information" << std::endl; - fOut << "======" << std::endl << std::endl; - fOut << commonFiles.str() << std::endl; - fOut << "Additional information" << std::endl; - fOut << "======" << std::endl << std::endl; - fOut << additionalFiles.str() << std::endl; - fOut << "Big Text Files" << std::endl; - fOut << "======" << std::endl; - fOut << bigTextFiles.str() << std::endl; - fOut << "Binary files" << std::endl; - fOut << "======" << std::endl; - fOut << binaryFiles.str() << std::endl; - fOut << std::endl; - fOut.close(); - } - else + if (fOut) { - throw CABRTException(EXCEP_PLUGIN, "CLogger::Report(): Cannot open file: " + m_sLogPath); + fputs(description.c_str(), fOut); + fclose(fOut); + return "file://" + m_sLogPath; } - return "file://" + m_sLogPath; + + throw CABRTException(EXCEP_PLUGIN, "CLogger::Report(): Cannot open file: " + m_sLogPath); } PLUGIN_INFO(REPORTER, diff --git a/lib/Plugins/Logger.h b/lib/Plugins/Logger.h index 60c76e4..0969bea 100644 --- a/lib/Plugins/Logger.h +++ b/lib/Plugins/Logger.h @@ -35,8 +35,9 @@ class CLogger : public CReporter CLogger(); virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual map_plugin_settings_t GetSettings(); + virtual const map_plugin_settings_t& GetSettings(); virtual std::string Report(const map_crash_report_t& pCrashReport, + const map_plugin_settings_t& pSettings, const std::string& pArgs); }; diff --git a/lib/Plugins/Mailx.cpp b/lib/Plugins/Mailx.cpp index f7a86fd..f083404 100644 --- a/lib/Plugins/Mailx.cpp +++ b/lib/Plugins/Mailx.cpp @@ -118,7 +118,8 @@ void CMailx::SendEmail(const std::string& pSubject, const std::string& pText, co ExecMailx(atoi(pUID.c_str()), pText); } -std::string CMailx::Report(const map_crash_report_t& pCrashReport, const std::string& pArgs) +std::string CMailx::Report(const map_crash_report_t& pCrashReport, + const map_plugin_settings_t& pSettings, const std::string& pArgs) { update_client(_("Creating a report...")); @@ -218,16 +219,14 @@ void CMailx::SetSettings(const map_plugin_settings_t& pSettings) } } -map_plugin_settings_t CMailx::GetSettings() +const map_plugin_settings_t& CMailx::GetSettings() { - map_plugin_settings_t ret; + m_pSettings["Subject"] = m_sSubject; + m_pSettings["EmailFrom"] = m_sEmailFrom; + m_pSettings["EmailTo"] = m_sEmailTo; + m_pSettings["SendBinaryData"] = m_bSendBinaryData ? "yes" : "no"; - ret["Subject"] = m_sSubject; - ret["EmailFrom"] = m_sEmailFrom; - ret["EmailTo"] = m_sEmailTo; - ret["SendBinaryData"] = m_bSendBinaryData ? "yes" : "no"; - - return ret; + return m_pSettings; } PLUGIN_INFO(REPORTER, diff --git a/lib/Plugins/Mailx.h b/lib/Plugins/Mailx.h index fd00bb8..7af1188 100644 --- a/lib/Plugins/Mailx.h +++ b/lib/Plugins/Mailx.h @@ -47,8 +47,9 @@ class CMailx : public CReporter CMailx(); virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual map_plugin_settings_t GetSettings(); + virtual const map_plugin_settings_t& GetSettings(); virtual std::string Report(const map_crash_report_t& pCrashReport, + const map_plugin_settings_t& pSettings, const std::string& pArgs); }; diff --git a/lib/Plugins/Makefile.am b/lib/Plugins/Makefile.am index bc032d7..0502f5e 100644 --- a/lib/Plugins/Makefile.am +++ b/lib/Plugins/Makefile.am @@ -1,28 +1,44 @@ AM_CPPFLAGS = -I$(srcdir)/../../inc -I$(srcdir)/../Utils pluginslibdir=$(PLUGINS_LIB_DIR) -pluginslib_LTLIBRARIES = libCCpp.la \ - libMailx.la \ - libSQLite3.la \ - libLogger.la \ - libKerneloopsScanner.la\ - libKerneloopsReporter.la\ - libKerneloops.la \ - libRunApp.la \ - libSOSreport.la \ - libBugzilla.la \ - libTicketUploader.la \ - libPython.la \ - libFileTransfer.la - -dist_pluginslib_DATA = KerneloopsReporter.GTKBuilder Logger.GTKBuilder Mailx.GTKBuilder Bugzilla.GTKBuilder TicketUploader.GTKBuilder - -pluginsconfdir=$(PLUGINS_CONF_DIR) -dist_pluginsconf_DATA = CCpp.conf Mailx.conf SQLite3.conf Logger.conf KerneloopsScanner.conf KerneloopsReporter.conf Bugzilla.conf TicketUploader.conf FileTransfer.conf - - -man_MANS = abrt-FileTransfer.7 abrt-Bugzilla.7 abrt-KerneloopsReporter.7\ - abrt-KerneloopsScanner.7 abrt-Logger.7 abrt-Mailx.7 abrt-plugins.7\ +pluginslib_LTLIBRARIES = \ + libCCpp.la \ + libMailx.la \ + libSQLite3.la \ + libLogger.la \ + libKerneloopsScanner.la\ + libKerneloopsReporter.la\ + libKerneloops.la \ + libRunApp.la \ + libSOSreport.la \ + libBugzilla.la \ + libCatcut.la \ + libTicketUploader.la \ + libPython.la \ + libFileTransfer.la + +dist_pluginslib_DATA = \ + KerneloopsReporter.GTKBuilder Logger.GTKBuilder \ + Mailx.GTKBuilder Bugzilla.GTKBuilder \ + TicketUploader.GTKBuilder Catcut.GTKBuilder + +pluginsconfdir = $(PLUGINS_CONF_DIR) +dist_pluginsconf_DATA = \ + CCpp.conf \ + Mailx.conf \ + SQLite3.conf \ + Logger.conf \ + Kerneloops.conf \ + KerneloopsScanner.conf \ + KerneloopsReporter.conf \ + Bugzilla.conf \ + Catcut.conf \ + TicketUploader.conf \ + FileTransfer.conf + +man_MANS = abrt-FileTransfer.7 abrt-Bugzilla.7 abrt-KerneloopsReporter.7 \ + abrt-KerneloopsScanner.7 abrt-Logger.7 abrt-Mailx.7 abrt-plugins.7 \ abrt-SQLite3.7 abrt-RunApp.7 abrt-TicketUploader.7 +# + abrt-Catcut.7 EXTRA_DIST = $(man_MANS) @@ -36,7 +52,11 @@ install-data-hook: libCCpp_la_SOURCES = CCpp.cpp CCpp.h libCCpp_la_LDFLAGS = -avoid-version libCCpp_la_LIBADD = $(NSS_LIBS) -libCCpp_la_CPPFLAGS = -I$(srcdir)/../../inc -I$(srcdir)/../Utils -DCCPP_HOOK_PATH=\"${libexecdir}/hookCCpp\" -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" $(NSS_CFLAGS) +libCCpp_la_CPPFLAGS = -I$(srcdir)/../../inc -I$(srcdir)/../Utils \ + -DCCPP_HOOK_PATH=\"${libexecdir}/hookCCpp\" \ + -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + $(NSS_CFLAGS) # Kerneloops libKerneloops_la_SOURCES = Kerneloops.cpp Kerneloops.h @@ -57,7 +77,7 @@ libKerneloopsScanner_la_CPPFLAGS = -I$(srcdir)/../../inc -I$(srcdir)/../Utils -D # Mailx libMailx_la_SOURCES = Mailx.cpp Mailx.h libMailx_la_LDFLAGS = -avoid-version -libMailx_la_CPPFLAGS = -I$(srcdir)/../../inc -I$(srcdir)/../Utils -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" +libMailx_la_CPPFLAGS = -I$(srcdir)/../../inc -I$(srcdir)/../Utils -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" -DLOCALSTATEDIR='"$(localstatedir)"' # SQLite3 libSQLite3_la_SOURCES = SQLite3.cpp SQLite3.h @@ -80,9 +100,15 @@ libSOSreport_la_LDFLAGS = -avoid-version # Bugzilla libBugzilla_la_SOURCES = Bugzilla.h Bugzilla.cpp -libBugzilla_la_LIBADD = $(XMLRPC_CPP_LIBS) $(XMLRPC_CLIENT_CPP_LIBS) $(NSS_LIBS) +libBugzilla_la_LIBADD = $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS) libBugzilla_la_LDFLAGS = -avoid-version -libBugzilla_la_CPPFLAGS = $(XMLRPC_CPP_CFLAGS) $(XMLRPC_CLIENT_CPP_CFLAGS) $(NSS_CFLAGS) -I$(srcdir)/../../inc -I$(srcdir)/../Utils -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" +libBugzilla_la_CPPFLAGS = $(XMLRPC_CFLAGS) $(XMLRPC_CLIENT_CFLAGS) -I$(srcdir)/../../inc -I$(srcdir)/../Utils -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" + +# Catcut +libCatcut_la_SOURCES = Catcut.h Catcut.cpp +libCatcut_la_LIBADD = $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS) +libCatcut_la_LDFLAGS = -avoid-version +libCatcut_la_CPPFLAGS = $(XMLRPC_CFLAGS) $(XMLRPC_CLIENT_CFLAGS) -I$(srcdir)/../../inc -I$(srcdir)/../Utils -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" # TicketUploader libTicketUploader_la_SOURCES = TicketUploader.h TicketUploader.cpp diff --git a/lib/Plugins/Python.cpp b/lib/Plugins/Python.cpp index 9be76b3..9fb7880 100644 --- a/lib/Plugins/Python.cpp +++ b/lib/Plugins/Python.cpp @@ -6,7 +6,7 @@ #define FILENAME_BACKTRACE "backtrace" #define PYHOOK_CONFIG "/etc/abrt/pyhook.conf" -std::string CAnalyzerPython::CreateHash(const std::string& pDebugDumpDir) +static std::string CreateHash(const char *pDebugDumpDir) { std::string uuid; CDebugDump dd; @@ -15,11 +15,11 @@ std::string CAnalyzerPython::CreateHash(const std::string& pDebugDumpDir) return uuid; } -std::string CAnalyzerPython::GetLocalUUID(const std::string& pDebugDumpDir) +std::string CAnalyzerPython::GetLocalUUID(const char *pDebugDumpDir) { return CreateHash(pDebugDumpDir); } -std::string CAnalyzerPython::GetGlobalUUID(const std::string& pDebugDumpDir) +std::string CAnalyzerPython::GetGlobalUUID(const char *pDebugDumpDir) { return GetLocalUUID(pDebugDumpDir); } diff --git a/lib/Plugins/Python.h b/lib/Plugins/Python.h index b54de8d..82f52c0 100644 --- a/lib/Plugins/Python.h +++ b/lib/Plugins/Python.h @@ -8,12 +8,11 @@ class CAnalyzerPython : public CAnalyzer { public: - virtual std::string GetLocalUUID(const std::string& pDebugDumpDir); - virtual std::string GetGlobalUUID(const std::string& pDebugDumpDir); - virtual void CreateReport(const std::string& pDebugDumpDir, int force) {} + virtual std::string GetLocalUUID(const char *pDebugDumpDir); + virtual std::string GetGlobalUUID(const char *pDebugDumpDir); + virtual void CreateReport(const char *pDebugDumpDir, int force) {} virtual void Init(); virtual void DeInit(); - virtual std::string CreateHash(const std::string& pInput); }; #endif /* PYTHON_H_ */ diff --git a/lib/Plugins/RunApp.cpp b/lib/Plugins/RunApp.cpp index 5a5c1d4..f816dc2 100644 --- a/lib/Plugins/RunApp.cpp +++ b/lib/Plugins/RunApp.cpp @@ -29,41 +29,41 @@ #define COMMAND 0 #define FILENAME 1 -void CActionRunApp::ParseArgs(const std::string& psArgs, vector_string_t& pArgs) +/* TODO: do not duplicate: SOSreport.cpp has same function too */ +static void ParseArgs(const char *psArgs, vector_string_t& pArgs) { - unsigned int ii; + unsigned ii; bool is_quote = false; - std::string item = ""; - for (ii = 0; ii < psArgs.length(); ii++) + std::string item; + + for (ii = 0; psArgs[ii]; ii++) { - if (psArgs[ii] == '\"') + if (psArgs[ii] == '"') { - is_quote = is_quote == true ? false : true; + is_quote = !is_quote; } else if (psArgs[ii] == ',' && !is_quote) { pArgs.push_back(item); - item = ""; + item.clear(); } else { item += psArgs[ii]; } } - if (item != "") + + if (item.size() != 0) { pArgs.push_back(item); } } -void CActionRunApp::Run(const std::string& pActionDir, - const std::string& pArgs) +void CActionRunApp::Run(const char *pActionDir, const char *pArgs) { update_client(_("Executing RunApp plugin...")); - char line[1024]; - std::string output = ""; - + std::string output; vector_string_t args; ParseArgs(pArgs, args); @@ -71,8 +71,9 @@ void CActionRunApp::Run(const std::string& pActionDir, FILE *fp = popen(args[COMMAND].c_str(), "r"); if (fp == NULL) { - throw CABRTException(EXCEP_PLUGIN, "CActionRunApp::Run(): cannot execute " + args[COMMAND]); + throw CABRTException(EXCEP_PLUGIN, "Can't execute " + args[COMMAND]); } + char line[1024]; while (fgets(line, 1024, fp) != NULL) { output += line; @@ -83,7 +84,7 @@ void CActionRunApp::Run(const std::string& pActionDir, { CDebugDump dd; dd.Open(pActionDir); - dd.SaveText(args[FILENAME].c_str(), output); + dd.SaveText(args[FILENAME].c_str(), output.c_str()); } } diff --git a/lib/Plugins/RunApp.h b/lib/Plugins/RunApp.h index 23315f1..939feaa 100644 --- a/lib/Plugins/RunApp.h +++ b/lib/Plugins/RunApp.h @@ -28,12 +28,8 @@ class CActionRunApp : public CAction { - private: - void ParseArgs(const std::string& psArgs, vector_string_t& pArgs); - public: - virtual void Run(const std::string& pActionDir, - const std::string& pArgs); + virtual void Run(const char *pActionDir, const char *pArgs); }; #endif diff --git a/lib/Plugins/SOSreport.cpp b/lib/Plugins/SOSreport.cpp index ab6125c..fedc51a 100644 --- a/lib/Plugins/SOSreport.cpp +++ b/lib/Plugins/SOSreport.cpp @@ -18,41 +18,26 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include <stdio.h> -#include <string.h> #include <ext/stdio_filebuf.h> #include <fstream> #include <sstream> +#include "abrtlib.h" +#include "abrt_types.h" +#include "ABRTException.h" #include "SOSreport.h" #include "DebugDump.h" #include "ABRTException.h" #include "CommLayerInner.h" -void CActionSOSreport::CopyFile(const std::string& pSourceName, const std::string& pDestName) -{ - std::ifstream source(pSourceName.c_str(), std::fstream::binary); - - if (!source) - { - throw CABRTException(EXCEP_PLUGIN, "CActionSOSreport::CopyFile(): could not open input sosreport filename:" + pSourceName); - } - std::ofstream dest(pDestName.c_str(),std::fstream::trunc|std::fstream::binary); - if (!dest) - { - throw CABRTException(EXCEP_PLUGIN, "CActionSOSreport::CopyFile(): could not open output sosreport filename:" + pDestName); - } - dest << source.rdbuf(); -} - -void CActionSOSreport::ErrorCheck(const index_type pI) +static void ErrorCheck(int pos) { - if (pI == std::string::npos) + if (pos < 0) { - throw CABRTException(EXCEP_PLUGIN, std::string("CActionSOSreport::ErrorCheck(): could not find filename in sosreport output")); + throw CABRTException(EXCEP_PLUGIN, "Can't find filename in sosreport output"); } } -std::string CActionSOSreport::ParseFilename(const std::string& pOutput) +static std::string ParseFilename(const std::string& pOutput) { /* the sosreport's filename is embedded in sosreport's output. @@ -63,83 +48,85 @@ std::string CActionSOSreport::ParseFilename(const std::string& pOutput) static const char sosreport_filename_marker[] = "Your sosreport has been generated and saved in:"; - index_type p = pOutput.find(sosreport_filename_marker); + int p = pOutput.find(sosreport_filename_marker); ErrorCheck(p); - p += strlen(sosreport_filename_marker); + p += sizeof(sosreport_filename_marker)-1; - index_type filename_start = pOutput.find_first_not_of(" \n\t", p); + int filename_start = pOutput.find_first_not_of(" \n\t", p); ErrorCheck(p); - index_type line_end = pOutput.find_first_of('\n',filename_start); + int line_end = pOutput.find_first_of('\n',filename_start); ErrorCheck(p); - index_type filename_end = pOutput.find_last_not_of(" \n\t",line_end); + int filename_end = pOutput.find_last_not_of(" \n\t", line_end); ErrorCheck(p); - return pOutput.substr(filename_start,(filename_end-filename_start)+1); + return pOutput.substr(filename_start, filename_end - filename_start + 1); } -void CActionSOSreport::ParseArgs(const std::string& psArgs, vector_args_t& pArgs) +/* TODO: do not duplicate: RunApp.cpp has same function too */ +static void ParseArgs(const char *psArgs, vector_string_t& pArgs) { - unsigned int ii; + unsigned ii; bool is_quote = false; - std::string item = ""; - for (ii = 0; ii < psArgs.length(); ii++) + std::string item; + + for (ii = 0; psArgs[ii]; ii++) { - if (psArgs[ii] == '\"') + if (psArgs[ii] == '"') { - is_quote = is_quote == true ? false : true; + is_quote = !is_quote; } else if (psArgs[ii] == ',' && !is_quote) { pArgs.push_back(item); - item = ""; + item.clear(); } else { item += psArgs[ii]; } } - if (item != "") + + if (item.size() != 0) { pArgs.push_back(item); } } -void CActionSOSreport::Run(const std::string& pActionDir, - const std::string& pArgs) +void CActionSOSreport::Run(const char *pActionDir, const char *pArgs) { update_client(_("Executing SOSreport plugin...")); - const char command_default[] = "sosreport --batch --no-progressbar --only=anaconda --only=bootloader" + static const char command_default[] = "sosreport --batch --no-progressbar --only=anaconda --only=bootloader" " --only=devicemapper --only=filesys --only=hardware --only=kernel" " --only=libraries --only=memory --only=networking --only=nfsserver" " --only=pam --only=process --only=rpm -k rpm.rpmva=off --only=ssh" " --only=startup --only=yum 2>&1"; - const char command_prefix[] = "sosreport --batch --no-progressbar"; + static const char command_prefix[] = "sosreport --batch --no-progressbar"; std::string command; - vector_args_t args; + vector_string_t args; ParseArgs(pArgs, args); if (args.size() == 0 || args[0] == "") { - command = std::string(command_default); + command = command_default; } else { - command = std::string(command_prefix) + ' ' + args[0] + " 2>&1"; + command = ssprintf("%s %s 2>&1", command_prefix, args[0].c_str()); } - update_client(_("running sosreport: ") + command); + update_client(_("running sosreport: %s"), command.c_str()); FILE *fp = popen(command.c_str(), "r"); - if (fp == NULL) { - throw CABRTException(EXCEP_PLUGIN, std::string("CActionSOSreport::Run(): cannot execute ") + command); + throw CABRTException(EXCEP_PLUGIN, ssprintf("Can't execute '%s'", command.c_str())); } +//vda TODO: fix this mess std::ostringstream output_stream; __gnu_cxx::stdio_filebuf<char> command_output_buffer(fp, std::ios_base::in); @@ -152,13 +139,21 @@ void CActionSOSreport::Run(const std::string& pActionDir, std::string output = output_stream.str(); std::string sosreport_filename = ParseFilename(output); - std::string sosreport_dd_filename = pActionDir + "/sosreport.tar.bz2"; + std::string sosreport_dd_filename = concat_path_file(pActionDir, "sosreport.tar.bz2"); CDebugDump dd; dd.Open(pActionDir); //Not useful //dd.SaveText("sosreportoutput", output); - CopyFile(sosreport_filename,sosreport_dd_filename); + if (copy_file(sosreport_filename.c_str(), sosreport_dd_filename.c_str()) < 0) + { + throw CABRTException(EXCEP_PLUGIN, + ssprintf("Can't copy '%s' to '%s'", + sosreport_filename.c_str(), + sosreport_dd_filename.c_str() + ) + ); + } } PLUGIN_INFO(ACTION, diff --git a/lib/Plugins/SOSreport.h b/lib/Plugins/SOSreport.h index 1a35287..d4e0d73 100644 --- a/lib/Plugins/SOSreport.h +++ b/lib/Plugins/SOSreport.h @@ -22,24 +22,11 @@ #define SOSREPORT_H_ #include "Action.h" -#include <string> -#include <vector> class CActionSOSreport : public CAction { - private: - typedef std::string::size_type index_type; - - void CopyFile(const std::string& pSourceName, const std::string& pDestName); - void ErrorCheck(const index_type pI); - std::string ParseFilename(const std::string& pOutput); - - typedef std::vector<std::string> vector_args_t; - void ParseArgs(const std::string& psArgs, vector_args_t& pArgs); - public: - virtual void Run(const std::string& pActionDir, - const std::string& pArgs); + virtual void Run(const char *pActionDir, const char *pArgs); }; #endif diff --git a/lib/Plugins/SQLite3.cpp b/lib/Plugins/SQLite3.cpp index a2dc426..ab39d04 100644 --- a/lib/Plugins/SQLite3.cpp +++ b/lib/Plugins/SQLite3.cpp @@ -24,7 +24,8 @@ #include <stdlib.h> #include "SQLite3.h" #include "ABRTException.h" - +#include <limits.h> +#include <abrtlib.h> #define ABRT_TABLE_VERSION 2 #define ABRT_TABLE_VERSION_STR "2" @@ -96,8 +97,10 @@ bool CSQLite3::Exist(const std::string& pUUID, const std::string& pUID) { vector_database_rows_t table; GetTable("SELECT "DATABASE_COLUMN_REPORTED" FROM "ABRT_TABLE" WHERE " - DATABASE_COLUMN_UUID" = '"+pUUID+"' AND " - DATABASE_COLUMN_UID" = '"+pUID+"';", table); + DATABASE_COLUMN_UUID" = '"+pUUID+"' " + "AND ("DATABASE_COLUMN_UID" = '"+pUID+"' " + "OR "DATABASE_COLUMN_UID" = '-1');" + , table); if (table.empty()) { return false; @@ -311,7 +314,8 @@ void CSQLite3::Delete(const std::string& pUUID, const std::string& pUID) { Exec("DELETE FROM "ABRT_TABLE" " "WHERE "DATABASE_COLUMN_UUID" = '"+pUUID+"' " - "AND "DATABASE_COLUMN_UID" = '"+pUID+"';"); + "AND "DATABASE_COLUMN_UID" = '"+pUID+"' " + "OR "DATABASE_COLUMN_UID" = '-1';"); } else { @@ -321,7 +325,7 @@ void CSQLite3::Delete(const std::string& pUUID, const std::string& pUID) void CSQLite3::SetReported(const std::string& pUUID, const std::string& pUID, const std::string& pMessage) { - if(pUID == "0") + if (pUID == "0") { Exec("UPDATE "ABRT_TABLE" " "SET "DATABASE_COLUMN_REPORTED" = 1 " @@ -335,15 +339,17 @@ void CSQLite3::SetReported(const std::string& pUUID, const std::string& pUID, co Exec("UPDATE "ABRT_TABLE" " "SET "DATABASE_COLUMN_REPORTED" = 1 " "WHERE "DATABASE_COLUMN_UUID" = '"+pUUID+"' " - "AND "DATABASE_COLUMN_UID" = '"+pUID+"';"); + "AND ("DATABASE_COLUMN_UID" = '"+pUID+"' " + "OR "DATABASE_COLUMN_UID" = '-1');"); Exec("UPDATE "ABRT_TABLE" " "SET "DATABASE_COLUMN_MESSAGE" = '" + pMessage + "' " "WHERE "DATABASE_COLUMN_UUID" = '"+pUUID+"' " - "AND "DATABASE_COLUMN_UID" = '"+pUID+"';"); + "AND ("DATABASE_COLUMN_UID" = '"+pUID+"' " + "OR "DATABASE_COLUMN_UID" = '-1');"); } else { - throw CABRTException(EXCEP_PLUGIN, "CSQLite3::SetReported(): UUID is not found in DB."); + throw CABRTException(EXCEP_PLUGIN, "CSQLite3::SetReported(): UUID"+pUID+" is not found in DB."); } } @@ -357,7 +363,8 @@ vector_database_rows_t CSQLite3::GetUIDData(const std::string& pUID) else { GetTable("SELECT * FROM "ABRT_TABLE - " WHERE "DATABASE_COLUMN_UID" = '"+pUID+"';", + " WHERE "DATABASE_COLUMN_UID" = '"+pUID+"' " + "OR "DATABASE_COLUMN_UID" = '-1';", table); } return table; @@ -377,7 +384,8 @@ database_row_t CSQLite3::GetUUIDData(const std::string& pUUID, const std::string { GetTable("SELECT * FROM "ABRT_TABLE" " "WHERE "DATABASE_COLUMN_UUID" = '"+pUUID+"' " - "AND "DATABASE_COLUMN_UID" = '"+pUID+"';", + "AND ("DATABASE_COLUMN_UID" = '"+pUID+"' " + "OR "DATABASE_COLUMN_UID" = '-1');", table); } @@ -396,13 +404,11 @@ void CSQLite3::SetSettings(const map_plugin_settings_t& pSettings) } } -map_plugin_settings_t CSQLite3::GetSettings() +const map_plugin_settings_t& CSQLite3::GetSettings() { - map_plugin_settings_t ret; - - ret["DBPath"] = m_sDBPath; + m_pSettings["DBPath"] = m_sDBPath; - return ret; + return m_pSettings; } PLUGIN_INFO(DATABASE, diff --git a/lib/Plugins/SQLite3.h b/lib/Plugins/SQLite3.h index e7ca8ae..0eb3d08 100644 --- a/lib/Plugins/SQLite3.h +++ b/lib/Plugins/SQLite3.h @@ -58,7 +58,7 @@ class CSQLite3 : public CDatabase virtual database_row_t GetUUIDData(const std::string& pUUID, const std::string& pUID); virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual map_plugin_settings_t GetSettings(); + virtual const map_plugin_settings_t& GetSettings(); }; #endif /* SQLITE3_H_ */ diff --git a/lib/Plugins/TicketUploader.cpp b/lib/Plugins/TicketUploader.cpp index 69243cd..e380de0 100644 --- a/lib/Plugins/TicketUploader.cpp +++ b/lib/Plugins/TicketUploader.cpp @@ -17,26 +17,20 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - +#include <string> +#include <fstream> +#include <sstream> +#include <curl/curl.h> +#include "abrtlib.h" #include "TicketUploader.h" #include "DebugDump.h" #include "ABRTException.h" #include "CommLayerInner.h" -#include <stdlib.h> -#include <sys/stat.h> - -#include <string> -#include <fstream> -#include <sstream> -#include <ext/stdio_filebuf.h> -#include <curl/curl.h> +using namespace std; CTicketUploader::CTicketUploader() : - m_sCustomer(""), - m_sTicket(""), - m_sURL(""), m_bEncrypt(false), m_bUpload(false), m_nRetryCount(3), @@ -47,196 +41,131 @@ CTicketUploader::~CTicketUploader() {} - -void CTicketUploader::Error(string func, string msg) -{ - update_client(msg); - throw CABRTException(EXCEP_PLUGIN, func + msg); -} - -void CTicketUploader::CopyFile(const std::string& pSourceName, const std::string& pDestName) +static void Error(const char *msg) { - std::ifstream source(pSourceName.c_str(), std::fstream::binary); - - if (!source) - { - throw CABRTException(EXCEP_PLUGIN, "CActionSOSreport::CopyFile(): could not open input sosreport filename:" + pSourceName); - } - std::ofstream dest(pDestName.c_str(),std::fstream::trunc|std::fstream::binary); - if (!dest) - { - throw CABRTException(EXCEP_PLUGIN, "CActionSOSreport::CopyFile(): could not open output sosreport filename:" + pDestName); - } - dest << source.rdbuf(); + update_client("%s", msg); + throw CABRTException(EXCEP_PLUGIN, msg); } -void CTicketUploader::RunCommand(string cmd) +static void RunCommand(const char *cmd) { - int retcode = system(cmd.c_str()); - if (retcode == -1) - { - Error("TicketUploader::RunCommand:", "error: could not start subshell: " + cmd); - } + int retcode = system(cmd); if (retcode) { - std::ostringstream msg; - msg << "error: subshell failed (rc=" << retcode << "):" << cmd; - Error("TicketUploader::RunCommand:", msg.str()); + Error(ssprintf("'%s' exited with %d", cmd, retcode).c_str()); } } -string CTicketUploader::ReadCommand(string cmd) +static string ReadCommand(const char *cmd) { - FILE* fp = popen(cmd.c_str(),"r"); + FILE* fp = popen(cmd, "r"); if (!fp) { - Error("TicketUploader::ReadCommand:", "error: could not start subshell: " + cmd); + Error(ssprintf("error running '%s'", cmd).c_str()); } - __gnu_cxx::stdio_filebuf<char> command_output_buffer(fp, std::ios_base::in); - std::ostringstream output_stream; - output_stream << &command_output_buffer; + string result; + char buff[1024]; + while (fgets(buff, sizeof(buff), fp) != NULL) + { + result += buff; + } int retcode = pclose(fp); if (retcode) { - std::ostringstream msg; - msg << "error: subshell failed (rc=" << retcode << "):" << cmd; - Error("TicketUploader::ReadCommand:", msg.str()); + Error(ssprintf("'%s' exited with %d", cmd, retcode).c_str()); } - return output_stream.str(); + return result; } -void CTicketUploader::WriteCommand(string cmd,string input) +static void WriteCommand(const char *cmd, const char *input) { - FILE* fp = popen(cmd.c_str(),"w"); + FILE* fp = popen(cmd, "w"); if (!fp) { - Error("TicketUploader::WriteCommand:", "error: could not start subshell: " + cmd); + Error(ssprintf("error running '%s'", cmd).c_str()); } - size_t input_length = input.length(); - size_t check = fwrite(input.c_str(),1,input_length,fp); - if (input_length != check) - { - Error("TicketUploader::WriteCommand:", "error: could not send input to subshell: " + cmd); - } + /* Hoping it's not too big to get us forever blocked... */ + fputs(input, fp); int retcode = pclose(fp); if (retcode) { - std::ostringstream msg; - msg << "error: subshell failed (rc=" << retcode << "):" << cmd; - Error("TicketUploader::ReadCommand:", msg.str()); + Error(ssprintf("'%s' exited with %d", cmd, retcode).c_str()); } - } -void CTicketUploader::SendFile(const std::string& pURL, - const std::string& pFilename) +void CTicketUploader::SendFile(const char *pURL, const char *pFilename) { - FILE * f; - struct stat buf; - CURL * curl; - std::string wholeURL, protocol; - int result, i, count = m_nRetryCount; - int len = pURL.length(); - std::string file; - - if (pURL == "") + if (pURL[0] == '\0') { - warn_client(_("FileTransfer: URL not specified")); + error_msg(_("FileTransfer: URL not specified")); return; } - protocol = ""; - i = 0; - while(pURL[i] != ':') - { - protocol += pURL[i]; - i++; - if(i == len) - { - throw CABRTException(EXCEP_PLUGIN, "CFileTransfer::SendFile(): malformed URL, does not contain protocol"); - } - } - file = pFilename.substr(pFilename.rfind("/") + 1, pFilename.length()); - - if( pURL[len-1] == '/' ) - { - wholeURL = pURL + file; - } - else - { - wholeURL = pURL + "/" + file; - } - - update_client(_("Sending archive ") + pFilename + _(" via ") + protocol + _(" to ") + pURL); + update_client(_("Sending archive %s to %s"), pFilename, pURL); + const char *base = (strrchr(pFilename, '/') ? : pFilename-1) + 1; + string wholeURL = concat_path_file(pURL, base); + int count = m_nRetryCount; + int result; do { - f = fopen(pFilename.c_str(),"r"); - if(!f) + FILE* f = fopen(pFilename, "r"); + if (!f) { - throw CABRTException(EXCEP_PLUGIN, "CFileTransfer::SendFile(): cannot open archive file "+pFilename); + throw CABRTException(EXCEP_PLUGIN, ssprintf("Can't open archive file '%s'", pFilename)); } - if (stat(pFilename.c_str(), &buf) == -1) + struct stat buf; + if (fstat(fileno(f), &buf) == -1) { - throw CABRTException(EXCEP_PLUGIN, "CFileTransfer::SendFile(): cannot stat archive file "+pFilename); + throw CABRTException(EXCEP_PLUGIN, ssprintf("Can't stat archive file '%s'", pFilename)); } - curl = curl_easy_init(); - if(!curl) + CURL* curl = curl_easy_init(); + if (!curl) { - throw CABRTException(EXCEP_PLUGIN, "CFileTransfer::SendFile(): Curl library error."); + throw CABRTException(EXCEP_PLUGIN, "Curl library init error"); } /* enable uploading */ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); /* specify target */ curl_easy_setopt(curl, CURLOPT_URL, wholeURL.c_str()); - /*file handle: passed to the default callback, it will fread() it*/ curl_easy_setopt(curl, CURLOPT_READDATA, f); - /*get file size*/ curl_easy_setopt(curl, CURLOPT_INFILESIZE, buf.st_size); - /*everything is done here; result 0 means success*/ + /* everything is done here; result 0 means success */ result = curl_easy_perform(curl); - /*goodbye*/ + /* goodbye */ curl_easy_cleanup(curl); fclose(f); if (result != 0) { - update_client(_("Sending failed, try it again: ") + std::string(curl_easy_strerror((CURLcode)result))); + update_client(_("Sending failed, trying again. %s"), curl_easy_strerror((CURLcode)result)); } } /*retry the upload if not succesful, wait a bit before next try*/ - while( result!=0 && --count != 0 && (sleep(m_nRetryDelay),1) ); + while (result != 0 && --count != 0 && (sleep(m_nRetryDelay), 1)); if (count <= 0 && result != 0) { - throw CABRTException(EXCEP_PLUGIN, "CFileTransfer::SendFile(): Curl can not send a ticket."); + throw CABRTException(EXCEP_PLUGIN, "Curl can not send a ticket"); } } -string CTicketUploader::Report(const map_crash_report_t& pCrashReport, const std::string& pArgs) +string CTicketUploader::Report(const map_crash_report_t& pCrashReport, + const map_plugin_settings_t& pSettings, const string& pArgs) { - string ret; update_client(_("Creating an TicketUploader report...")); - - // Get ticket name, customer name, and do_encrypt from config settings - string ticket_name; - string customer_name; - string upload_url; - bool do_encrypt = false; - bool do_upload = false; - - customer_name = m_sCustomer; - ticket_name = m_sTicket; - upload_url = m_sURL; - do_encrypt = m_bEncrypt; - do_upload = m_bUpload; + string customer_name = m_sCustomer; + string ticket_name = m_sTicket; + string upload_url = m_sURL; + bool do_encrypt = m_bEncrypt; + bool do_upload = m_bUpload; bool have_ticket_name = false; if (ticket_name == "") @@ -248,238 +177,221 @@ string CTicketUploader::Report(const map_crash_report_t& pCrashReport, const std have_ticket_name = true; } - - // Format the time to add to the file name const int timebufmax = 256; char timebuf[timebufmax]; time_t curtime = time(NULL); - if (!strftime(timebuf,timebufmax,"-%G%m%d%k%M%S",gmtime(&curtime))) + if (!strftime(timebuf, timebufmax, "-%G%m%d%k%M%S", gmtime(&curtime))) { - Error("TicketUploader::Report:","could not format time"); + Error("Can't format time"); } - - // Create a tmp work directory, and within that the directory // that will be the root of the tarball string file_name = ticket_name + timebuf; - char TEMPLATE[] = "/tmp/rhuploadXXXXXX"; - string tmpdir_name = mkdtemp(TEMPLATE); - string tmptar_name = tmpdir_name + '/' + file_name; + char tmpdir_name[] = "/tmp/rhuploadXXXXXX"; + if (mkdtemp(tmpdir_name) == NULL) + { + Error("Can't mkdir a temporary directory in /tmp"); + } + string tmptar_name = concat_path_file(tmpdir_name, file_name.c_str()); if (mkdir(tmptar_name.c_str(),S_IRWXU)) { - Error("TicketUploader::Report:","error: could not mkdir: " + tmptar_name); + Error(ssprintf("Can't mkdir '%s'", tmptar_name.c_str()).c_str()); } - - // Copy each entry into the tarball root, // files are simply copied, strings are written to a file map_crash_report_t::const_iterator it; for (it = pCrashReport.begin(); it != pCrashReport.end(); it++) { - if (it->second[CD_TYPE] == CD_TXT) - { - string ofile_name = tmptar_name + '/' + it->first; - std::ofstream ofile(ofile_name.c_str(),std::fstream::trunc|std::fstream::binary); + if (it->second[CD_TYPE] == CD_TXT + || it->second[CD_TYPE] == CD_ATT + ) { + string ofile_name = concat_path_file(tmptar_name.c_str(), it->first.c_str()); + ofstream ofile(ofile_name.c_str(), fstream::trunc|fstream::binary); if (!ofile) { - Error("TicketUploader::Report:","error: could not open: " + ofile_name); + Error(ssprintf("Can't open '%s'", ofile_name.c_str()).c_str()); } - ofile << it->second[CD_CONTENT] << std::endl; + ofile << it->second[CD_CONTENT] << endl; ofile.close(); } - if (it->second[CD_TYPE] == CD_ATT) + else if (it->second[CD_TYPE] == CD_BIN) { - string ofile_name = tmptar_name + '/' + it->first; - std::ofstream ofile(ofile_name.c_str(),std::fstream::trunc|std::fstream::binary); - if (!ofile) + string ofile_name = concat_path_file(tmptar_name.c_str(), it->first.c_str()); + if (copy_file(it->second[CD_CONTENT].c_str(), ofile_name.c_str()) < 0) { - Error("TicketUploader::Report:","error: could not open: " + ofile_name); + throw CABRTException(EXCEP_PLUGIN, + ssprintf("Can't copy '%s' to '%s'", + it->second[CD_CONTENT].c_str(), + ofile_name.c_str() + ) + ); } - ofile << it->second[CD_CONTENT] << std::endl; - ofile.close(); - } - if (it->second[CD_TYPE] == CD_BIN) - { - string ofile_name = tmptar_name + '/' + it->first; - CopyFile(it->second[CD_CONTENT],ofile_name); } } - - // add ticket_name and customer name to tarball if (have_ticket_name) { string ofile_name = tmptar_name + "/TICKET"; - std::ofstream ofile(ofile_name.c_str(),std::fstream::trunc|std::fstream::binary); + ofstream ofile(ofile_name.c_str(), fstream::trunc|fstream::binary); if (!ofile) { - Error("TicketUploader::Report:","error: could not open: " + ofile_name); + Error(ssprintf("Can't open '%s'", ofile_name.c_str()).c_str()); } - ofile << ticket_name << std::endl; + ofile << ticket_name << endl; ofile.close(); } if (customer_name != "") { string ofile_name = tmptar_name + "/CUSTOMER"; - std::ofstream ofile(ofile_name.c_str(),std::fstream::trunc|std::fstream::binary); + ofstream ofile(ofile_name.c_str(), fstream::trunc|fstream::binary); if (!ofile) { - Error("TicketUploader::Report:","error: could not open: " + ofile_name); + Error(ssprintf("Can't open '%s'", ofile_name.c_str()).c_str()); } - ofile << customer_name << std::endl; + ofile << customer_name << endl; ofile.close(); } - - // Create the compressed tarball string outfile_basename = file_name + ".tar.gz"; - string outfile_name = tmpdir_name + '/' + outfile_basename; - string cmd = string("tar -C ") + tmpdir_name + - " --create --gzip --file=" + outfile_name + ' ' + file_name; - RunCommand(cmd); - - - + string outfile_name = concat_path_file(tmpdir_name, outfile_basename.c_str()); + string cmd = ssprintf("tar -C %s --create --gzip --file=%s %s", tmpdir_name, outfile_name.c_str(), file_name.c_str()); + RunCommand(cmd.c_str()); // encrypt if requested string key; if (do_encrypt) { - cmd = string("openssl rand -base64 48"); - key = ReadCommand(cmd); + key = ReadCommand("openssl rand -base64 48"); string infile_name = outfile_name; outfile_basename += ".aes"; outfile_name += ".aes"; - cmd = string("openssl aes-128-cbc -in ") + infile_name + - " -out " + outfile_name + " -pass stdin"; - WriteCommand(cmd,key); + cmd = ssprintf("openssl aes-128-cbc -in %s -out %s -pass stdin", infile_name.c_str(), outfile_name.c_str()); + WriteCommand(cmd.c_str(), key.c_str()); } - - // generate md5sum - cmd = string("cd ") + tmpdir_name + string("; md5sum ") + outfile_basename; - string md5sum = ReadCommand(cmd); - - + cmd = ssprintf("cd %s; md5sum %s", tmpdir_name, outfile_basename.c_str()); + string md5sum = ReadCommand(cmd.c_str()); // upload or cp to /tmp if (do_upload) { // FIXME: SendFile isn't working sometime (scp) - SendFile(upload_url,outfile_name); + SendFile(upload_url.c_str(), outfile_name.c_str()); } else { - cmd = string("cp ") + outfile_name + " /tmp/"; - RunCommand(cmd); + cmd = ssprintf("cp %s /tmp/", outfile_name.c_str()); + RunCommand(cmd.c_str()); } - - // generate a reciept telling md5sum and encryption key - std::ostringstream msgbuf; + ostringstream msgbuf; if (have_ticket_name) - msgbuf << _("Please copy this into ticket: ") << ticket_name << std::endl; + msgbuf << _("Please copy this into ticket: ") << ticket_name << endl; else - msgbuf << _("Please send this to your technical support: ") << std::endl; + msgbuf << _("Please send this to your technical support: ") << endl; if (do_upload) - msgbuf << _("RHUPLOAD: This report was sent to ") + upload_url << std::endl; + msgbuf << _("RHUPLOAD: This report was sent to ") + upload_url << endl; else - msgbuf << _("RHUPLOAD: This report was copied into /tmp/: ") << std::endl; + msgbuf << _("RHUPLOAD: This report was copied into /tmp/: ") << endl; if (have_ticket_name) - msgbuf << _("TICKET: ") << ticket_name << std::endl; - msgbuf << _("FILE: ") << outfile_basename << std::endl; - msgbuf << _("MD5SUM: ") << std::endl; + msgbuf << _("TICKET: ") << ticket_name << endl; + msgbuf << _("FILE: ") << outfile_basename << endl; + msgbuf << _("MD5SUM: ") << endl; msgbuf << md5sum; if (do_encrypt) { - msgbuf << _("KEY: aes-128-cbc") << std::endl; + msgbuf << _("KEY: aes-128-cbc") << endl; msgbuf << key; } - msgbuf << _("END: ") << std::endl; + msgbuf << _("END: ") << endl; - warn_client(msgbuf.str()); + error_msg("%s", msgbuf.str().c_str()); + string ret; if (do_upload) { string xx = _("report sent to ") + upload_url + '/' + outfile_basename; - update_client(xx); + update_client("%s", xx.c_str()); ret = xx; } else { string xx = _("report copied to /tmp/") + outfile_basename; - update_client(xx); + update_client("%s", xx.c_str()); ret = xx; } // delete the temporary directory - cmd = string("rm -rf ") + tmpdir_name; - RunCommand(cmd); + cmd = ssprintf("rm -rf %s", tmpdir_name); + RunCommand(cmd.c_str()); return ret; } void CTicketUploader::SetSettings(const map_plugin_settings_t& pSettings) { - if (pSettings.find("Customer") != pSettings.end()) + map_plugin_settings_t::const_iterator end = pSettings.end(); + map_plugin_settings_t::const_iterator it; + + it = pSettings.find("Customer"); + if (it != end) { - m_sCustomer = pSettings.find("Customer")->second; + m_sCustomer = it->second; } - if (pSettings.find("Ticket") != pSettings.end()) + it = pSettings.find("Ticket"); + if (it != end) { - m_sTicket = pSettings.find("Ticket")->second; + m_sTicket = it->second; } - if (pSettings.find("URL") != pSettings.end()) + it = pSettings.find("URL"); + if (it != end) { - m_sURL = pSettings.find("URL")->second; + m_sURL = it->second; } - if (pSettings.find("Encrypt") != pSettings.end()) + it = pSettings.find("Encrypt"); + if (it != end) { - m_bEncrypt = pSettings.find("Encrypt")->second == "yes"; + m_bEncrypt = it->second == "yes"; } - if (pSettings.find("Upload") != pSettings.end()) + it = pSettings.find("Upload"); + if (it != end) { - m_bUpload = pSettings.find("Upload")->second == "yes"; + m_bUpload = it->second == "yes"; } - if (pSettings.find("RetryCount") != pSettings.end()) + it = pSettings.find("RetryCount"); + if (it != end) { - m_nRetryCount = atoi(pSettings.find("RetryCount")->second.c_str()); + m_nRetryCount = atoi(it->second.c_str()); } - if (pSettings.find("RetryDelay") != pSettings.end()) + it = pSettings.find("RetryDelay"); + if (it != end) { - m_nRetryDelay = atoi(pSettings.find("RetryDelay")->second.c_str()); + m_nRetryDelay = atoi(it->second.c_str()); } } -map_plugin_settings_t CTicketUploader::GetSettings() +const map_plugin_settings_t& CTicketUploader::GetSettings() { - map_plugin_settings_t ret; - - ret["Customer"] = m_sCustomer; - ret["Ticket"] = m_sTicket; - ret["URL"] = m_sURL; - ret["Encrypt"] = m_bEncrypt ? "yes" : "no"; - ret["Upload"] = m_bEncrypt ? "yes" : "no"; - - std::stringstream ss; - ss << m_nRetryCount; - ret["RetryCount"] = ss.str(); - ss.str(""); - ss << m_nRetryDelay; - ret["RetryDelay"] = ss.str(); - - return ret; + m_pSettings["Customer"] = m_sCustomer; + m_pSettings["Ticket"] = m_sTicket; + m_pSettings["URL"] = m_sURL; + m_pSettings["Encrypt"] = m_bEncrypt ? "yes" : "no"; + m_pSettings["Upload"] = m_bEncrypt ? "yes" : "no"; + m_pSettings["RetryCount"] = to_string(m_nRetryCount); + m_pSettings["RetryDelay"] = to_string(m_nRetryDelay); + + return m_pSettings; } PLUGIN_INFO(REPORTER, diff --git a/lib/Plugins/TicketUploader.h b/lib/Plugins/TicketUploader.h index 68a2963..9ae3478 100644 --- a/lib/Plugins/TicketUploader.h +++ b/lib/Plugins/TicketUploader.h @@ -29,45 +29,28 @@ #include "Reporter.h" #include "CrashTypes.h" -typedef std::string string; - class CTicketUploader : public CReporter { private: - string m_sCustomer; - string m_sTicket; - string m_sURL; - bool m_bEncrypt; - bool m_bUpload; - - int m_nRetryCount; - int m_nRetryDelay; - - void Error(string func, string msg); - void CopyFile(const std::string& pSourceName, const std::string& pDestName); - // Wrappers around popen/system - // the wrapper in each case handles errors, - // and converts from string->char* - // RunCommand - a wrapper around system(cmd) - void RunCommand(string cmd); - // ReadCommand - a wrapper around popen(cmd,"r") - string ReadCommand(string cmd); - // WriteCommand - a wrapper around popen(cmd,"w") - void WriteCommand(string cmd, string input ); + std::string m_sCustomer; + std::string m_sTicket; + std::string m_sURL; + bool m_bEncrypt; + bool m_bUpload; + int m_nRetryCount; + int m_nRetryDelay; - void SendFile(const std::string& pURL, - const std::string& pFilename); + void SendFile(const char *pURL, const char *pFilename); public: CTicketUploader(); virtual ~CTicketUploader(); - virtual map_plugin_settings_t GetSettings(); + virtual const map_plugin_settings_t& GetSettings(); virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual string Report(const map_crash_report_t& pCrashReport, + virtual std::string Report(const map_crash_report_t& pCrashReport, + const map_plugin_settings_t& pSettings, const std::string& pArgs); - - }; -#endif /* TICKETUPLOADER_H_ */ +#endif diff --git a/lib/Utils/Action.h b/lib/Utils/Action.h index 1286feb..5992cbf 100644 --- a/lib/Utils/Action.h +++ b/lib/Utils/Action.h @@ -22,7 +22,6 @@ #ifndef ACTION_H_ #define ACTION_H_ -#include <string> #include "Plugin.h" /** @@ -40,8 +39,7 @@ class CAction : public CPlugin * @param pActionDir An actual directory. * @param pArgs Plugin's arguments. */ - virtual void Run(const std::string& pActionDir, - const std::string& pArgs) = 0; + virtual void Run(const char *pActionDir, const char *pArgs) = 0; }; -#endif /*ACTION_H_*/ +#endif diff --git a/lib/Utils/Analyzer.h b/lib/Utils/Analyzer.h index e5bda57..9108a91 100644 --- a/lib/Utils/Analyzer.h +++ b/lib/Utils/Analyzer.h @@ -37,20 +37,20 @@ class CAnalyzer : public CPlugin * @param pDebugDumpPath A debugdump dir containing all necessary data. * @return A local UUID. */ - virtual std::string GetLocalUUID(const std::string& pDebugDumpPath) = 0; + virtual std::string GetLocalUUID(const char *pDebugDumpDir) = 0; /** * A method, which gets a global UUID of particular crash. * @param pDebugDumpPath A debugdump dir containing all necessary data. * @return A global UUID. */ - virtual std::string GetGlobalUUID(const std::string& pDebugDumpPath) = 0; + virtual std::string GetGlobalUUID(const char *pDebugDumpDir) = 0; /** * A method, which takes care of getting all additional data needed * for computing UUIDs and creating a report. This report could be send * somewhere afterwards. * @param pDebugDumpPath A debugdump dir containing all necessary data. */ - virtual void CreateReport(const std::string& pDebugDumpPath, int force) = 0; + virtual void CreateReport(const char *pDebugDumpDir, int force) = 0; }; #endif /*ANALYZER_H_*/ diff --git a/lib/Utils/CommLayerInner.cpp b/lib/Utils/CommLayerInner.cpp index b5b8db7..307fe66 100644 --- a/lib/Utils/CommLayerInner.cpp +++ b/lib/Utils/CommLayerInner.cpp @@ -10,29 +10,48 @@ static map_uint_str_t s_mapClientID; static pthread_mutex_t s_map_mutex; static bool s_map_mutex_inited; +/* called via [p]error_msg() */ +static void warn_client(const char *msg) +{ + if (!s_pObs) + return; + + uint64_t key = uint64_t(pthread_self()); + + pthread_mutex_lock(&s_map_mutex); + map_uint_str_t::const_iterator ki = s_mapClientID.find(key); + const char* peer = (ki != s_mapClientID.end() ? ki->second.c_str() : NULL); + pthread_mutex_unlock(&s_map_mutex); + + if (peer) + s_pObs->Warning(msg, peer, key); +} + void init_daemon_logging(CObserver *pObs) { s_pObs = pObs; if (!s_map_mutex_inited) { - pthread_mutex_init(&s_map_mutex, NULL); s_map_mutex_inited = true; + pthread_mutex_init(&s_map_mutex, NULL); + g_custom_logger = &warn_client; } } -void set_client_name(const char* name) +void set_client_name(const char *name) { uint64_t key = uint64_t(pthread_self()); pthread_mutex_lock(&s_map_mutex); - if (!name) + if (!name) { s_mapClientID.erase(key); - else + } else { s_mapClientID[key] = name; + } pthread_mutex_unlock(&s_map_mutex); } -void warn_client(const std::string& pMessage) +void update_client(const char *fmt, ...) { if (!s_pObs) return; @@ -44,26 +63,16 @@ void warn_client(const std::string& pMessage) const char* peer = (ki != s_mapClientID.end() ? ki->second.c_str() : NULL); pthread_mutex_unlock(&s_map_mutex); - if (peer) - s_pObs->Warning(pMessage, peer, key); - else /* Bug: someone tries to warn_client() without set_client_name()!? */ - log("Hmm, stray %s: '%s'", __func__, pMessage.c_str()); -} - -void update_client(const std::string& pMessage) -{ - if (!s_pObs) + if (!peer) return; - uint64_t key = uint64_t(pthread_self()); + va_list p; + va_start(p, fmt); + char *msg; + int used = vasprintf(&msg, fmt, p); + va_end(p); + if (used < 0) + return; - pthread_mutex_lock(&s_map_mutex); - map_uint_str_t::const_iterator ki = s_mapClientID.find(key); - const char* peer = (ki != s_mapClientID.end() ? ki->second.c_str() : NULL); - pthread_mutex_unlock(&s_map_mutex); - - if (peer) - s_pObs->Status(pMessage, peer, key); - else - log("Hmm, stray %s: '%s'", __func__, pMessage.c_str()); + s_pObs->Status(msg, peer, key); } diff --git a/lib/Utils/CommLayerInner.h b/lib/Utils/CommLayerInner.h index d161cfc..9c22968 100644 --- a/lib/Utils/CommLayerInner.h +++ b/lib/Utils/CommLayerInner.h @@ -9,15 +9,19 @@ void init_daemon_logging(CObserver *pObs); * Set client's name (dbus ID). NULL unsets it. */ void set_client_name(const char* name); -/* Ask a client to warn the user about a non-fatal, but unexpected condition. + +/* + * Ask a client to warn the user about a non-fatal, but unexpected condition. * In GUI, it will usually be presented as a popup message. + * Usually there is no need to call it directly, just use [p]error_msg(). */ -void warn_client(const std::string& pMessage); -/* Logs a message to a client. +//now static: void warn_client(const char *msg); + +/* + * Logs a message to a client. * In UI, it will usually appear as a new status line message in GUI, * or as a new message line in CLI. */ -void update_client(const std::string& pMessage); - -#endif /* COMMLAYERINNER_H_ */ +void update_client(const char *fmt, ...); +#endif diff --git a/lib/Utils/DBusCommon.h b/lib/Utils/DBusCommon.h index b3e3af2..2e3ed8a 100644 --- a/lib/Utils/DBusCommon.h +++ b/lib/Utils/DBusCommon.h @@ -22,8 +22,8 @@ #include "CrashTypes.h" -#define CC_DBUS_NAME "com.redhat.abrt" -#define CC_DBUS_PATH "/com/redhat/abrt" -#define CC_DBUS_IFACE "com.redhat.abrt" +#define ABRTD_DBUS_NAME "com.redhat.abrt" +#define ABRTD_DBUS_PATH "/com/redhat/abrt" +#define ABRTD_DBUS_IFACE "com.redhat.abrt" #endif diff --git a/lib/Utils/DebugDump.cpp b/lib/Utils/DebugDump.cpp index 43eb324..bf793bb 100644 --- a/lib/Utils/DebugDump.cpp +++ b/lib/Utils/DebugDump.cpp @@ -23,45 +23,41 @@ #include <iostream> #include <sstream> #include <sys/utsname.h> -#include <magic.h> +//#include <magic.h> #include "abrtlib.h" #include "DebugDump.h" #include "ABRTException.h" #include "CommLayerInner.h" -/* Is it "." or ".."? */ -/* abrtlib candidate */ -static bool dot_or_dotdot(const char *filename) -{ - if (filename[0] != '.') return false; - if (filename[1] == '\0') return true; - if (filename[1] != '.') return false; - if (filename[2] == '\0') return true; - return false; -} - static bool isdigit_str(const char *str) { - while (*str) + do { if (*str < '0' || *str > '9') return false; str++; - } + } while (*str); return true; } -static std::string RemoveBackSlashes(const std::string& pDir); +static std::string RemoveBackSlashes(const char *pDir) +{ + unsigned len = strlen(pDir); + while (len != 0 && pDir[len-1] == '/') + len--; + return std::string(pDir, len); +} + static bool ExistFileDir(const char* pPath); -static void LoadTextFile(const std::string& pPath, std::string& pData); +static void LoadTextFile(const char *pPath, std::string& pData); CDebugDump::CDebugDump() : m_sDebugDumpDir(""), - m_bOpened(false), m_pGetNextFileDir(NULL), - m_nLockfileFD(-1) + m_bOpened(false), + m_bLocked(false) {} -void CDebugDump::Open(const std::string& pDir) +void CDebugDump::Open(const char *pDir) { if (m_bOpened) { @@ -70,7 +66,7 @@ void CDebugDump::Open(const std::string& pDir) m_sDebugDumpDir = RemoveBackSlashes(pDir); if (!ExistFileDir(m_sDebugDumpDir.c_str())) { - throw CABRTException(EXCEP_DD_OPEN, "CDebugDump::CDebugDump(): "+m_sDebugDumpDir+" does not exist."); + throw CABRTException(EXCEP_DD_OPEN, "CDebugDump::CDebugDump(): " + m_sDebugDumpDir + " does not exist."); } Lock(); m_bOpened = true; @@ -96,10 +92,49 @@ static bool ExistFileDir(const char* pPath) return false; } -static int GetAndSetLock(const char* pLockFile, const char* pPID) +static bool GetAndSetLock(const char* pLockFile, const char* pPID) { - int fd; + while (symlink(pPID, pLockFile) != 0) + { + if (errno != EEXIST) + perror_msg_and_die("Can't create lock file '%s'", pLockFile); + char pid_buf[sizeof(pid_t)*3 + 4]; + ssize_t r = readlink(pLockFile, pid_buf, sizeof(pid_buf) - 1); + if (r < 0) + perror_msg_and_die("Can't read lock file '%s'", pLockFile); + pid_buf[r] = '\0'; + + if (strcmp(pid_buf, pPID) == 0) + { + log("Lock file '%s' is already locked by us", pLockFile); + return false; + } + if (isdigit_str(pid_buf)) + { + if (access(ssprintf("/proc/%s", pid_buf).c_str(), F_OK) == 0) + { + log("Lock file '%s' is locked by process %s", pLockFile, pid_buf); + return false; + } + log("Lock file '%s' was locked by process %s, but it crashed?", pLockFile, pid_buf); + } + /* The file may be deleted by now by other process. Ignore ENOENT */ + if (unlink(pLockFile) != 0 && errno != ENOENT) + { + perror_msg_and_die("Can't remove stale lock file '%s'", pLockFile); + } + } + + VERB1 log("Locked '%s'", pLockFile); + return true; + +#if 0 +/* Old code was using ordinary files instead of symlinks, + * but it had a race window between open and write, during which file was + * empty. It was seen to happen in practice. + */ + int fd; while ((fd = open(pLockFile, O_WRONLY | O_CREAT | O_EXCL, 0640)) < 0) { if (errno != EEXIST) @@ -148,20 +183,22 @@ static int GetAndSetLock(const char* pLockFile, const char* pPID) /* close(fd); - not needed, exiting does it too */ perror_msg_and_die("Can't write lock file '%s'", pLockFile); } + close(fd); VERB1 log("Locked '%s'", pLockFile); - return fd; + return true; +#endif } void CDebugDump::Lock() { - if (m_nLockfileFD >= 0) + if (m_bLocked) error_msg_and_die("Locking bug on '%s'", m_sDebugDumpDir.c_str()); std::string lockFile = m_sDebugDumpDir + ".lock"; char pid_buf[sizeof(int)*3 + 2]; sprintf(pid_buf, "%u", (unsigned)getpid()); - while ((m_nLockfileFD = GetAndSetLock(lockFile.c_str(), pid_buf)) < 0) + while ((m_bLocked = GetAndSetLock(lockFile.c_str(), pid_buf)) != true) { usleep(500000); } @@ -169,27 +206,26 @@ void CDebugDump::Lock() void CDebugDump::UnLock() { - if (m_nLockfileFD >= 0) + if (m_bLocked) { + m_bLocked = false; std::string lockFile = m_sDebugDumpDir + ".lock"; - close(m_nLockfileFD); - m_nLockfileFD = -1; xunlink(lockFile.c_str()); VERB1 log("UnLocked '%s'", lockFile.c_str()); } } -void CDebugDump::Create(const std::string& pDir, uid_t uid) +void CDebugDump::Create(const char *pDir, int64_t uid) { if (m_bOpened) { - throw CABRTException(EXCEP_ERROR, "CDebugDump::CDebugDump(): DebugDump is already opened."); + throw CABRTException(EXCEP_ERROR, "DebugDump is already opened"); } m_sDebugDumpDir = RemoveBackSlashes(pDir); if (ExistFileDir(m_sDebugDumpDir.c_str())) { - throw CABRTException(EXCEP_DD_OPEN, "CDebugDump::CDebugDump(): "+m_sDebugDumpDir+" already exists."); + throw CABRTException(EXCEP_DD_OPEN, ssprintf("'%s' already exists", m_sDebugDumpDir.c_str())); } Lock(); @@ -199,13 +235,13 @@ void CDebugDump::Create(const std::string& pDir, uid_t uid) { UnLock(); m_bOpened = false; - throw CABRTException(EXCEP_DD_OPEN, "CDebugDump::Create(): Cannot create dir: " + pDir); + throw CABRTException(EXCEP_DD_OPEN, ssprintf("Can't create dir '%s'", pDir)); } if (chmod(m_sDebugDumpDir.c_str(), 0700) == -1) { UnLock(); m_bOpened = false; - throw CABRTException(EXCEP_DD_OPEN, "CDebugDump::Create(): Cannot change permissions, dir: " + pDir); + throw CABRTException(EXCEP_DD_OPEN, ssprintf("Can't change mode of '%s'", pDir)); } struct passwd* pw = getpwuid(uid); gid_t gid = pw ? pw->pw_gid : uid; @@ -216,14 +252,15 @@ void CDebugDump::Create(const std::string& pDir, uid_t uid) perror_msg("can't change '%s' ownership to %u:%u", m_sDebugDumpDir.c_str(), (int)uid, (int)gid); } - SaveText(FILENAME_UID, ssprintf("%u", (int)uid)); + SaveText(FILENAME_UID, to_string(uid).c_str()); SaveKernelArchitectureRelease(); - SaveTime(); + time_t t = time(NULL); + SaveText(FILENAME_TIME, to_string(t).c_str()); } -static void DeleteFileDir(const std::string& pDir) +static void DeleteFileDir(const char *pDir) { - DIR *dir = opendir(pDir.c_str()); + DIR *dir = opendir(pDir); if (!dir) return; @@ -232,26 +269,28 @@ static void DeleteFileDir(const std::string& pDir) { if (dot_or_dotdot(dent->d_name)) continue; - std::string fullPath = pDir + "/" + dent->d_name; + std::string fullPath = concat_path_file(pDir, dent->d_name); if (unlink(fullPath.c_str()) == -1) { if (errno != EISDIR) { closedir(dir); - throw CABRTException(EXCEP_DD_DELETE, std::string(__func__) + ": Cannot remove file: " + fullPath); + throw CABRTException(EXCEP_DD_DELETE, ssprintf("Can't remove dir %s", fullPath.c_str())); } - DeleteFileDir(fullPath); + DeleteFileDir(fullPath.c_str()); } } closedir(dir); - if (remove(pDir.c_str()) == -1) + if (remove(pDir) == -1) { - throw CABRTException(EXCEP_DD_DELETE, std::string(__func__) + ": Cannot remove dir: " + pDir); + throw CABRTException(EXCEP_DD_DELETE, ssprintf("Can't remove dir %s", pDir)); } } -static bool IsTextFile(const std::string& pName) +static bool IsTextFile(const char *name) { +/* This idiotic library thinks that file containing just "0" is not text (!!) + magic_t m = magic_open(MAGIC_MIME_TYPE); if (m == NULL) @@ -280,16 +319,24 @@ static bool IsTextFile(const std::string& pName) magic_close(m); return isText; -} + */ + int fd = open(name, O_RDONLY); + if (fd < 0) + return false; -static std::string RemoveBackSlashes(const std::string& pDir) -{ - std::string ret = pDir; - while (ret[ret.length() - 1] == '/') + unsigned char buf[4*1024]; + int r = full_read(fd, buf, sizeof(buf)); + close(fd); + + while (--r >= 0) { - ret = ret.substr(0, ret.length() - 2); + if (buf[r] >= 0x7f) + return false; + /* Among control chars, only '\t','\n' etc are allowed */ + if (buf[r] < ' ' && !isspace(buf[r])) + return false; } - return ret; + return true; } void CDebugDump::Delete() @@ -298,7 +345,7 @@ void CDebugDump::Delete() { return; } - DeleteFileDir(m_sDebugDumpDir); + DeleteFileDir(m_sDebugDumpDir.c_str()); } void CDebugDump::Close() @@ -322,100 +369,47 @@ void CDebugDump::SaveKernelArchitectureRelease() } std::string release; LoadTextFile("/etc/redhat-release", release); - SaveText(FILENAME_RELEASE, release); + const char *release_ptr = release.c_str(); + unsigned len_1st_str = strchrnul(release_ptr, '\n') - release_ptr; + release.erase(len_1st_str); /* usually simply removes trailing '\n' */ + SaveText(FILENAME_RELEASE, release.c_str()); } -void CDebugDump::SaveTime() +static void LoadTextFile(const char *pPath, std::string& pData) { - time_t t = time(NULL); - SaveText(FILENAME_TIME, to_string(t)); -} - -static void LoadTextFile(const std::string& pPath, std::string& pData) -{ - std::ifstream fIn; + FILE *fp = fopen(pPath, "r"); + if (!fp) + { + throw CABRTException(EXCEP_DD_LOAD, ssprintf("Can't open file '%s'", pPath)); + } pData = ""; - fIn.open(pPath.c_str()); - if (fIn.is_open()) + int ch; + while ((ch = fgetc(fp)) != EOF) { - // TODO: rewrite this - int ch; - while ((ch = fIn.get())!= EOF) + if (ch == '\0') { - if (ch == 0) - { - pData += " "; - } - else if (isspace(ch) || (isascii(ch) && !iscntrl(ch))) - { - pData += ch; - } + pData += ' '; } - fIn.close(); - } - else - { - throw CABRTException(EXCEP_DD_LOAD, std::string(__func__) + ": Cannot open file " + pPath); - } -} - -static void LoadBinaryFile(const std::string& pPath, char** pData, unsigned int* pSize) -{ - std::ifstream fIn; - fIn.open(pPath.c_str(), std::ios::binary | std::ios::ate); - unsigned int size; - if (fIn.is_open()) - { - size = fIn.tellg(); - char *data = new char [size]; - fIn.read(data, size); - - *pData = data; - *pSize = size; - - fIn.close(); - } - else - { - throw CABRTException(EXCEP_DD_LOAD, std::string(__func__) + ": Cannot open file " + pPath); - } -} - -static void SaveTextFile(const std::string& pPath, const std::string& pData) -{ - std::ofstream fOut; - fOut.open(pPath.c_str()); - if (fOut.is_open()) - { - fOut << pData; - if (!fOut.good()) + else if (isspace(ch) || (isascii(ch) && !iscntrl(ch))) { - throw CABRTException(EXCEP_DD_SAVE, std::string(__func__) + ": Cannot save file " + pPath); + pData += ch; } - fOut.close(); - } - else - { - throw CABRTException(EXCEP_DD_SAVE, std::string(__func__) + ": Cannot open file " + pPath); } + fclose(fp); } -static void SaveBinaryFile(const std::string& pPath, const char* pData, const unsigned pSize) +static void SaveBinaryFile(const char *pPath, const char* pData, unsigned pSize) { - std::ofstream fOut; - fOut.open(pPath.c_str(), std::ios::binary); - if (fOut.is_open()) + int fd = open(pPath, O_WRONLY | O_TRUNC | O_CREAT, 0666); + if (fd < 0) { - fOut.write(pData, pSize); - if (!fOut.good()) - { - throw CABRTException(EXCEP_DD_SAVE, std::string(__func__) + ": Cannot save file " + pPath); - } - fOut.close(); + throw CABRTException(EXCEP_DD_SAVE, ssprintf("Can't open file '%s'", pPath)); } - else + unsigned r = full_write(fd, pData, pSize); + close(fd); + if (r != pSize) { - throw CABRTException(EXCEP_DD_SAVE, std::string(__func__) + ": Cannot open file " + pPath); + throw CABRTException(EXCEP_DD_SAVE, ssprintf("Can't save file '%s'", pPath)); } } @@ -425,36 +419,27 @@ void CDebugDump::LoadText(const char* pName, std::string& pData) { throw CABRTException(EXCEP_DD_OPEN, "CDebugDump::LoadText(): DebugDump is not opened."); } - std::string fullPath = m_sDebugDumpDir + "/" + pName; - LoadTextFile(fullPath, pData); -} -void CDebugDump::LoadBinary(const char* pName, char** pData, unsigned int* pSize) -{ - if (!m_bOpened) - { - throw CABRTException(EXCEP_DD_OPEN, "CDebugDump::LoadBinary(): DebugDump is not opened."); - } - std::string fullPath = m_sDebugDumpDir + "/" + pName; - LoadBinaryFile(fullPath, pData, pSize); + std::string fullPath = m_sDebugDumpDir + '/' + pName; + LoadTextFile(fullPath.c_str(), pData); } -void CDebugDump::SaveText(const char* pName, const std::string& pData) +void CDebugDump::SaveText(const char* pName, const char* pData) { if (!m_bOpened) { throw CABRTException(EXCEP_DD_OPEN, "CDebugDump::SaveText(): DebugDump is not opened."); } std::string fullPath = m_sDebugDumpDir + "/" + pName; - SaveTextFile(fullPath, pData); + SaveBinaryFile(fullPath.c_str(), pData, strlen(pData)); } -void CDebugDump::SaveBinary(const char* pName, const char* pData, const unsigned int pSize) +void CDebugDump::SaveBinary(const char* pName, const char* pData, unsigned pSize) { if (!m_bOpened) { throw CABRTException(EXCEP_DD_OPEN, "CDebugDump::SaveBinary(): DebugDump is not opened."); } std::string fullPath = m_sDebugDumpDir + "/" + pName; - SaveBinaryFile(fullPath, pData, pSize); + SaveBinaryFile(fullPath.c_str(), pData, pSize); } void CDebugDump::InitGetNextFile() @@ -486,17 +471,17 @@ bool CDebugDump::GetNextFile(std::string& pFileName, std::string& pContent, bool { if (is_regular_file(dent, m_sDebugDumpDir.c_str())) { - std::string fullname = m_sDebugDumpDir + "/" + dent->d_name; + std::string fullname = m_sDebugDumpDir + '/' + dent->d_name; pFileName = dent->d_name; - if (IsTextFile(fullname)) + if (IsTextFile(fullname.c_str())) { LoadText(dent->d_name, pContent); pIsTextFile = true; } else { - pContent = ""; + pContent.clear(); pIsTextFile = false; } return true; @@ -506,4 +491,3 @@ bool CDebugDump::GetNextFile(std::string& pFileName, std::string& pContent, bool m_pGetNextFileDir = NULL; return false; } - diff --git a/lib/Utils/DebugDump.h b/lib/Utils/DebugDump.h index d6ff4f9..b48a386 100644 --- a/lib/Utils/DebugDump.h +++ b/lib/Utils/DebugDump.h @@ -25,6 +25,7 @@ #include <string> #include <dirent.h> +#include <stdint.h> #define FILENAME_ARCHITECTURE "architecture" #define FILENAME_KERNEL "kernel" @@ -39,17 +40,17 @@ #define FILENAME_REASON "reason" #define FILENAME_COMMENT "comment" #define FILENAME_REPRODUCE "reproduce" +#define FILENAME_RATING "rating" class CDebugDump { private: std::string m_sDebugDumpDir; - bool m_bOpened; DIR* m_pGetNextFileDir; - int m_nLockfileFD; + bool m_bOpened; + bool m_bLocked; void SaveKernelArchitectureRelease(); - void SaveTime(); void Lock(); void UnLock(); @@ -58,18 +59,17 @@ class CDebugDump CDebugDump(); ~CDebugDump() { Close(); } - void Open(const std::string& pDir); - void Create(const std::string& pDir, uid_t nUID); + void Open(const char *pDir); + void Create(const char *pDir, int64_t uid); void Delete(); void Close(); bool Exist(const char* pFileName); void LoadText(const char* pName, std::string& pData); - void LoadBinary(const char* pName, char** pData, unsigned int* pSize); - void SaveText(const char* pName, const std::string& pData); - void SaveBinary(const char* pName, const char* pData, const unsigned int pSize); + void SaveText(const char* pName, const char *pData); + void SaveBinary(const char* pName, const char* pData, unsigned pSize); void InitGetNextFile(); bool GetNextFile(std::string& pFileName, std::string& pContent, bool& pIsTextFile); diff --git a/lib/Utils/Makefile.am b/lib/Utils/Makefile.am index 713fe86..68c925f 100644 --- a/lib/Utils/Makefile.am +++ b/lib/Utils/Makefile.am @@ -1,15 +1,21 @@ lib_LTLIBRARIES = libABRTUtils.la +# Not used just yet: +# time.cpp +# xconnect.cpp + libABRTUtils_la_SOURCES = \ xfuncs.cpp \ + encbase64.cpp \ read_write.cpp \ logging.cpp \ copyfd.cpp \ + skip_whitespace.cpp \ CrashTypesSocket.cpp \ DebugDump.h DebugDump.cpp \ CommLayerInner.h CommLayerInner.cpp \ abrt_dbus.h abrt_dbus.cpp \ - Plugin.h Plugin.cpp \ + Plugin.h Plugin.cpp make_descr.cpp \ Polkit.h Polkit.cpp \ Action.h Database.h Reporter.h Analyzer.h \ Observer.h \ @@ -31,7 +37,6 @@ libABRTUtils_la_LDFLAGS = \ $(DL_LIBS) \ $(DBUS_LIBS) libABRTUtils_la_LIBADD = \ - -lmagic \ $(POLKIT_LIBS) install-data-local: diff --git a/lib/Utils/Observer.h b/lib/Utils/Observer.h index d6ec6f3..db74865 100644 --- a/lib/Utils/Observer.h +++ b/lib/Utils/Observer.h @@ -8,8 +8,8 @@ class CObserver { public: virtual ~CObserver() {} - virtual void Status(const std::string& pMessage, const char* peer, uint64_t pDest) = 0; - virtual void Warning(const std::string& pMessage, const char* peer, uint64_t pDest) = 0; + virtual void Status(const char *pMessage, const char* peer, uint64_t pDest) = 0; + virtual void Warning(const char *pMessage, const char* peer, uint64_t pDest) = 0; }; #endif diff --git a/lib/Utils/Plugin.cpp b/lib/Utils/Plugin.cpp index 161ead8..4d561b4 100644 --- a/lib/Utils/Plugin.cpp +++ b/lib/Utils/Plugin.cpp @@ -19,9 +19,18 @@ #include "Plugin.h" +CPlugin::CPlugin() {} + /* class CPlugin's virtuals */ CPlugin::~CPlugin() {} void CPlugin::Init() {} void CPlugin::DeInit() {} -void CPlugin::SetSettings(const map_plugin_settings_t& pSettings) {} -map_plugin_settings_t CPlugin::GetSettings() {return map_plugin_settings_t();} +void CPlugin::SetSettings(const map_plugin_settings_t& pSettings) +{ + m_pSettings = pSettings; +} + +const map_plugin_settings_t& CPlugin::GetSettings() +{ + return m_pSettings; +} diff --git a/lib/Utils/Plugin.h b/lib/Utils/Plugin.h index 3929023..f93f7e7 100644 --- a/lib/Utils/Plugin.h +++ b/lib/Utils/Plugin.h @@ -24,17 +24,10 @@ #define PLUGIN_H_ #include "abrt_types.h" - -#define PLUGINS_MAGIC_NUMBER 6 - -#define PLUGINS_CONF_EXTENSION "conf" -#define PLUGINS_LIB_EXTENSION "so" -#define PLUGINS_LIB_PREFIX "lib" - +#include "CrashTypes.h" #if HAVE_CONFIG_H #include <config.h> #endif - #if ENABLE_NLS #include <libintl.h> #define _(S) gettext(S) @@ -42,13 +35,23 @@ #define _(S) (S) #endif +#define PLUGINS_MAGIC_NUMBER 6 + +#define PLUGINS_CONF_EXTENSION "conf" +#define PLUGINS_LIB_EXTENSION "so" +#define PLUGINS_LIB_PREFIX "lib" + /** * An abstract class. The class defines a common plugin interface. If a plugin * has some settings, then a *Settings(*) method has to be written. */ class CPlugin { + protected: + map_plugin_settings_t m_pSettings; + public: + CPlugin(); /** * A destructor. */ @@ -70,7 +73,7 @@ class CPlugin * A method, which return current settings. It is not mandatory method. * @return Plugin's settings */ - virtual map_plugin_settings_t GetSettings(); + virtual const map_plugin_settings_t& GetSettings(); }; /** @@ -115,4 +118,8 @@ typedef struct SPluginInfo PLUGINS_MAGIC_NUMBER,\ }; -#endif /* PLUGIN_H_ */ +/* helper finctions */ +std::string make_description_bz(const map_crash_report_t& pCrashReport); +std::string make_description_logger(const map_crash_report_t& pCrashReport); + +#endif diff --git a/lib/Utils/Reporter.h b/lib/Utils/Reporter.h index c74a10c..f278899 100644 --- a/lib/Utils/Reporter.h +++ b/lib/Utils/Reporter.h @@ -41,6 +41,7 @@ class CReporter : public CPlugin * @retun A message which can be displayed after a report is created. */ virtual std::string Report(const map_crash_report_t& pCrashReport, + const map_plugin_settings_t& pSettings, const std::string& pArgs) = 0; }; diff --git a/lib/Utils/copyfd.cpp b/lib/Utils/copyfd.cpp index cda52b0..9abe752 100644 --- a/lib/Utils/copyfd.cpp +++ b/lib/Utils/copyfd.cpp @@ -105,3 +105,25 @@ off_t copyfd_eof(int fd1, int fd2) { return full_fd_action(fd1, fd2, 0); } + +off_t copy_file(const char *src_name, const char *dst_name) +{ + off_t r; + int src = open(src_name, O_RDONLY); + if (src < 0) + { + perror_msg("Can't open '%s'", src_name); + return -1; + } + int dst = open(dst_name, O_WRONLY | O_TRUNC | O_CREAT, 0666); + if (dst < 0) + { + close(src); + perror_msg("Can't open '%s'", dst_name); + return -1; + } + r = copyfd_eof(src, dst); + close(src); + close(dst); + return r; +} diff --git a/lib/Utils/encbase64.cpp b/lib/Utils/encbase64.cpp new file mode 100644 index 0000000..6a6f1f7 --- /dev/null +++ b/lib/Utils/encbase64.cpp @@ -0,0 +1,78 @@ +/* + * Copyright 2006 Rob Landley <rob@landley.net> + * + * Licensed under GPLv2 or later. + */ +#include "abrtlib.h" /* xmalloc */ + +/* Conversion table for base 64 */ +static const char tbl_base64[65 /*+ 2*/] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', + '=' /* termination character */, + // '\n', '\0' /* needed for uudecode.c */ +}; + +/* Conversion table for uuencode +const char tbl_uuencode[65] ALIGN1 = { + '`', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`' +}; +*/ + +/* + * Encode bytes at S of length LENGTH. + * Result will be 0-terminated, and must point to a writable + * buffer of at least 1+BASE64_LENGTH(length) bytes, + * where BASE64_LENGTH(len) = 4 * ((LENGTH + 2) / 3) + */ +static void encode_64bit(char *p, const void *src, int length, const char *tbl) +{ + const unsigned char *s = (const unsigned char *)src; + + /* Transform the 3x8 bits to 4x6 bits */ + while (length > 0) { + unsigned s1, s2; + + /* Are s[1], s[2] valid or should be assumed 0? */ + s1 = s2 = 0; + length -= 3; /* can be >=0, -1, -2 */ + if (length >= -1) { + s1 = s[1]; + if (length >= 0) + s2 = s[2]; + } + *p++ = tbl[s[0] >> 2]; + *p++ = tbl[((s[0] & 3) << 4) + (s1 >> 4)]; + *p++ = tbl[((s1 & 0xf) << 2) + (s2 >> 6)]; + *p++ = tbl[s2 & 0x3f]; + s += 3; + } + /* Zero-terminate */ + *p = '\0'; + /* If length is -2 or -1, pad last char or two */ + while (length) { + *--p = tbl[64]; + length++; + } +} + +char *encode_base64(const void *src, int length) +{ + char *dst = (char *)xmalloc(4 * ((length + 2) / 3) + 1); + encode_64bit(dst, src, length, tbl_base64); + return dst; +} diff --git a/lib/Utils/logging.cpp b/lib/Utils/logging.cpp index f70d23f..cae609b 100644 --- a/lib/Utils/logging.cpp +++ b/lib/Utils/logging.cpp @@ -7,19 +7,18 @@ #include <syslog.h> int xfunc_error_retval = EXIT_FAILURE; - int g_verbose; +int logmode = LOGMODE_STDIO; +const char *msg_prefix = ""; +const char *msg_eol = "\n"; +void (*g_custom_logger)(const char*); void xfunc_die(void) { exit(xfunc_error_retval); } -const char *msg_prefix = ""; -const char *msg_eol = "\n"; -int logmode = LOGMODE_STDIO; - -void verror_msg(const char *s, va_list p, const char* strerr) +static void verror_msg_helper(const char *s, va_list p, const char* strerr, int flags) { char *msg; int prefix_len, strerr_len, msgeol_len, used; @@ -27,9 +26,6 @@ void verror_msg(const char *s, va_list p, const char* strerr) if (!logmode) return; - if (!s) /* nomsg[_and_die] uses NULL fmt */ - s = ""; /* some libc don't like printf(NULL) */ - used = vasprintf(&msg, s, p); if (used < 0) return; @@ -51,7 +47,7 @@ void verror_msg(const char *s, va_list p, const char* strerr) memcpy(msg, msg_prefix, prefix_len); } if (strerr) { - if (s[0]) { /* not perror_nomsg? */ + if (s[0]) { msg[used++] = ':'; msg[used++] = ' '; } @@ -60,24 +56,26 @@ void verror_msg(const char *s, va_list p, const char* strerr) } strcpy(&msg[used], msg_eol); - if (logmode & LOGMODE_STDIO) { + if (flags & LOGMODE_STDIO) { fflush(stdout); full_write(STDERR_FILENO, msg, used + msgeol_len); } - if (logmode & LOGMODE_SYSLOG) { + if (flags & LOGMODE_SYSLOG) { syslog(LOG_ERR, "%s", msg + prefix_len); } + if ((flags & LOGMODE_CUSTOM) && g_custom_logger) { + g_custom_logger(msg + prefix_len); + } free(msg); } -void error_msg_and_die(const char *s, ...) +void log_msg(const char *s, ...) { va_list p; va_start(p, s); - verror_msg(s, p, NULL); + verror_msg_helper(s, p, NULL, logmode); va_end(p); - xfunc_die(); } void error_msg(const char *s, ...) @@ -85,8 +83,18 @@ void error_msg(const char *s, ...) va_list p; va_start(p, s); - verror_msg(s, p, NULL); + verror_msg_helper(s, p, NULL, (logmode | LOGMODE_CUSTOM)); + va_end(p); +} + +void error_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + verror_msg_helper(s, p, NULL, (logmode | LOGMODE_CUSTOM)); va_end(p); + xfunc_die(); } void perror_msg_and_die(const char *s, ...) @@ -95,7 +103,7 @@ void perror_msg_and_die(const char *s, ...) va_start(p, s); /* Guard against "<error message>: Success" */ - verror_msg(s, p, errno ? strerror(errno) : NULL); + verror_msg_helper(s, p, errno ? strerror(errno) : NULL, (logmode | LOGMODE_CUSTOM)); va_end(p); xfunc_die(); } @@ -106,7 +114,7 @@ void perror_msg(const char *s, ...) va_start(p, s); /* Guard against "<error message>: Success" */ - verror_msg(s, p, errno ? strerror(errno) : NULL); + verror_msg_helper(s, p, errno ? strerror(errno) : NULL, (logmode | LOGMODE_CUSTOM)); va_end(p); } @@ -122,5 +130,5 @@ void simple_perror_msg(const char *s) void die_out_of_memory(void) { - error_msg_and_die("Out of memory, exiting"); + error_msg_and_die("Out of memory, exiting"); } diff --git a/lib/Utils/make_descr.cpp b/lib/Utils/make_descr.cpp new file mode 100644 index 0000000..1352149 --- /dev/null +++ b/lib/Utils/make_descr.cpp @@ -0,0 +1,133 @@ +#include "abrtlib.h" +//#include "abrt_types.h" +#include "CrashTypes.h" +#include "DebugDump.h" /* FILENAME_ARCHITECTURE etc */ + +using namespace std; + +static void add_content(bool &was_multiline, string& description, const char *header, const char *content) +{ + /* We separate multiline contents with emply line */ + if (was_multiline) + description += '\n'; + + while (content[0] == '\n') + content++; + + if (strchr(content, '\n') == NULL) + { + if (skip_whitespace(content)[0] == '\0') + { + /* empty, dont report at all */ + return; + } + /* one string value, like OS release */ + description += header; + description += ": "; + description += content; + description += '\n'; + was_multiline = 0; + } + else + { + /* multi-string value, like backtrace */ + if (!was_multiline && description.size() != 0) /* if wasn't yet separated */ + description += '\n'; /* do it now */ + description += header; + description += "\n-----\n"; + description += content; + if (content[strlen(content) - 1] != '\n') + description += '\n'; + was_multiline = 1; + } +} + +string make_description_bz(const map_crash_report_t& pCrashReport) +{ + string description; + + map_crash_report_t::const_iterator it; + map_crash_report_t::const_iterator end = pCrashReport.end(); + + bool was_multiline = 0; + it = pCrashReport.find(CD_REPRODUCE); + if (it != end && it->second[CD_CONTENT] != "1.\n2.\n3.\n") + { + add_content(was_multiline, description, "How to reproduce", it->second[CD_CONTENT].c_str()); + } + + it = pCrashReport.find(CD_COMMENT); + if (it != end) + { + add_content(was_multiline, description, "Comment", it->second[CD_CONTENT].c_str()); + } + + it = pCrashReport.begin(); + for (; it != end; it++) + { + const string &filename = it->first; + const string &type = it->second[CD_TYPE]; + const string &content = it->second[CD_CONTENT]; + if (type == CD_TXT) + { + if (filename != CD_UUID + && filename != FILENAME_ARCHITECTURE + && filename != FILENAME_RELEASE + && filename != CD_REPRODUCE + && filename != CD_COMMENT + ) { + add_content(was_multiline, description, filename.c_str(), content.c_str()); + } + } + else if (type == CD_ATT) + { + add_content(was_multiline, description, "Attached file", filename.c_str()); + } + } + + return description; +} + +string make_description_logger(const map_crash_report_t& pCrashReport) +{ + string description; + string long_description; + + map_crash_report_t::const_iterator it = pCrashReport.begin(); + for (; it != pCrashReport.end(); it++) + { + const string &filename = it->first; + const string &type = it->second[CD_TYPE]; + const string &content = it->second[CD_CONTENT]; + if (type == CD_TXT + || type == CD_ATT + || type == CD_BIN + ) { + if (content == "1.\n2.\n3.\n") + continue; /* user did not change default "How to reproduce" */ + + bool was_multiline = 0; + string tmp; + add_content(was_multiline, tmp, filename.c_str(), content.c_str()); + + if (was_multiline) + { + if (long_description.size() != 0) + long_description += '\n'; + long_description += tmp; + } + else + { + description += tmp; + } + } + } + + if (description.size() != 0 && long_description.size() != 0) + { + description += '\n'; + description += long_description; + } + + return description; +} diff --git a/lib/Utils/skip_whitespace.cpp b/lib/Utils/skip_whitespace.cpp new file mode 100644 index 0000000..816928b --- /dev/null +++ b/lib/Utils/skip_whitespace.cpp @@ -0,0 +1,22 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ +#include "abrtlib.h" + +char* skip_whitespace(const char *s) +{ + /* NB: isspace('\0') returns 0 */ + while (isspace(*s)) ++s; + + return (char *) s; +} + +char* skip_non_whitespace(const char *s) +{ + while (*s && !isspace(*s)) ++s; + + return (char *) s; +} diff --git a/lib/Utils/time.cpp b/lib/Utils/time.cpp new file mode 100644 index 0000000..37ade2c --- /dev/null +++ b/lib/Utils/time.cpp @@ -0,0 +1,65 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Licensed under GPL version 2, see file LICENSE in this tarball for details. + */ +#include "abrtlib.h" + +#define ENABLE_MONOTONIC_SYSCALL 1 + +#if ENABLE_MONOTONIC_SYSCALL + +#include <sys/syscall.h> +/* Old glibc (< 2.3.4) does not provide this constant. We use syscall + * directly so this definition is safe. */ +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC 1 +#endif + +/* libc has incredibly messy way of doing this, + * typically requiring -lrt. We just skip all this mess */ +static void get_mono(struct timespec *ts) +{ + if (syscall(__NR_clock_gettime, CLOCK_MONOTONIC, ts)) + error_msg_and_die("clock_gettime(MONOTONIC) failed"); +} +unsigned long long monotonic_ns(void) +{ + struct timespec ts; + get_mono(&ts); + return ts.tv_sec * 1000000000ULL + ts.tv_nsec; +} +unsigned long long monotonic_us(void) +{ + struct timespec ts; + get_mono(&ts); + return ts.tv_sec * 1000000ULL + ts.tv_nsec/1000; +} +unsigned monotonic_sec(void) +{ + struct timespec ts; + get_mono(&ts); + return ts.tv_sec; +} + +#else + +unsigned long long monotonic_ns(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000; +} +unsigned long long monotonic_us(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000ULL + tv.tv_usec; +} +unsigned monotonic_sec(void) +{ + return time(NULL); +} + +#endif diff --git a/lib/Utils/xconnect.cpp b/lib/Utils/xconnect.cpp new file mode 100644 index 0000000..746edd6 --- /dev/null +++ b/lib/Utils/xconnect.cpp @@ -0,0 +1,416 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Connect to host at port using address resolution from getaddrinfo + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +#include "abrtlib.h" +#include <sys/socket.h> /* netinet/in.h needs it */ +#include <netinet/in.h> +#include <net/if.h> +#include <sys/un.h> +#include <netdb.h> + +#define ENABLE_FEATURE_IPV6 1 +#define ENABLE_FEATURE_PREFER_IPV4_ADDRESS 1 + +static const int const_int_1 = 1; + +void setsockopt_reuseaddr(int fd) +{ + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_1, sizeof(const_int_1)); +} +int setsockopt_broadcast(int fd) +{ + return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &const_int_1, sizeof(const_int_1)); +} +int setsockopt_bindtodevice(int fd, const char *iface) +{ + int r; + struct ifreq ifr; + strncpy(ifr.ifr_name, iface, IFNAMSIZ); + /* NB: passing (iface, strlen(iface) + 1) does not work! + * (maybe it works on _some_ kernels, but not on 2.6.26) + * Actually, ifr_name is at offset 0, and in practice + * just giving char[IFNAMSIZ] instead of struct ifreq works too. + * But just in case it's not true on some obscure arch... */ + r = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)); + if (r) + perror_msg("can't bind to interface %s", iface); + return r; +} + +len_and_sockaddr* get_sock_lsa(int fd) +{ + len_and_sockaddr lsa; + len_and_sockaddr *lsa_ptr; + + lsa.len = LSA_SIZEOF_SA; + if (getsockname(fd, &lsa.u.sa, &lsa.len) != 0) + return NULL; + + lsa_ptr = (len_and_sockaddr *)xzalloc(LSA_LEN_SIZE + lsa.len); + if (lsa.len > LSA_SIZEOF_SA) { /* rarely (if ever) happens */ + lsa_ptr->len = lsa.len; + getsockname(fd, &lsa_ptr->u.sa, &lsa_ptr->len); + } else { + memcpy(lsa_ptr, &lsa, LSA_LEN_SIZE + lsa.len); + } + return lsa_ptr; +} + +void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) +{ + if (connect(s, s_addr, addrlen) < 0) { + close(s); + if (s_addr->sa_family == AF_INET) + perror_msg_and_die("%s (%s)", + "cannot connect to remote host", + inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr)); + perror_msg_and_die("cannot connect to remote host"); + } +} + +/* Return port number for a service. + * If "port" is a number use it as the port. + * If "port" is a name it is looked up in /etc/services, + * if it isnt found return default_port + */ +unsigned lookup_port(const char *port, const char *protocol, unsigned default_port) +{ + unsigned port_nr = default_port; + if (port) { + int old_errno; + char *end; + + /* Since this is a lib function, we're not allowed to reset errno to 0. + * Doing so could break an app that is deferring checking of errno. */ + old_errno = errno; + errno = 0; + port_nr = strtoul(port, &end, 10); + if (errno || *end || port_nr > 65535) { + struct servent *tserv = getservbyname(port, protocol); + port_nr = default_port; + if (tserv) + port_nr = ntohs(tserv->s_port); + } + errno = old_errno; + } + return (uint16_t)port_nr; +} + +int get_nport(const struct sockaddr *sa) +{ +#if ENABLE_FEATURE_IPV6 + if (sa->sa_family == AF_INET6) { + return ((struct sockaddr_in6*)sa)->sin6_port; + } +#endif + if (sa->sa_family == AF_INET) { + return ((struct sockaddr_in*)sa)->sin_port; + } + /* What? UNIX socket? IPX?? :) */ + return -1; +} + +void set_nport(len_and_sockaddr *lsa, unsigned port) +{ +#if ENABLE_FEATURE_IPV6 + if (lsa->u.sa.sa_family == AF_INET6) { + lsa->u.sin6.sin6_port = port; + return; + } +#endif + if (lsa->u.sa.sa_family == AF_INET) { + lsa->u.sin.sin_port = port; + return; + } + /* What? UNIX socket? IPX?? :) */ +} + +/* We hijack this constant to mean something else */ +/* It doesn't hurt because we will remove this bit anyway */ +#define DIE_ON_ERROR AI_CANONNAME + +/* host: "1.2.3.4[:port]", "www.google.com[:port]" + * port: if neither of above specifies port # */ +static len_and_sockaddr* str2sockaddr( + const char *host, int port, + sa_family_t af, + int ai_flags) +{ + int rc; + len_and_sockaddr *r; + struct addrinfo *result = NULL; + struct addrinfo *used_res; + const char *org_host = host; /* only for error msg */ + const char *cp; + struct addrinfo hint; + + r = NULL; + + /* Ugly parsing of host:addr */ + if (ENABLE_FEATURE_IPV6 && host[0] == '[') { + /* Even uglier parsing of [xx]:nn */ + host++; + cp = strchr(host, ']'); + if (!cp || (cp[1] != ':' && cp[1] != '\0')) { + /* Malformed: must be [xx]:nn or [xx] */ + error_msg("bad address '%s'", org_host); + if (ai_flags & DIE_ON_ERROR) + xfunc_die(); + return NULL; + } + } else { + cp = strrchr(host, ':'); + if (ENABLE_FEATURE_IPV6 && cp && strchr(host, ':') != cp) { + /* There is more than one ':' (e.g. "::1") */ + cp = NULL; /* it's not a port spec */ + } + } + if (cp) { /* points to ":" or "]:" */ + int sz = cp - host + 1; + char *hbuf = (char*)alloca(sz); + hbuf[--sz] = '\0'; + host = strncpy(hbuf, host, sz); + if (ENABLE_FEATURE_IPV6 && *cp != ':') { + cp++; /* skip ']' */ + if (*cp == '\0') /* [xx] without port */ + goto skip; + } + cp++; /* skip ':' */ + char *end; + errno = 0; + port = strtoul(cp, &end, 10); + if (errno || *end || (unsigned)port > 0xffff) { + error_msg("bad port spec '%s'", org_host); + if (ai_flags & DIE_ON_ERROR) + xfunc_die(); + return NULL; + } + skip: ; + } + + memset(&hint, 0 , sizeof(hint)); +#if !ENABLE_FEATURE_IPV6 + hint.ai_family = AF_INET; /* do not try to find IPv6 */ +#else + hint.ai_family = af; +#endif + /* Needed. Or else we will get each address thrice (or more) + * for each possible socket type (tcp,udp,raw...): */ + hint.ai_socktype = SOCK_STREAM; + hint.ai_flags = ai_flags & ~DIE_ON_ERROR; + rc = getaddrinfo(host, NULL, &hint, &result); + if (rc || !result) { + error_msg("bad address '%s'", org_host); + if (ai_flags & DIE_ON_ERROR) + xfunc_die(); + goto ret; + } + used_res = result; +#if ENABLE_FEATURE_PREFER_IPV4_ADDRESS + while (1) { + if (used_res->ai_family == AF_INET) + break; + used_res = used_res->ai_next; + if (!used_res) { + used_res = result; + break; + } + } +#endif + r = (len_and_sockaddr *)xmalloc(offsetof(len_and_sockaddr, u.sa) + used_res->ai_addrlen); + r->len = used_res->ai_addrlen; + memcpy(&r->u.sa, used_res->ai_addr, used_res->ai_addrlen); + set_nport(r, htons(port)); + ret: + freeaddrinfo(result); + return r; +} +#if !ENABLE_FEATURE_IPV6 +#define str2sockaddr(host, port, af, ai_flags) str2sockaddr(host, port, ai_flags) +#endif + +#if ENABLE_FEATURE_IPV6 +len_and_sockaddr* host_and_af2sockaddr(const char *host, int port, sa_family_t af) +{ + return str2sockaddr(host, port, af, 0); +} + +len_and_sockaddr* xhost_and_af2sockaddr(const char *host, int port, sa_family_t af) +{ + return str2sockaddr(host, port, af, DIE_ON_ERROR); +} +#endif + +len_and_sockaddr* host2sockaddr(const char *host, int port) +{ + return str2sockaddr(host, port, AF_UNSPEC, 0); +} + +len_and_sockaddr* xhost2sockaddr(const char *host, int port) +{ + return str2sockaddr(host, port, AF_UNSPEC, DIE_ON_ERROR); +} + +len_and_sockaddr* xdotted2sockaddr(const char *host, int port) +{ + return str2sockaddr(host, port, AF_UNSPEC, AI_NUMERICHOST | DIE_ON_ERROR); +} + +#undef xsocket_type +int xsocket_type(len_and_sockaddr **lsap, int family, int sock_type) +{ + len_and_sockaddr *lsa; + int fd; + int len; + +#if ENABLE_FEATURE_IPV6 + if (family == AF_UNSPEC) { + fd = socket(AF_INET6, sock_type, 0); + if (fd >= 0) { + family = AF_INET6; + goto done; + } + family = AF_INET; + } +#endif + fd = xsocket(family, sock_type, 0); + len = sizeof(struct sockaddr_in); +#if ENABLE_FEATURE_IPV6 + if (family == AF_INET6) { + done: + len = sizeof(struct sockaddr_in6); + } +#endif + lsa = (len_and_sockaddr *)xzalloc(offsetof(len_and_sockaddr, u.sa) + len); + lsa->len = len; + lsa->u.sa.sa_family = family; + *lsap = lsa; + return fd; +} + +int xsocket_stream(len_and_sockaddr **lsap) +{ + return xsocket_type(lsap, AF_UNSPEC, SOCK_STREAM); +} + +static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type) +{ + int fd; + len_and_sockaddr *lsa; + + if (bindaddr && bindaddr[0]) { + lsa = xdotted2sockaddr(bindaddr, port); + /* user specified bind addr dictates family */ + fd = xsocket(lsa->u.sa.sa_family, sock_type, 0); + } else { + fd = xsocket_type(&lsa, AF_UNSPEC, sock_type); + set_nport(lsa, htons(port)); + } + setsockopt_reuseaddr(fd); + xbind(fd, &lsa->u.sa, lsa->len); + free(lsa); + return fd; +} + +int create_and_bind_stream_or_die(const char *bindaddr, int port) +{ + return create_and_bind_or_die(bindaddr, port, SOCK_STREAM); +} + +int create_and_bind_dgram_or_die(const char *bindaddr, int port) +{ + return create_and_bind_or_die(bindaddr, port, SOCK_DGRAM); +} + + +int create_and_connect_stream_or_die(const char *peer, int port) +{ + int fd; + len_and_sockaddr *lsa; + + lsa = xhost2sockaddr(peer, port); + fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0); + setsockopt_reuseaddr(fd); + xconnect(fd, &lsa->u.sa, lsa->len); + free(lsa); + return fd; +} + +int xconnect_stream(const len_and_sockaddr *lsa) +{ + int fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0); + xconnect(fd, &lsa->u.sa, lsa->len); + return fd; +} + +/* We hijack this constant to mean something else */ +/* It doesn't hurt because we will add this bit anyway */ +#define IGNORE_PORT NI_NUMERICSERV +static char* sockaddr2str(const struct sockaddr *sa, int flags) +{ + char host[128]; + char serv[16]; + int rc; + socklen_t salen; + + salen = LSA_SIZEOF_SA; +#if ENABLE_FEATURE_IPV6 + if (sa->sa_family == AF_INET) + salen = sizeof(struct sockaddr_in); + if (sa->sa_family == AF_INET6) + salen = sizeof(struct sockaddr_in6); +#endif + rc = getnameinfo(sa, salen, + host, sizeof(host), + /* can do ((flags & IGNORE_PORT) ? NULL : serv) but why bother? */ + serv, sizeof(serv), + /* do not resolve port# into service _name_ */ + flags | NI_NUMERICSERV + ); + if (rc) + return NULL; + if (flags & IGNORE_PORT) + return xstrdup(host); +#if ENABLE_FEATURE_IPV6 + if (sa->sa_family == AF_INET6) { + if (strchr(host, ':')) /* heh, it's not a resolved hostname */ + return xasprintf("[%s]:%s", host, serv); + /*return xasprintf("%s:%s", host, serv);*/ + /* - fall through instead */ + } +#endif + /* For now we don't support anything else, so it has to be INET */ + /*if (sa->sa_family == AF_INET)*/ + return xasprintf("%s:%s", host, serv); + /*return xstrdup(host);*/ +} + +char* xmalloc_sockaddr2host(const struct sockaddr *sa) +{ + return sockaddr2str(sa, 0); +} + +char* xmalloc_sockaddr2host_noport(const struct sockaddr *sa) +{ + return sockaddr2str(sa, IGNORE_PORT); +} + +char* xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa) +{ + return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT); +} +char* xmalloc_sockaddr2dotted(const struct sockaddr *sa) +{ + return sockaddr2str(sa, NI_NUMERICHOST); +} + +char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) +{ + return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT); +} diff --git a/lib/Utils/xfuncs.cpp b/lib/Utils/xfuncs.cpp index d256c19..97c2f76 100644 --- a/lib/Utils/xfuncs.cpp +++ b/lib/Utils/xfuncs.cpp @@ -5,6 +5,22 @@ */ #include "abrtlib.h" +/* Turn on nonblocking I/O on a fd */ +int ndelay_on(int fd) +{ + return fcntl(fd, F_SETFL, fcntl(fd,F_GETFL) | O_NONBLOCK); +} + +int ndelay_off(int fd) +{ + return fcntl(fd, F_SETFL, fcntl(fd,F_GETFL) & ~O_NONBLOCK); +} + +int close_on_exec_on(int fd) +{ + return fcntl(fd, F_SETFD, FD_CLOEXEC); +} + // Die if we can't allocate size bytes of memory. void* xmalloc(size_t size) { @@ -305,3 +321,50 @@ int is_regular_file(struct dirent *dent, const char *dirname) return r == 0 && S_ISREG(statbuf.st_mode); } + +/* Is it "." or ".."? */ +/* abrtlib candidate */ +bool dot_or_dotdot(const char *filename) +{ + if (filename[0] != '.') return false; + if (filename[1] == '\0') return true; + if (filename[1] != '.') return false; + if (filename[2] == '\0') return true; + return false; +} + +/* Find out if the last character of a string matches the one given. + * Don't underrun the buffer if the string length is 0. + */ +char *last_char_is(const char *s, int c) +{ + if (s && *s) { + s += strlen(s) - 1; + if ((unsigned char)*s == c) + return (char*)s; + } + return NULL; +} + +std::string concat_path_file(const char *path, const char *filename) +{ + char *lc; + + while (*filename == '/') + filename++; + lc = last_char_is(path, '/'); + return ssprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename); +} + +bool string_to_bool(const char *s) +{ + if (s[0] == '1' && s[1] == '\0') + return true; + if (strcasecmp(s, "on") == 0) + return true; + if (strcasecmp(s, "yes") == 0) + return true; + if (strcasecmp(s, "true") == 0) + return true; + return false; +} |
