5 * Copyright (C) 2007-2012 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
36 #include <sys/inotify.h>
42 #define ETC_LOCALTIME "/etc/localtime"
43 #define ETC_SYSCONFIG_CLOCK "/etc/sysconfig/clock"
44 #define USR_SHARE_ZONEINFO "/usr/share/zoneinfo"
46 static char *read_key_file(const char *pathname, const char *key)
49 char *map, *ptr, *str;
53 fd = open(pathname, O_RDONLY | O_CLOEXEC);
57 if (fstat(fd, &st) < 0) {
62 map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
63 if (map == NULL || map == MAP_FAILED) {
72 while (ptrlen > keylen + 1) {
73 int cmp = strncmp(ptr, key, keylen);
79 if (*(ptr - 1) == '\n' && *(ptr + keylen) == '=')
83 ptr = memchr(ptr + 1, key[0], ptrlen - 1);
87 ptrlen = st.st_size - (ptr - map);
93 ptrlen = st.st_size - (ptr - map);
95 end = memchr(ptr, '\n', ptrlen);
99 val = memchr(ptr, '"', ptrlen);
101 end = memchr(val + 1, '"', end - val - 1);
103 str = g_strndup(val + 1, end - val - 1);
107 str = g_strndup(ptr + keylen + 1, ptrlen - keylen - 1);
111 munmap(map, st.st_size);
118 static int compare_file(void *src_map, struct stat *src_st,
119 const char *pathname)
125 fd = open(pathname, O_RDONLY | O_CLOEXEC);
129 if (fstat(fd, &dst_st) < 0) {
134 if (src_st->st_size != dst_st.st_size) {
139 dst_map = mmap(0, dst_st.st_size, PROT_READ, MAP_SHARED, fd, 0);
140 if (dst_map == NULL || dst_map == MAP_FAILED) {
145 result = memcmp(src_map, dst_map, src_st->st_size);
147 munmap(dst_map, dst_st.st_size);
154 static char *find_origin(void *src_map, struct stat *src_st,
155 const char *basepath, const char *subpath)
159 char *str, pathname[PATH_MAX];
162 strncpy(pathname, basepath, sizeof(pathname));
164 snprintf(pathname, sizeof(pathname),
165 "%s/%s", basepath, subpath);
167 dir = opendir(pathname);
171 while ((d = readdir(dir))) {
172 if (strcmp(d->d_name, ".") == 0 ||
173 strcmp(d->d_name, "..") == 0 ||
174 strcmp(d->d_name, "posix") == 0 ||
175 strcmp(d->d_name, "right") == 0)
181 snprintf(pathname, PATH_MAX,
182 "%s/%s", basepath, d->d_name);
184 snprintf(pathname, PATH_MAX,
185 "%s/%s/%s", basepath,
188 if (compare_file(src_map, src_st, pathname) == 0) {
189 str = g_strdup_printf("%s/%s",
197 strncpy(pathname, d->d_name, sizeof(pathname));
199 snprintf(pathname, sizeof(pathname),
200 "%s/%s", subpath, d->d_name);
202 str = find_origin(src_map, src_st, basepath, pathname);
216 char *__connman_timezone_lookup(void)
223 zone = read_key_file(ETC_SYSCONFIG_CLOCK, "ZONE");
225 DBG("sysconfig zone %s", zone);
227 fd = open(ETC_LOCALTIME, O_RDONLY | O_CLOEXEC);
233 if (fstat(fd, &st) < 0)
236 if (S_ISREG(st.st_mode)) {
237 map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
238 if (map == NULL || map == MAP_FAILED) {
246 char pathname[PATH_MAX];
248 snprintf(pathname, PATH_MAX, "%s/%s",
249 USR_SHARE_ZONEINFO, zone);
251 if (compare_file(map, &st, pathname) != 0) {
258 zone = find_origin(map, &st, USR_SHARE_ZONEINFO, NULL);
260 munmap(map, st.st_size);
269 DBG("localtime zone %s", zone);
274 static int write_file(void *src_map, struct stat *src_st, const char *pathname)
280 DBG("pathname %s", pathname);
282 if (lstat(pathname, &st) == 0) {
283 if (S_ISLNK(st.st_mode))
287 fd = open(pathname, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
291 written = write(fd, src_map, src_st->st_size);
301 int __connman_timezone_change(const char *zone)
304 char *map, pathname[PATH_MAX];
307 DBG("zone %s", zone);
309 snprintf(pathname, PATH_MAX, "%s/%s", USR_SHARE_ZONEINFO, zone);
311 fd = open(pathname, O_RDONLY | O_CLOEXEC);
315 if (fstat(fd, &st) < 0) {
320 map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
321 if (map == NULL || map == MAP_FAILED) {
326 err = write_file(map, &st, ETC_LOCALTIME);
328 munmap(map, st.st_size);
335 static guint inotify_watch = 0;
337 static gboolean inotify_data(GIOChannel *channel, GIOCondition cond,
347 if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
352 status = g_io_channel_read_chars(channel, buffer, sizeof(buffer),
356 case G_IO_STATUS_NORMAL:
358 case G_IO_STATUS_AGAIN:
365 DBG("bytes read %zd", bytes_read);
367 while (bytes_read > 0) {
368 struct inotify_event *event = ptr;
370 if (bytes_read < sizeof(*event))
373 ptr += sizeof(*event);
374 bytes_read -= sizeof(*event);
379 if (bytes_read < event->len)
383 bytes_read -= event->len;
385 if (g_strcmp0(event->name, "localtime") == 0)
386 __connman_clock_update_timezone();
392 int __connman_timezone_init(void)
400 fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
404 channel = g_io_channel_unix_new(fd);
405 if (channel == NULL) {
410 g_io_channel_set_close_on_unref(channel, TRUE);
411 g_io_channel_set_encoding(channel, NULL, NULL);
412 g_io_channel_set_buffered(channel, FALSE);
414 inotify_watch = g_io_add_watch(channel,
415 G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
418 g_io_channel_unref(channel);
420 dirname = g_path_get_dirname(ETC_LOCALTIME);
422 wd = inotify_add_watch(fd, dirname, IN_DONT_FOLLOW |
423 IN_CLOSE_WRITE | IN_MOVED_TO);
433 void __connman_timezone_cleanup(void)
437 if (inotify_watch > 0) {
438 g_source_remove(inotify_watch);