From 882971ae45a6a0b2e788543881c3bc4509b5da9aacfd98bf8e0463434c03f6b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Tesa=C5=99=C3=ADk?= Date: Mon, 19 May 2014 14:19:01 +0000 Subject: [PATCH 1/3] - kdump-fix-udev-rules.patch: Avoid entering failed state on CPU hotplug (bnc#874992, bnc#809209). OBS-URL: https://build.opensuse.org/package/show/Kernel:kdump/kdump?expand=0&rev=88 --- kdump-fix-udev-rules.patch | 145 +++++++++++++++++++++++++++++++++++++ kdump.changes | 6 ++ kdump.spec | 2 + 3 files changed, 153 insertions(+) create mode 100644 kdump-fix-udev-rules.patch diff --git a/kdump-fix-udev-rules.patch b/kdump-fix-udev-rules.patch new file mode 100644 index 0000000..2954ad5 --- /dev/null +++ b/kdump-fix-udev-rules.patch @@ -0,0 +1,145 @@ +From: Petr Tesarik +Subject: Avoid entering failed state on CPU hotplug +References: bnc#874992, bnc#809209 +Patch-mainline: scheduled for v0.8.13 + +When more than 5 CPUs are quickly offlined or onlined, the kdump +service enters and remains in a "failed state". + +This issue appears to be caused by systemd's service start rate +limit default [more than 5 times within 10 seconds per +systemd.service(5) man page] being exceeded due to the udev +initiated kdump service restart for every CPU offline or online +operation. + +Avoid the problem by preventing the systemd rate limit threshold +encounter by flushing the reset rate counter prior to each CPU +offline and online related kdump service restart. + +Additionally, limit the restart to IBM POWER, because other +systems do not appear to be affected by bnc 389658. + +And the z/Architecture do not need a restart on memory hotplug +either, so the udev rules are not needed at all. + +Signed-off-by: Petr Tesarik +diff --git a/70-kdump.rules b/70-kdump.rules +deleted file mode 100644 +index c215f23..0000000 +--- a/70-kdump.rules ++++ /dev/null +@@ -1,12 +0,0 @@ +-# +-# Kdump core headers needs to be regnerated if the CPUs or memory changes. +-# For this, reload kdump. +-# +-# Novell Bug #389658 +-# +- +-SUBSYSTEM=="cpu", ACTION=="online", PROGRAM="/etc/init.d/boot.kdump try-restart" +-SUBSYSTEM=="cpu", ACTION=="offline", PROGRAM="/etc/init.d/boot.kdump try-restart" +-SUBSYSTEM=="memory", ACTION=="add", PROGRAM="/etc/init.d/boot.kdump try-restart" +-SUBSYSTEM=="memory", ACTION=="remove", PROGRAM="/etc/init.d/boot.kdump try-restart" +- +diff --git a/70-kdump.rules.in b/70-kdump.rules.in +new file mode 100644 +index 0000000..0ec2127 +--- /dev/null ++++ b/70-kdump.rules.in +@@ -0,0 +1,37 @@ ++@if @ARCH@ s390 s390x ++# ++# For s390x the ELF header is created in the kdump kernel and therefore ++# no kdump udev rules are required. ++# ++@else ++# ++# Kdump core headers needs to be regnerated if the CPUs or memory changes. ++# For this, reload kdump. ++# ++# Novell Bug #389658 ++# ++ ++TEST="/usr/bin/systemctl", GOTO="kdump_systemd" ++ ++@if @ARCH@ ppc ppc64 ppc64le ++SUBSYSTEM=="cpu", ACTION=="online", RUN+="/etc/init.d/boot.kdump try-restart" ++SUBSYSTEM=="cpu", ACTION=="offline", RUN+="/etc/init.d/boot.kdump try-restart" ++@endif ++SUBSYSTEM=="memory", ACTION=="add", RUN+="/etc/init.d/boot.kdump try-restart" ++SUBSYSTEM=="memory", ACTION=="remove", RUN+="/etc/init.d/boot.kdump try-restart" ++ ++GOTO="kdump_end" ++ ++# Systemd limits service start rate, so if udev events are emitted too ++# often, kdump will enter failed state, unless the counter is reset here. ++LABEL="kdump_systemd" ++ ++@if @ARCH@ ppc ppc64 ppc64le ++SUBSYSTEM=="cpu", ACTION=="online", RUN+="/usr/bin/systemctl reset-failed kdump", RUN+="/usr/bin/systemctl try-restart kdump" ++SUBSYSTEM=="cpu", ACTION=="offline", RUN+="/usr/bin/systemctl reset-failed kdump", RUN+="/usr/bin/systemctl try-restart kdump" ++@endif ++SUBSYSTEM=="memory", ACTION=="add", RUN+="/usr/bin/systemctl reset-failed kdump", RUN+="/usr/bin/systemctl try-restart kdump" ++SUBSYSTEM=="memory", ACTION=="remove", RUN+="/usr/bin/systemctl reset-failed kdump", RUN+="/usr/bin/systemctl try-restart kdump" ++ ++LABEL="kdump_end" ++@endif +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 59c4607..e10afa9 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -26,7 +26,7 @@ SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) + + cmake_minimum_required(VERSION 2.6.2) + set (PACKAGE_STRING "kdump") +-set (PACKAGE_VERSION "0.8.12") ++set (PACKAGE_VERSION "0.8.13") + + include_directories("${PROJECT_BINARY_DIR}") + +@@ -195,8 +195,26 @@ INSTALL(FILES + /var/adm/fillup-templates/ + ) + ++ADD_CUSTOM_COMMAND( ++ OUTPUT ++ 70-kdump.rules ++ COMMAND ++ ARCH=${CMAKE_SYSTEM_PROCESSOR} ++ awk -f ${CMAKE_CURRENT_SOURCE_DIR}/process_cond.awk ++ ${CMAKE_CURRENT_SOURCE_DIR}/70-kdump.rules.in ++ > ${CMAKE_CURRENT_BINARY_DIR}/70-kdump.rules ++ DEPENDS ++ 70-kdump.rules.in ++) ++ADD_CUSTOM_TARGET( ++ kdump.rules ++ ALL ++ DEPENDS ++ 70-kdump.rules ++) ++ + INSTALL(FILES +- ${CMAKE_CURRENT_SOURCE_DIR}/70-kdump.rules ++ ${CMAKE_CURRENT_BINARY_DIR}/70-kdump.rules + DESTINATION + /etc/udev/rules.d + ) +diff --git a/process_cond.awk b/process_cond.awk +index 82ffe92..d6f966b 100755 +--- a/process_cond.awk ++++ b/process_cond.awk +@@ -5,7 +5,12 @@ + ENVIRON[var[1]] \ + substr($0, RSTART + RLENGTH) + stack[sp++] = remove +- remove = remove || ($2 != $3) ++ condition = 0 ++ for (i = 3; i <= NF; i++) { ++ if ($2 == $i) ++ condition = 1 ++ } ++ remove = remove || !condition + skip = 1 + } + /^@else\>/ { diff --git a/kdump.changes b/kdump.changes index 05945ab..cf25c2c 100644 --- a/kdump.changes +++ b/kdump.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Mon May 19 14:18:27 UTC 2014 - ptesarik@suse.cz + +- kdump-fix-udev-rules.patch: Avoid entering failed state on CPU + hotplug (bnc#874992, bnc#809209). + ------------------------------------------------------------------- Fri May 16 17:42:37 UTC 2014 - ptesarik@suse.cz diff --git a/kdump.spec b/kdump.spec index 89466ee..9946af0 100644 --- a/kdump.spec +++ b/kdump.spec @@ -59,6 +59,7 @@ PreReq: %insserv_prereq %fillup_prereq mkinitrd Source: %{name}-%{version}.tar.bz2 Source2: %{name}-%{version}-rpmlintrc Source3: kdump.service +Patch1: %{name}-fix-udev-rules.patch BuildRoot: %{_tmppath}/%{name}-%{version}-build # rename "kdump-helpers" (10.3) -> "kdump" (11.0/SP2) Provides: kdump-helpers = %{version} @@ -96,6 +97,7 @@ Authors: %prep %setup +%patch1 -p1 %build export CFLAGS="%optflags" From 53406c38d1454ce7621ae337e6aad3a988e5ec76e190ae5f622c802586e05291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Tesa=C5=99=C3=ADk?= Date: Mon, 19 May 2014 14:38:03 +0000 Subject: [PATCH 2/3] Fix a typo in the udev file. OBS-URL: https://build.opensuse.org/package/show/Kernel:kdump/kdump?expand=0&rev=89 --- kdump-fix-udev-rules.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdump-fix-udev-rules.patch b/kdump-fix-udev-rules.patch index 2954ad5..fe7faa7 100644 --- a/kdump-fix-udev-rules.patch +++ b/kdump-fix-udev-rules.patch @@ -60,7 +60,7 @@ index 0000000..0ec2127 +# Novell Bug #389658 +# + -+TEST="/usr/bin/systemctl", GOTO="kdump_systemd" ++TEST=="/usr/bin/systemctl", GOTO="kdump_systemd" + +@if @ARCH@ ppc ppc64 ppc64le +SUBSYSTEM=="cpu", ACTION=="online", RUN+="/etc/init.d/boot.kdump try-restart" From 0f56f110678e2f4427c979e9d4cbbbe1ff8cb4301b9b3089dc39e9083ce6b8f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Tesa=C5=99=C3=ADk?= Date: Tue, 20 May 2014 11:45:43 +0000 Subject: [PATCH 3/3] Accepting request 234827 from home:ptesarik:branches:Kernel:kdump Fix service failures after online/offline CPU under systemd. URL-decode the target URL. OBS-URL: https://build.opensuse.org/request/show/234827 OBS-URL: https://build.opensuse.org/package/show/Kernel:kdump/kdump?expand=0&rev=90 --- kdump-urldecode.patch | 718 ++++++++++++++++++++++++++++++++++++++++++ kdump.changes | 6 + kdump.spec | 3 + 3 files changed, 727 insertions(+) create mode 100644 kdump-urldecode.patch diff --git a/kdump-urldecode.patch b/kdump-urldecode.patch new file mode 100644 index 0000000..c8a2b91 --- /dev/null +++ b/kdump-urldecode.patch @@ -0,0 +1,718 @@ +From: Petr Tesarik +Subject: Perform percent decoding on target URL +References: bnc#869590 +Patch-mainline: scheduled for v0.8.13 + +Signed-off-by: Petr Tesarik + +diff --git a/kdumptool/CMakeLists.txt b/kdumptool/CMakeLists.txt +index 81ce47e..9a5bc1a 100644 +--- a/kdumptool/CMakeLists.txt ++++ b/kdumptool/CMakeLists.txt +@@ -142,3 +142,8 @@ add_executable(testprocess + testprocess.cc + ) + target_link_libraries(testprocess common ${EXTRA_LIBS}) ++ ++add_executable(testurldecode ++ testurldecode.cc ++) ++target_link_libraries(testurldecode common ${EXTRA_LIBS}) +diff --git a/kdumptool/stringutil.cc b/kdumptool/stringutil.cc +index 807a30a..70d2dc5 100644 +--- a/kdumptool/stringutil.cc ++++ b/kdumptool/stringutil.cc +@@ -270,6 +270,19 @@ void Stringutil::digest_base64(const void *buf, size_t len, + + #endif // HAVE_LIBSSL + ++// ----------------------------------------------------------------------------- ++int Stringutil::hex2int(char c) ++ throw (KError) ++{ ++ if (c >= '0' && c <= '9') ++ return c - '0'; ++ if (c >= 'a' && c <= 'f') ++ return c - 'a' + 10; ++ if (c >= 'A' && c <= 'F') ++ return c - 'A' + 10; ++ throw KError(string("Stringutil::hex2int: '") + c + "' is not a hex digit"); ++} ++ + //}}} + //{{{ KString ------------------------------------------------------------------ + +@@ -333,6 +346,27 @@ bool KString::endsWith(const string &part) const + return strcmp(c_str() + size() - part.size(), part.c_str()) == 0; + } + ++// ----------------------------------------------------------------------------- ++KString &KString::decodeURL(bool formenc) ++ throw() ++{ ++ iterator src, dst; ++ for (src = dst = begin(); src != end(); ++src) { ++ char c1, c2; ++ if (*src == '%' && end() - src >= 2 && ++ isxdigit(c1 = src[1]) && isxdigit(c2 = src[2])) { ++ *dst++ = (Stringutil::hex2int(c1) << 4) | ++ Stringutil::hex2int(c2); ++ src += 2; ++ } else if (formenc && *src == '+') ++ *dst++ = ' '; ++ else ++ *dst++ = *src; ++ } ++ resize(dst - begin()); ++ return *this; ++} ++ + //}}} + + // vim: set sw=4 ts=4 fdm=marker et: :collapseFolds=1: +diff --git a/kdumptool/stringutil.h b/kdumptool/stringutil.h +index 321ab59..4dad735 100644 +--- a/kdumptool/stringutil.h ++++ b/kdumptool/stringutil.h +@@ -200,6 +200,9 @@ class Stringutil { + throw (KError); + + #endif // HAVE_LIBSSL ++ ++ static int hex2int(char c) ++ throw (KError); + }; + + //}}} +@@ -289,6 +292,14 @@ class KString : public std::string { + bool endsWith(const std::string &part) const + throw (); + ++ /** ++ * Perform URL decoding on the string. ++ * ++ * @param[in] formenc if @c true, translate '+' into spaces ++ * @return reference to this object (after decoding) ++ */ ++ KString &decodeURL(bool formenc = false) ++ throw(); + }; + + //}}} +diff --git a/kdumptool/testurldecode.cc b/kdumptool/testurldecode.cc +new file mode 100644 +index 0000000..36e639f +--- /dev/null ++++ b/kdumptool/testurldecode.cc +@@ -0,0 +1,60 @@ ++/* ++ * (c) 2014, Petr Tesarik , SUSE LINUX Products GmbH ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++#include ++#include ++ ++#include "global.h" ++#include "stringutil.h" ++#include "debug.h" ++ ++using std::cout; ++using std::cerr; ++using std::endl; ++ ++// ----------------------------------------------------------------------------- ++int main(int argc, char *argv[]) ++{ ++ Debug::debug()->setStderrLevel(Debug::DL_TRACE); ++ bool formenc = false; ++ ++ try { ++ int i = 1; ++ if (i < argc && KString(argv[i]) == "-f") { ++ formenc = true; ++ ++i; ++ } ++ ++ bool addspace = false; ++ while (i < argc) { ++ KString arg(argv[i]); ++ if (addspace) ++ cout << ' '; ++ cout << arg.decodeURL(formenc); ++ addspace = true; ++ ++i; ++ } ++ cout << endl; ++ ++ } catch(const std::exception &ex) { ++ cerr << "Fatal exception: " << ex.what() << endl; ++ return EXIT_FAILURE; ++ } ++ ++ return EXIT_SUCCESS; ++} +diff --git a/kdumptool/urlparser.cc b/kdumptool/urlparser.cc +index 095415e..fda09ea 100644 +--- a/kdumptool/urlparser.cc ++++ b/kdumptool/urlparser.cc +@@ -86,166 +86,147 @@ string URLParser::protocol2string(URLParser::Protocol protocol) + } + + // ----------------------------------------------------------------------------- ++string URLParser::extractScheme(string::iterator &it, ++ const string::const_iterator &end) ++{ ++ string::iterator const start = it; ++ ++ if (it == m_url.end() || !isalpha(*it)) ++ return string(); ++ ++ do { ++ ++it; ++ if (it == m_url.end()) { ++ it = start; ++ return string(); ++ } ++ } while (*it != ':' && *it != '/' && *it != '?' && *it != '#'); ++ ++ if (*it != ':') { ++ it = start; ++ return string(); ++ } ++ ++ return string(start, it++); ++} ++ ++// ----------------------------------------------------------------------------- ++string URLParser::extractAuthority(string::iterator &it, ++ const string::const_iterator &end) ++{ ++ if (end - it < 2 || it[0] != '/' || it[1] != '/') ++ return string(); ++ ++ it += 2; ++ string::iterator const start = it; ++ while (*it != '/' && *it != '?' && *it != '#') ++ ++it; ++ ++ return string(start, it); ++} ++ ++// ----------------------------------------------------------------------------- + URLParser::URLParser(const std::string &url) + throw (KError) + : m_url(url), m_port(-1) + { + Debug::debug()->trace("URLParser::URLParser(%s)", url.c_str()); + +- if (url.size() == 0) +- throw KError("URL must be longer than 0 characters."); +- +- // local files that don't have URL syntax +- // we support that for backward-compatibility +- if (url[0] == '/') { +- m_protocol = PROT_FILE; +- m_path = url; +- return; +- } ++ string::iterator it = m_url.begin(); + + // +- // get the protocol ++ // extract the three main URL componenets + // ++ string scheme = extractScheme(it, m_url.end()); ++ string authority = extractAuthority(it, m_url.end()); ++ m_path.assign(it, m_url.end()); ++ // TODO: query and fragment are not handled + +- string::size_type first_colon = url.find(':'); +- if (first_colon == string::npos) +- throw KError("The URL does not contain any protocol."); +- string proto_part = url.substr(0, first_colon); +- Debug::debug()->trace("Setting protocol to %s", proto_part.c_str()); +- m_protocol = string2protocol(proto_part); +- +- if (url.size() < (first_colon + 3) || +- url[first_colon+1] != '/' || +- url[first_colon+2] != '/') +- throw KError("protocol: must be followed by '//'."); ++ if (m_path.empty() || m_path[0] != '/') ++ throw KError("URLParser: Only absolute paths are supported."); + + // +- // call the parse methods matching for the protocol ++ // parse the authority part + // + +- if (m_protocol == PROT_FILE) { +- m_path = url.substr(first_colon+3); +- } else if (m_protocol == PROT_NFS) { +- parseNFSUrl(url.substr(first_colon+3)); +- } else if (m_protocol == PROT_SFTP || m_protocol == PROT_SSH || +- m_protocol == PROT_FTP || m_protocol == PROT_CIFS) { +- parseFTPUrl(url.substr(first_colon+3)); +- } else +- throw KError("Invalid protocol: " + +- Stringutil::number2string(m_protocol) + "."); ++ // Look for (optional) port ++ it = authority.end(); ++ while (it != authority.begin() && isdigit(it[-1])) ++ --it; ++ if (it != authority.begin() && it[-1] == ':') { ++ string port(it, authority.end()); ++ authority.resize(it - authority.begin() - 1); + +- Debug::debug()->dbg("URL parsed as: protocol=%s, host=%s, port=%d, " +- "username=%s, password=%s, path=%s", +- getProtocolAsString().c_str(), +- getHostname().c_str(), getPort(), getUsername().c_str(), +- getPassword().c_str(), getPath().c_str()); ++ if (port.size() > 0) ++ m_port = Stringutil::string2number(port); ++ } + +-} ++ // look for userinfo ++ string::size_type last_at = authority.rfind('@'); ++ if (last_at != string::npos) { ++ m_hostname = authority.substr(last_at+1); ++ authority.resize(last_at); ++ ++ // now separate user and password ++ string::size_type first_colon = authority.find(':'); ++ if (first_colon != string::npos) { ++ m_username = authority.substr(0, first_colon); ++ m_password = authority.substr(first_colon+1); ++ } else ++ m_username = authority; ++ } else ++ m_hostname = authority; + +-// ----------------------------------------------------------------------------- +-void URLParser::parseUserPassHostPort(const string &userpasshostport) +- throw (KError) +-{ +- // now scan for an '@' to separate userhost from hostport +- string::size_type last_at = userpasshostport.rfind('@'); +- string userhost; +- string hostport; +- if (last_at == string::npos) { +- hostport = userpasshostport; +- +- switch (m_protocol) { +- case PROT_FTP: +- m_username = FTP_DEFAULT_USER; +- break; +- +- case PROT_SFTP: +- case PROT_SSH: +- m_username = SFTP_DEFAULT_USER; +- break; +- +- default: +- // do nothing but make the compiler happy +- break; +- } ++ // ++ // undo percent encoding ++ // ++ m_hostname.decodeURL(); ++ m_password.decodeURL(); ++ m_username.decodeURL(); ++ m_path.decodeURL(); + ++ // ++ // guess the scheme, if omitted ++ // ++ if (scheme.empty()) { ++ if (m_hostname.empty() || m_hostname == "localhost") { ++ m_protocol = PROT_FILE; ++ Debug::debug()->trace("URL looks like a local file"); ++ } else { ++ m_protocol = PROT_NFS; ++ Debug::debug()->trace("URL looks like a remote file"); ++ } + } else { +- string userpass = userpasshostport.substr(0, last_at); +- hostport = userpasshostport.substr(last_at+1); +- +- // now separate user and passwort +- string::size_type firstcolon = userpass.find(':'); +- if (firstcolon != string::npos) { +- m_username = userpass.substr(0, firstcolon); +- m_password = userpass.substr(firstcolon+1); +- } else +- m_username = userpass; ++ Debug::debug()->trace("Scheme explicitly set to %s", scheme.c_str()); ++ m_protocol = string2protocol(scheme); + } + +- // look for a literal IPv6 addresses +- string::size_type last_colon = hostport.rfind(':'); +- if (hostport[0] == '[') { +- string::size_type bracket = hostport.find(']'); +- if (bracket != string::npos && bracket > last_colon) +- last_colon = string::npos; +- } +- +- // and separate host and port +- if (last_colon != string::npos) { +- m_hostname = hostport.substr(0, last_colon); +- string portstr = hostport.substr(last_colon+1); +- if (portstr.size() > 0) +- m_port = Stringutil::string2number(portstr); +- } else +- m_hostname = hostport; +-} +- +-// ----------------------------------------------------------------------------- +-void URLParser::parseNFSUrl(const string &partUrl) +- throw (KError) +-{ +- Debug::debug()->trace("URLParser::parseNFSUrl(%s)", partUrl.c_str()); +- +- // look for the first '/' +- string::size_type first_slash = partUrl.find('/'); +- if (first_slash == string::npos) +- throw KError("NFS URL must contain at least one '/'."); ++ // ++ // protocol-specific defaults ++ // + +- m_hostname = partUrl.substr(0, first_slash); ++ if (m_protocol == PROT_FILE && m_hostname == "localhost") ++ m_hostname.clear(); + +- string::size_type last_colon = m_hostname.rfind(':'); +- if (m_hostname[0] == '[') { +- string::size_type bracket = m_hostname.find(']'); +- if (bracket != string::npos && bracket > last_colon) +- last_colon = string::npos; ++ if (m_username.empty()) { ++ if (m_protocol == PROT_FTP) ++ m_username = FTP_DEFAULT_USER; ++ else if (m_protocol == PROT_SFTP || m_protocol == PROT_SSH) ++ m_username = SFTP_DEFAULT_USER; + } +- if (last_colon != string::npos) { +- string hostport = m_hostname; +- m_hostname = hostport.substr(0, last_colon); +- string portstring = hostport.substr(last_colon+1); +- if (portstring.size() > 0) +- m_port = Stringutil::string2number(hostport.substr(last_colon+1)); +- } +- +- m_path = partUrl.substr(first_slash); +-} +- +-// ----------------------------------------------------------------------------- +-void URLParser::parseFTPUrl(const string &partUrl) +- throw (KError) +-{ +- Debug::debug()->trace("URLParser::parseFTPUrl(%s)", partUrl.c_str()); + +- // look for the first '/' to separate the host name part from the +- // path name +- string::size_type first_slash = partUrl.find('/'); +- if (first_slash == string::npos) +- throw KError(getProtocolAsString() + +- " URL must contain at least one '/'."); ++ // ++ // sanity checks ++ // ++ if (m_protocol == PROT_FILE && !m_hostname.empty()) ++ throw KError("File protocol cannot specify a remote host"); + +- parseUserPassHostPort(partUrl.substr(0, first_slash)); ++ Debug::debug()->dbg("URL parsed as: protocol=%s, host=%s, port=%d, " ++ "username=%s, password=%s, path=%s", ++ getProtocolAsString().c_str(), ++ getHostname().c_str(), getPort(), getUsername().c_str(), ++ getPassword().c_str(), getPath().c_str()); + +- // and the rest is the path +- m_path = partUrl.substr(first_slash); + } + + // ----------------------------------------------------------------------------- +diff --git a/kdumptool/urlparser.h b/kdumptool/urlparser.h +index aac01e7..94aab27 100644 +--- a/kdumptool/urlparser.h ++++ b/kdumptool/urlparser.h +@@ -26,6 +26,7 @@ + #include "global.h" + #include "optionparser.h" + #include "subcommand.h" ++#include "stringutil.h" + + //{{{ URLParser ---------------------------------------------------------------- + +@@ -158,29 +159,32 @@ class URLParser { + static std::string protocol2string(Protocol protocol) + throw (KError); + +- protected: +- +- void parseCifsUrl(const std::string &partUrl) +- throw (KError); +- +- void parseNFSUrl(const std::string &partUrl) +- throw (KError); +- +- void parseUserPassHostPort(const std::string &userpasshostport) +- throw (KError); +- +- void parseFTPUrl(const std::string &partUrl) +- throw (KError); +- + private: +- std::string m_url; ++ std::string m_url; + Protocol m_protocol; +- std::string m_hostname; +- std::string m_password; +- std::string m_username; ++ KString m_hostname; ++ KString m_password; ++ KString m_username; + int m_port; +- std::string m_path; +- std::string m_share; ++ KString m_path; ++ ++ /** ++ * Extract scheme from a string ++ * ++ * @param[in,out] it starting position (updated if scheme is found) ++ * @param[in] end end of input string ++ */ ++ std::string extractScheme(std::string::iterator &it, ++ const std::string::const_iterator &end); ++ ++ /** ++ * Extract authority from a string ++ * ++ * @param[in,out] it starting position (updated if authority is found) ++ * @param[in] end end of input string ++ */ ++ std::string extractAuthority(std::string::iterator &it, ++ const std::string::const_iterator &end); + }; + + //}}} +diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt +index 41c222b..0b35466 100644 +--- a/tests/CMakeLists.txt ++++ b/tests/CMakeLists.txt +@@ -60,3 +60,7 @@ ADD_TEST(delete_dumps + ADD_TEST(process + ${CMAKE_CURRENT_SOURCE_DIR}/process.sh + ${CMAKE_BINARY_DIR}/kdumptool/testprocess) ++ ++ADD_TEST(urldecode ++ ${CMAKE_CURRENT_SOURCE_DIR}/testurldecode.sh ++ ${CMAKE_BINARY_DIR}/kdumptool/testurldecode) +diff --git a/tests/testurldecode.sh b/tests/testurldecode.sh +new file mode 100755 +index 0000000..a8076b4 +--- /dev/null ++++ b/tests/testurldecode.sh +@@ -0,0 +1,163 @@ ++#!/bin/bash ++# ++# (c) 2014, Petr Tesarik , SUSE LINUX Products GmbH ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation; either version 2 ++# of the License, or (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++# 02110-1301, USA. ++# ++ ++# Check that results match expectation ++# {{{ ++function check() ++{ ++ local arg="$1" ++ local expect="$2" ++ local result="$3" ++ if [ "$result" != "$expect" ] ; then ++ echo "failed string: $arg" ++ echo "Expected:" ++ echo "$expect" ++ echo "Result:" ++ echo "$result" ++ errornumber=$(( errornumber + 1 )) ++ fi ++} ++# }}} ++ ++# ++# Program {{{ ++# ++ ++# number of tested characters for random tests ++NTEST=100 ++ ++URLDECODE=$1 ++HEXDUMP="od -Ax -tx1" ++ ++if [ -z "$URLDECODE" ] ; then ++ echo "Usage: $0 urldecode" ++ exit 1 ++fi ++ ++errornumber=0 ++ ++# TEST #1: Random uppercase %-sequences ++ ++ARG= ++EXPECT= ++i=0 ++while [ $i -lt $NTEST ] ++do ++ ch=$(( $RANDOM % 256 )) ++ hex=$( printf "%02X" $ch ) ++ ARG="$ARG%$hex" ++ EXPECT="$EXPECT\\x$hex" ++ i=$(( i + 1 )) ++done ++EXPECT=$( echo -e "$EXPECT" | $HEXDUMP ) ++RESULT=$( "$URLDECODE" "$ARG" | $HEXDUMP ) ++check "$ARG" "$EXPECT" "$RESULT" ++ ++# TEST #2: Random lowercase %-sequences ++ ++ARG= ++EXPECT= ++i=0 ++while [ $i -lt $NTEST ] ++do ++ ch=$(( $RANDOM % 256 )) ++ hex=$( printf "%02x" $ch ) ++ ARG="$ARG%$hex" ++ EXPECT="$EXPECT\\x$hex" ++ i=$(( i + 1 )) ++done ++EXPECT=$( echo -e "$EXPECT" | $HEXDUMP ) ++RESULT=$( "$URLDECODE" "$ARG" | $HEXDUMP ) ++check "$ARG" "$EXPECT" "$RESULT" ++ ++# TEST #3: Decoding '+' ++ ++ARG=" + +begin hello+world end+" ++EXPECT="+ +begin hello+world end+" ++RESULT=$( "$URLDECODE" $ARG ) ++check "$ARG" "$EXPECT" "$RESULT" ++ ++# now using form decoding ++EXPECT=" begin hello world end " ++RESULT=$( "$URLDECODE" -f $ARG ) ++check "$ARG" "$EXPECT" "$RESULT" ++ ++# TEST #4: Transitions quoted <-> unquoted ++ ++ARG= ++EXPECT= ++i=0 ++while [ $i -lt $NTEST ] ++do ++ ch=$(( $RANDOM % 256 )) ++ hex=$( printf "%02x" $ch ) ++ case $(( $ch % 3 )) in ++ 0) ++ ARG="$ARG $hex%$hex" ++ EXPECT="$EXPECT $hex\\x$hex" ++ ;; ++ ++ 1) ++ ARG="$ARG %$hex$hex" ++ EXPECT="$EXPECT \\x$hex$hex" ++ ;; ++ ++ 2) ++ ARG="$ARG $hex%$hex$hex" ++ EXPECT="$EXPECT $hex\\x$hex$hex" ++ ;; ++ ++ 3) ++ ARG="$ARG +%$hex+$hex" ++ EXPECT="$EXPECT \\x$hex $hex" ++ ;; ++ esac ++ i=$(( i + 1 )) ++done ++EXPECT=$( echo -e "$EXPECT" | $HEXDUMP ) ++RESULT=$( "$URLDECODE" "$ARG" | $HEXDUMP ) ++check "$ARG" "$EXPECT" "$RESULT" ++ ++# TEST #5: Test invalid sequence ++ ++ARG=" %invalid %0invalid %ainvalid %x0invalid %+invalid %0+invalid" ++EXPECT="%invalid %0invalid %ainvalid %x0invalid % invalid %0 invalid" ++RESULT=$( "$URLDECODE" -f $ARG ) ++check "$ARG" "$EXPECT" "$RESULT" ++ ++# TEST #6: More than two hex digits ++ ++ARG="%4567 %456789abcdef0123" ++EXPECT="E67 E6789abcdef0123" ++RESULT=$( "$URLDECODE" $ARG ) ++check "$ARG" "$EXPECT" "$RESULT" ++ ++# TEST #7: NUL character ++ ++ARG="NUL character here: '%00'" ++EXPECT=$( echo -e "NUL character here: '\000'" | $HEXDUMP ) ++RESULT=$( "$URLDECODE" $ARG | $HEXDUMP ) ++check "$ARG" "$EXPECT" "$RESULT" ++ ++exit $errornumber ++ ++# }}} ++ ++# vim: set sw=4 ts=4 fdm=marker et: :collapseFolds=1: +diff --git a/tests/testurlparser.sh b/tests/testurlparser.sh +index 9cb9f1b..1d05353 100755 +--- a/tests/testurlparser.sh ++++ b/tests/testurlparser.sh +@@ -32,6 +32,7 @@ URLS=( "/var/log/dump" + "cifs://bwalle:dontsay@neptunium:/var/log/dump" + "smb://bwalle@192.168.0.70:/var/log" + "cifs://bwalle:dontsay@neptunium:/var/log/dump" ++ "ftp://pt%65sarik:don%27t%20say@fu%6eny+host/var/log/dum%70" + ) + + # protocol:host:port:user:pass:path +@@ -45,6 +46,7 @@ VALUES=( "file::-1:::/var/log/dump" + "cifs:neptunium:-1:bwalle:dontsay:/var/log/dump" + "cifs:192.168.0.70:-1:bwalle::/var/log" + "cifs:neptunium:-1:bwalle:dontsay:/var/log/dump" ++ "ftp:funny+host:-1:ptesarik:don't say:/var/log/dump" + ) + # }}} + diff --git a/kdump.changes b/kdump.changes index cf25c2c..0c2e006 100644 --- a/kdump.changes +++ b/kdump.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Tue May 20 11:15:40 UTC 2014 - ptesarik@suse.cz + +- kdump-urldecode.patch: Perform percent decoding on target URL + (bnc#869590). + ------------------------------------------------------------------- Mon May 19 14:18:27 UTC 2014 - ptesarik@suse.cz diff --git a/kdump.spec b/kdump.spec index 9946af0..52d584d 100644 --- a/kdump.spec +++ b/kdump.spec @@ -60,6 +60,7 @@ Source: %{name}-%{version}.tar.bz2 Source2: %{name}-%{version}-rpmlintrc Source3: kdump.service Patch1: %{name}-fix-udev-rules.patch +Patch2: %{name}-urldecode.patch BuildRoot: %{_tmppath}/%{name}-%{version}-build # rename "kdump-helpers" (10.3) -> "kdump" (11.0/SP2) Provides: kdump-helpers = %{version} @@ -98,6 +99,8 @@ Authors: %prep %setup %patch1 -p1 +%patch2 -p1 +chmod +x tests/testurldecode.sh %build export CFLAGS="%optflags"