From 673843c99b7f7c86d336574359ad768b2714e718 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 18 Apr 2011 13:15:49 -0700 Subject: timezone: Add support for retrieving current timezone --- src/timezone.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 217 insertions(+), 1 deletion(-) diff --git a/src/timezone.c b/src/timezone.c index 339542e4..d0045b10 100644 --- a/src/timezone.c +++ b/src/timezone.c @@ -23,9 +23,225 @@ #include #endif +#include +#include +#include +#include +#include +#include +#include +#include + #include "connman.h" -char *__connman_timezone_lookup(void) +#define ETC_LOCALTIME "/etc/localtime" +#define ETC_SYSCONFIG_CLOCK "/etc/sysconfig/clock" +#define USR_SHARE_ZONEINFO "/usr/share/zoneinfo" + +static char *read_key_file(const char *pathname, const char *key) { + struct stat st; + char *map, *ptr, *str; + off_t ptrlen, keylen; + int fd; + + fd = open(pathname, O_RDONLY); + if (fd < 0) + return NULL; + + if (fstat(fd, &st) < 0) { + close(fd); + return NULL; + } + + map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (map == NULL || map == MAP_FAILED) { + close(fd); + return NULL; + } + + ptr = map; + ptrlen = st.st_size; + keylen = strlen(key); + + while (ptrlen > keylen + 1) { + int cmp = strncmp(ptr, key, keylen); + + if (cmp == 0) { + if (ptr == map) + break; + + if (*(ptr - 1) == '\n' && *(ptr + keylen) == '=') + break; + } + + ptr = memchr(ptr + 1, key[0], ptrlen - 1); + if (ptr == NULL) + break; + + ptrlen = st.st_size - (ptr - map); + } + + if (ptr != NULL) { + char *end, *val; + + ptrlen = st.st_size - (ptr - map); + + end = memchr(ptr, '\n', ptrlen); + if (end != NULL) + ptrlen = end - ptr; + + val = memchr(ptr, '"', ptrlen); + if (val != NULL) { + end = memchr(val + 1, '"', end - val - 1); + if (end != NULL) + str = strndup(val + 1, end - val - 1); + else + str = NULL; + } else + str = strndup(ptr + keylen + 1, ptrlen - keylen - 1); + } else + str = NULL; + + munmap(map, st.st_size); + + close(fd); + + return str; +} + +static int compare_file(void *src_map, struct stat *src_st, + const char *pathname) +{ + struct stat dst_st; + void *dst_map; + int fd, result; + + fd = open(pathname, O_RDONLY); + if (fd < 0) + return -1; + + if (fstat(fd, &dst_st) < 0) { + close(fd); + return -1; + } + + if (src_st->st_size != dst_st.st_size) { + close(fd); + return -1; + } + + dst_map = mmap(0, dst_st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (dst_map == NULL || dst_map == MAP_FAILED) { + close(fd); + return -1; + } + + result = memcmp(src_map, dst_map, src_st->st_size); + + munmap(dst_map, dst_st.st_size); + + close(fd); + + return result; +} + +static char *find_origin(void *src_map, struct stat *src_st, + const char *basepath) +{ + DIR *dir; + struct dirent *d; + char *str, pathname[PATH_MAX]; + + dir = opendir(basepath); + if (dir == NULL) + return NULL; + + while ((d = readdir(dir))) { + if (strcmp(d->d_name, ".") == 0 || + strcmp(d->d_name, "..") == 0 || + strcmp(d->d_name, "posix") == 0 || + strcmp(d->d_name, "right") == 0) + continue; + + snprintf(pathname, PATH_MAX, "%s/%s", basepath, d->d_name); + + switch (d->d_type) { + case DT_REG: + if (compare_file(src_map, src_st, pathname) == 0) { + closedir(dir); + return strdup(d->d_name); + } + break; + case DT_DIR: + str = find_origin(src_map, src_st, pathname); + if (str != NULL) { + closedir(dir); + return str; + } + break; + } + } + + closedir(dir); + return NULL; } + +char *__connman_timezone_lookup(void) +{ + struct stat st; + void *map; + int fd; + char *zone; + + zone = read_key_file(ETC_SYSCONFIG_CLOCK, "ZONE"); + + DBG("sysconfig zone %s", zone); + + fd = open(ETC_LOCALTIME, O_RDONLY); + if (fd < 0) { + free(zone); + return NULL; + } + + if (fstat(fd, &st) < 0) + goto done; + + if (S_ISREG(st.st_mode)) { + map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (map == NULL || map == MAP_FAILED) { + free(zone); + zone = NULL; + + goto done; + } + + if (zone != NULL) { + char pathname[PATH_MAX]; + + snprintf(pathname, PATH_MAX, "%s/%s", + USR_SHARE_ZONEINFO, zone); + + if (compare_file(map, &st, pathname) != 0) { + free(zone); + zone = NULL; + } + } + + if (zone == NULL) + zone = find_origin(map, &st, USR_SHARE_ZONEINFO); + + munmap(map, st.st_size); + } else { + free(zone); + zone = NULL; + } + +done: + close(fd); + + DBG("localtime zone %s", zone); + + return zone; +} -- cgit v1.2.3