From 7f1404b870ab7c721a7add2331bf9422993ff840 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Thu, 20 Jan 2022 11:55:48 -0500 Subject: [PATCH] LibTimeZone: Use /etc/timezone as the basis for the system time zone This changes LibTimeZone to read the current time zone from the file /etc/timezone rather than using tzset/tzname. Instead, in a subsequent commit, LibC's time methods will be changed to used LibTimeZone to retrieve the system time zone. Also add an API to set the system time zone. This method is only allowed when running within Serenity. --- Base/etc/timezone | 1 + Userland/Libraries/LibTimeZone/TimeZone.cpp | 80 ++++++++++++++++++--- Userland/Libraries/LibTimeZone/TimeZone.h | 2 + 3 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 Base/etc/timezone diff --git a/Base/etc/timezone b/Base/etc/timezone new file mode 100644 index 0000000000..e2e7775815 --- /dev/null +++ b/Base/etc/timezone @@ -0,0 +1 @@ +UTC diff --git a/Userland/Libraries/LibTimeZone/TimeZone.cpp b/Userland/Libraries/LibTimeZone/TimeZone.cpp index f9953c53b2..8629ca7380 100644 --- a/Userland/Libraries/LibTimeZone/TimeZone.cpp +++ b/Userland/Libraries/LibTimeZone/TimeZone.cpp @@ -4,8 +4,10 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include #include -#include +#include namespace TimeZone { @@ -20,15 +22,77 @@ enum class TimeZone : u16 { }; #endif -StringView current_time_zone() -{ - static bool initialized_time_zone = false; - if (!initialized_time_zone) { - initialized_time_zone = true; - tzset(); +class TimeZoneFile { +public: + TimeZoneFile(char const* mode) + : m_file(fopen("/etc/timezone", mode)) + { + if (m_file) + flockfile(m_file); } - return canonicalize_time_zone(tzname[0]).value_or("UTC"sv); + ~TimeZoneFile() + { + if (m_file) { + funlockfile(m_file); + fclose(m_file); + } + } + + ErrorOr read_time_zone() + { + if (!m_file) + return Error::from_string_literal("Could not open /etc/timezone"); + + Array buffer; + size_t bytes = fread(buffer.data(), 1, buffer.size(), m_file); + + if (bytes == 0) + return Error::from_string_literal("Could not read time zone from /etc/timezone"); + + return String(buffer.span().slice(0, bytes)).trim_whitespace(); + } + + ErrorOr write_time_zone(StringView time_zone) + { + if (!m_file) + return Error::from_string_literal("Could not open /etc/timezone"); + + auto bytes = fwrite(time_zone.characters_without_null_termination(), 1, time_zone.length(), m_file); + if (bytes != time_zone.length()) + return Error::from_string_literal("Could not write new time zone to /etc/timezone"); + + return {}; + } + +private: + FILE* m_file { nullptr }; +}; + +StringView current_time_zone() +{ + TimeZoneFile time_zone_file("r"); + + // FIXME: Propagate the error to existing callers. + if (auto time_zone = time_zone_file.read_time_zone(); !time_zone.is_error()) + return canonicalize_time_zone(time_zone.value()).value_or("UTC"sv); + + return "UTC"sv; +} + +ErrorOr change_time_zone([[maybe_unused]] StringView time_zone) +{ +#ifdef __serenity__ + TimeZoneFile time_zone_file("w"); + + if (auto new_time_zone = canonicalize_time_zone(time_zone); new_time_zone.has_value()) + return time_zone_file.write_time_zone(*new_time_zone); + + return Error::from_string_literal("Provided time zone is not supported"); +#else + // Do not even attempt to change the time zone of someone's host machine. + return {}; +#endif } Span __attribute__((weak)) all_time_zones() diff --git a/Userland/Libraries/LibTimeZone/TimeZone.h b/Userland/Libraries/LibTimeZone/TimeZone.h index 493f89eb4d..fb4d58177a 100644 --- a/Userland/Libraries/LibTimeZone/TimeZone.h +++ b/Userland/Libraries/LibTimeZone/TimeZone.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -25,6 +26,7 @@ struct Offset { }; StringView current_time_zone(); +ErrorOr change_time_zone(StringView time_zone); Span all_time_zones(); Optional time_zone_from_string(StringView time_zone);