diff options
Diffstat (limited to '')
-rw-r--r-- | src/VBox/Runtime/r3/posix/RTTimeZoneGetCurrent-posix.cpp | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r3/posix/RTTimeZoneGetCurrent-posix.cpp b/src/VBox/Runtime/r3/posix/RTTimeZoneGetCurrent-posix.cpp new file mode 100644 index 00000000..b921c0f6 --- /dev/null +++ b/src/VBox/Runtime/r3/posix/RTTimeZoneGetCurrent-posix.cpp @@ -0,0 +1,239 @@ +/* $Id: RTTimeZoneGetCurrent-posix.cpp $ */ +/** @file + * IPRT - RTTimeZoneGetCurrent, POSIX. + */ + +/* + * Copyright (C) 2020 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/time.h> +#include "internal/iprt.h" + +#include <iprt/env.h> +#include <iprt/file.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/err.h> +#include <iprt/errcore.h> +#include <iprt/types.h> +#include <iprt/symlink.h> +#include <iprt/stream.h> + +#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) +# include <tzfile.h> +#else +# define TZDIR "/usr/share/zoneinfo" +# define TZ_MAGIC "TZif" +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define PATH_LOCALTIME "/etc/localtime" +#if defined(RT_OS_FREEBSD) +# define PATH_TIMEZONE "/var/db/zoneinfo" +#else +# define PATH_TIMEZONE "/etc/timezone" +#endif +#define PATH_SYSCONFIG_CLOCK "/etc/sysconfig/clock" + + +/** + * Checks if a time zone database file is valid by verifying it begins with + * TZ_MAGIC. + * + * @returns IPRT status code. + * @param pszTimezone The time zone database file relative to + * <tzfile.h>:TZDIR (normally /usr/share/zoneinfo), + * e.g. Europe/London, or Etc/UTC, or UTC, or etc. + */ +static int rtIsValidTimeZoneFile(const char *pszTimeZone) +{ + if (pszTimeZone == NULL || *pszTimeZone == '\0' || *pszTimeZone == '/') + return VERR_INVALID_PARAMETER; + + int rc = RTStrValidateEncoding(pszTimeZone); + if (RT_SUCCESS(rc)) + { + /* construct full pathname of the time zone file */ + char szTZPath[RTPATH_MAX]; + rc = RTPathJoin(szTZPath, sizeof(szTZPath), TZDIR, pszTimeZone); + if (RT_SUCCESS(rc)) + { + /* open the time zone file and check that it begins with the correct magic number */ + RTFILE hFile = NIL_RTFILE; + rc = RTFileOpen(&hFile, szTZPath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE); + if (RT_SUCCESS(rc)) + { + char achTZBuf[sizeof(TZ_MAGIC)]; + rc = RTFileRead(hFile, achTZBuf, sizeof(achTZBuf), NULL); + RTFileClose(hFile); + if (RT_SUCCESS(rc)) + { + if (memcmp(achTZBuf, RT_STR_TUPLE(TZ_MAGIC)) == 0) + rc = VINF_SUCCESS; + else + rc = VERR_INVALID_MAGIC; + } + } + } + } + + return rc; +} + + +/** + * Return the system time zone. + * + * @returns IPRT status code. + * @param pszName The buffer to return the time zone in. + * @param cbName The size of the pszName buffer. + */ +RTDECL(int) RTTimeZoneGetCurrent(char *pszName, size_t cbName) +{ + int rc = RTEnvGetEx(RTENV_DEFAULT, "TZ", pszName, cbName, NULL); + if (RT_SUCCESS(rc)) + { + /* + * $TZ can have two different formats and one of them doesn't specify + * a time zone database file under <tzfile.h>:TZDIR but since all + * current callers of this routine expect a time zone filename we do + * the validation check here so that if it is invalid then we fall back + * to the other mechanisms to return the system's current time zone. + */ + if (*pszName == ':') /* POSIX allows $TZ to begin with a colon (:) so we allow for that here */ + memmove(pszName, pszName + 1, strlen(pszName)); + /** @todo this isn't perfect for absolute paths... Should probably try treat + * it like /etc/localtime. */ + rc = rtIsValidTimeZoneFile(pszName); + if (RT_SUCCESS(rc)) + return rc; + } + else if (rc != VERR_ENV_VAR_NOT_FOUND) + return rc; + + /* + * /etc/localtime is a symbolic link to the system time zone on many OSes + * including Solaris, macOS, Ubuntu, RH/OEL 6 and later, Arch Linux, NetBSD, + * and etc. We extract the time zone pathname relative to TZDIR defined in + * <tzfile.h> which is normally /usr/share/zoneinfo. + * + * N.B. Some OSes have /etc/localtime as a regular file instead of a + * symlink and while we could trawl through all the files under TZDIR + * looking for a match we instead fallback to other popular mechanisms of + * specifying the system-wide time zone for the sake of simplicity. + */ + char szBuf[RTPATH_MAX]; + const char *pszPath = PATH_LOCALTIME; + if (RTSymlinkExists(pszPath)) + { + /* the contents of the symink may contain '..' or other links */ + char szLinkPathReal[RTPATH_MAX]; + rc = RTPathReal(pszPath, szLinkPathReal, sizeof(szLinkPathReal)); + if (RT_SUCCESS(rc)) + { + rc = RTPathReal(TZDIR, szBuf, sizeof(szBuf)); + AssertRC(rc); + if (RT_SUCCESS(rc)) + { + Assert(RTPathStartsWith(szLinkPathReal, szBuf)); + if (RTPathStartsWith(szLinkPathReal, szBuf)) + { + /* <tzfile.h>:TZDIR doesn't include a trailing slash */ + const char *pszTimeZone = &szLinkPathReal[strlen(szBuf) + 1]; + rc = rtIsValidTimeZoneFile(pszTimeZone); + if (RT_SUCCESS(rc)) + return RTStrCopy(pszName, cbName, pszTimeZone); + } + } + } + } + + /* + * /etc/timezone is a regular file consisting of a single line containing + * the time zone (e.g. Europe/London or Etc/UTC or etc.) and is used by a + * variety of Linux distros such as Ubuntu, Gentoo, Debian, and etc. + * The equivalent on FreeBSD is /var/db/zoneinfo. + */ + pszPath = PATH_TIMEZONE; + if (RTFileExists(pszPath)) + { + RTFILE hFile = NIL_RTFILE; + rc = RTFileOpen(&hFile, PATH_TIMEZONE, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE); + if (RT_SUCCESS(rc)) + { + size_t cbRead = 0; + rc = RTFileRead(hFile, szBuf, sizeof(szBuf), &cbRead); + RTFileClose(hFile); + if (RT_SUCCESS(rc) && cbRead > 0) + { + /* Get the first line and strip it. */ + szBuf[RT_MIN(sizeof(szBuf) - 1, cbRead)] = '\0'; + size_t const offNewLine = RTStrOffCharOrTerm(szBuf, '\n'); + szBuf[offNewLine] = '\0'; + const char *pszTimeZone = RTStrStrip(szBuf); + + rc = rtIsValidTimeZoneFile(pszTimeZone); + if (RT_SUCCESS(rc)) + return RTStrCopy(pszName, cbName, pszTimeZone); + } + } + } + + /* + * Older versions of RedHat / OEL don't have /etc/localtime as a symlink or + * /etc/timezone but instead have /etc/sysconfig/clock which contains a line + * of the syntax ZONE=Europe/London amongst other entries. + */ + pszPath = PATH_SYSCONFIG_CLOCK; + if (RTFileExists(pszPath)) + { + PRTSTREAM pStrm; + rc = RTStrmOpen(pszPath, "r", &pStrm); + if (RT_SUCCESS(rc)) + { + while (RT_SUCCESS(rc = RTStrmGetLine(pStrm, szBuf, sizeof(szBuf)))) + { + static char const s_szVarEq[] = "ZONE="; + if (memcmp(szBuf, RT_STR_TUPLE(s_szVarEq)) == 0) + { + const char *pszTimeZone = &szBuf[sizeof(s_szVarEq) - 1]; + rc = rtIsValidTimeZoneFile(pszTimeZone); + if (RT_SUCCESS(rc)) + { + RTStrmClose(pStrm); + return RTStrCopy(pszName, cbName, pszTimeZone); + } + } + } + RTStrmClose(pStrm); + } + } + + return rc; +} + |