From 9a0ee2542848ab8625984fc8cdbfb9b5414c0082 Mon Sep 17 00:00:00 2001 From: Howard Hinnant Date: Fri, 24 Jan 2020 20:23:30 -0500 Subject: [PATCH] Do a runtime test for realpath vs readlink * Ubuntu needs to use readlink under current_zone and most everyone else needs realpath. Attempting to make everyone happy with this commit. --- src/tz.cpp | 87 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 23 deletions(-) diff --git a/src/tz.cpp b/src/tz.cpp index ad4e90e..68436c3 100644 --- a/src/tz.cpp +++ b/src/tz.cpp @@ -3673,6 +3673,56 @@ tzdb::current_zone() const #else // !_WIN32 +#if HAS_STRING_VIEW + +static +std::string_view +extract_tz_name(char const* rp) +{ + using namespace std; + string_view result = rp; + CONSTDATA string_view zoneinfo = "zoneinfo"; + size_t pos = result.rfind(zoneinfo); + if (pos == result.npos) + throw runtime_error( + "current_zone() failed to find \"zoneinfo\" in " + string(result)); + pos = result.find('/', pos); + result.remove_prefix(pos + 1); + return result; +} + +#else // !HAS_STRING_VIEW + +static +std::string +extract_tz_name(char const* rp) +{ + using namespace std; + string result = rp; + CONSTDATA char zoneinfo[] = "zoneinfo"; + size_t pos = result.rfind(zoneinfo); + if (pos == result.npos) + throw runtime_error( + "current_zone() failed to find \"zoneinfo\" in " + result); + pos = result.find('/', pos); + result.erase(0, pos + 1); + return result; +} + +#endif // HAS_STRING_VIEW + +static +bool +sniff_realpath(const char* timezone) +{ + using namespace std; + char rp[PATH_MAX+1] = {}; + if (realpath(timezone, rp) == nullptr) + throw system_error(errno, system_category(), "realpath() failed"); + auto result = extract_tz_name(rp); + return result != "posixrules"; +} + const time_zone* tzdb::current_zone() const { @@ -3692,31 +3742,22 @@ tzdb::current_zone() const { struct stat sb; CONSTDATA auto timezone = "/etc/localtime"; - if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) { + if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) + { using namespace std; + static const bool use_realpath = sniff_realpath(timezone); char rp[PATH_MAX+1] = {}; - if (realpath(timezone, rp) == nullptr) - throw system_error(errno, system_category(), "realpath() failed"); -#if HAS_STRING_VIEW - string_view result = rp; - CONSTDATA string_view zoneinfo = "zoneinfo"; - size_t pos = result.rfind(zoneinfo); - if (pos == result.npos) - throw runtime_error( - "current_zone() failed to find \"zoneinfo\" in " + string(result)); - pos = result.find('/', pos); - result.remove_prefix(pos + 1); -#else - string result = rp; - CONSTDATA char zoneinfo[] = "zoneinfo"; - size_t pos = result.rfind(zoneinfo); - if (pos == result.npos) - throw runtime_error( - "current_zone() failed to find \"zoneinfo\" in " + result); - pos = result.find('/', pos); - result.erase(0, pos + 1); -#endif - return locate_zone(result); + if (use_realpath) + { + if (realpath(timezone, rp) == nullptr) + throw system_error(errno, system_category(), "realpath() failed"); + } + else + { + if (readlink(timezone, rp, sizeof(rp)-1) <= 0) + throw system_error(errno, system_category(), "readlink() failed"); + } + return locate_zone(extract_tz_name(rp)); } } // On embedded systems e.g. buildroot with uclibc the timezone is linked