enhance tz_dir detection for buildroot with uclibc systems

* use /etc/TZ for timezone detection on buildroot with uclibc systems
This commit is contained in:
Michael Maroszek
2017-11-15 13:27:04 +01:00
committed by Howard Hinnant
parent d97bc984c7
commit 16439a8ce2

View File

@@ -314,17 +314,26 @@ CONSTCD14 const sys_seconds min_seconds = sys_days(min_year/min_day);
#endif // USE_OS_TZDB #endif // USE_OS_TZDB
#ifndef _WIN32 #ifndef _WIN32
# ifndef __APPLE__
static const std::string tz_dir = "/usr/share/zoneinfo";
# else // __APPLE__
static static
std::string std::string
discover_tz_dir() discover_tz_dir()
{ {
struct stat sb; struct stat sb;
CONSTDATA auto timezone = "/etc/localtime";
using namespace std; using namespace std;
# ifndef __APPLE__
CONSTDATA auto tz_dir_default = "/usr/share/zoneinfo";
CONSTDATA auto tz_dir_buildroot = "/usr/share/zoneinfo/uclibc";
// Check special path which is valid for buildroot with uclibc builds
if(stat(tz_dir_buildroot, &sb) == 0 && S_ISDIR(sb.st_mode))
return tz_dir_buildroot;
else if(stat(tz_dir_default, &sb) == 0 && S_ISDIR(sb.st_mode))
return tz_dir_default;
else
throw runtime_error("discover_tz_dir failed to find zoneinfo\n");
# else // __APPLE__
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))
throw runtime_error("discover_tz_dir failed\n"); throw runtime_error("discover_tz_dir failed\n");
string result; string result;
@@ -340,11 +349,11 @@ discover_tz_dir()
if (i == string::npos) if (i == string::npos)
throw runtime_error("discover_tz_dir failed to find '/'\n"); throw runtime_error("discover_tz_dir failed to find '/'\n");
return result.substr(0, i); return result.substr(0, i);
# endif // __APPLE__
} }
static const std::string tz_dir = discover_tz_dir(); static const std::string tz_dir = discover_tz_dir();
# endif // __APPLE__
#endif #endif
// +-------------------+ // +-------------------+
@@ -3637,22 +3646,51 @@ tzdb::current_zone() const
// exception will be thrown by local_timezone. // exception will be thrown by local_timezone.
// The path may also take a relative form: // The path may also take a relative form:
// "../usr/share/zoneinfo/America/Los_Angeles". // "../usr/share/zoneinfo/America/Los_Angeles".
struct stat sb;
CONSTDATA auto timezone = "/etc/localtime";
if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0)
{ {
using namespace std; struct stat sb;
string result; CONSTDATA auto timezone = "/etc/localtime";
char rp[PATH_MAX]; if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) {
if (realpath(timezone, rp)) using namespace std;
result = string(rp); string result;
else char rp[PATH_MAX];
throw system_error(errno, system_category(), "realpath() failed"); if (realpath(timezone, rp))
result = string(rp);
else
throw system_error(errno, system_category(), "realpath() failed");
const size_t pos = result.find(tz_dir); const size_t pos = result.find(tz_dir);
if (pos != result.npos) if (pos != result.npos)
result.erase(0, tz_dir.size()+1+pos); result.erase(0, tz_dir.size() + 1 + pos);
return locate_zone(result); return locate_zone(result);
}
}
// On embedded systems e.g. buildroot with uclibc the timezone is linked
// into /etc/TZ which is a symlink to path like this:
// "/usr/share/zoneinfo/uclibc/America/Los_Angeles"
// If it does, we try to determine the current
// timezone from the remainder of the path by removing the prefix
// and hoping the rest resolves to valid timezone.
// It may not always work though. If it doesn't then an
// exception will be thrown by local_timezone.
// The path may also take a relative form:
// "../usr/share/zoneinfo/uclibc/America/Los_Angeles".
{
struct stat sb;
CONSTDATA auto timezone = "/etc/TZ";
if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) {
using namespace std;
string result;
char rp[PATH_MAX];
if (realpath(timezone, rp))
result = string(rp);
else
throw system_error(errno, system_category(), "realpath() failed");
const size_t pos = result.find(tz_dir);
if (pos != result.npos)
result.erase(0, tz_dir.size() + 1 + pos);
return locate_zone(result);
}
} }
{ {
// On some versions of some linux distro's (e.g. Ubuntu), // On some versions of some linux distro's (e.g. Ubuntu),