timezone: Add support for retrieving current timezone
[platform/upstream/connman.git] / src / timezone.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <dirent.h>
32 #include <sys/stat.h>
33 #include <sys/mman.h>
34
35 #include "connman.h"
36
37 #define ETC_LOCALTIME           "/etc/localtime"
38 #define ETC_SYSCONFIG_CLOCK     "/etc/sysconfig/clock"
39 #define USR_SHARE_ZONEINFO      "/usr/share/zoneinfo"
40
41 static char *read_key_file(const char *pathname, const char *key)
42 {
43         struct stat st;
44         char *map, *ptr, *str;
45         off_t ptrlen, keylen;
46         int fd;
47
48         fd = open(pathname, O_RDONLY);
49         if (fd < 0)
50                 return NULL;
51
52         if (fstat(fd, &st) < 0) {
53                 close(fd);
54                 return NULL;
55         }
56
57         map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
58         if (map == NULL || map == MAP_FAILED) {
59                 close(fd);
60                 return NULL;
61         }
62
63         ptr = map;
64         ptrlen = st.st_size;
65         keylen = strlen(key);
66
67         while (ptrlen > keylen + 1) {
68                 int cmp = strncmp(ptr, key, keylen);
69
70                 if (cmp == 0) {
71                         if (ptr == map)
72                                 break;
73
74                         if (*(ptr - 1) == '\n' && *(ptr + keylen) == '=')
75                                 break;
76                 }
77
78                 ptr = memchr(ptr + 1, key[0], ptrlen - 1);
79                 if (ptr == NULL)
80                         break;
81
82                 ptrlen = st.st_size - (ptr - map);
83         }
84
85         if (ptr != NULL) {
86                 char *end, *val;
87
88                 ptrlen = st.st_size - (ptr - map);
89
90                 end = memchr(ptr, '\n', ptrlen);
91                 if (end != NULL)
92                         ptrlen = end - ptr;
93
94                 val = memchr(ptr, '"', ptrlen);
95                 if (val != NULL) {
96                         end = memchr(val + 1, '"', end - val - 1);
97                         if (end != NULL)
98                                 str = strndup(val + 1, end - val - 1);
99                         else
100                                 str = NULL;
101                 } else
102                         str = strndup(ptr + keylen + 1, ptrlen - keylen - 1);
103         } else
104                 str = NULL;
105
106         munmap(map, st.st_size);
107
108         close(fd);
109
110         return str;
111 }
112
113 static int compare_file(void *src_map, struct stat *src_st,
114                                                 const char *pathname)
115 {
116         struct stat dst_st;
117         void *dst_map;
118         int fd, result;
119
120         fd = open(pathname, O_RDONLY);
121         if (fd < 0)
122                 return -1;
123
124         if (fstat(fd, &dst_st) < 0) {
125                 close(fd);
126                 return -1;
127         }
128
129         if (src_st->st_size != dst_st.st_size) {
130                 close(fd);
131                 return -1;
132         }
133
134         dst_map = mmap(0, dst_st.st_size, PROT_READ, MAP_SHARED, fd, 0);
135         if (dst_map == NULL || dst_map == MAP_FAILED) {
136                 close(fd);
137                 return -1;
138         }
139
140         result = memcmp(src_map, dst_map, src_st->st_size);
141
142         munmap(dst_map, dst_st.st_size);
143
144         close(fd);
145
146         return result;
147 }
148
149 static char *find_origin(void *src_map, struct stat *src_st,
150                                                 const char *basepath)
151 {
152         DIR *dir;
153         struct dirent *d;
154         char *str, pathname[PATH_MAX];
155
156         dir = opendir(basepath);
157         if (dir == NULL)
158                 return NULL;
159
160         while ((d = readdir(dir))) {
161                 if (strcmp(d->d_name, ".") == 0 ||
162                                 strcmp(d->d_name, "..") == 0 ||
163                                 strcmp(d->d_name, "posix") == 0 ||
164                                 strcmp(d->d_name, "right") == 0)
165                         continue;
166
167                 snprintf(pathname, PATH_MAX, "%s/%s", basepath, d->d_name);
168
169                 switch (d->d_type) {
170                 case DT_REG:
171                         if (compare_file(src_map, src_st, pathname) == 0) {
172                                 closedir(dir);
173                                 return strdup(d->d_name);
174                         }
175                         break;
176                 case DT_DIR:
177                         str = find_origin(src_map, src_st, pathname);
178                         if (str != NULL) {
179                                 closedir(dir);
180                                 return str;
181                         }
182                         break;
183                 }
184         }
185
186         closedir(dir);
187
188         return NULL;
189 }
190
191 char *__connman_timezone_lookup(void)
192 {
193         struct stat st;
194         void *map;
195         int fd;
196         char *zone;
197
198         zone = read_key_file(ETC_SYSCONFIG_CLOCK, "ZONE");
199
200         DBG("sysconfig zone %s", zone);
201
202         fd = open(ETC_LOCALTIME, O_RDONLY);
203         if (fd < 0) {
204                 free(zone);
205                 return NULL;
206         }
207
208         if (fstat(fd, &st) < 0)
209                 goto done;
210
211         if (S_ISREG(st.st_mode)) {
212                 map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
213                 if (map == NULL || map == MAP_FAILED) {
214                         free(zone);
215                         zone = NULL;
216
217                         goto done;
218                 }
219
220                 if (zone != NULL) {
221                         char pathname[PATH_MAX];
222
223                         snprintf(pathname, PATH_MAX, "%s/%s",
224                                                 USR_SHARE_ZONEINFO, zone);
225
226                         if (compare_file(map, &st, pathname) != 0) {
227                                 free(zone);
228                                 zone = NULL;
229                         }
230                 }
231
232                 if (zone == NULL)
233                         zone = find_origin(map, &st, USR_SHARE_ZONEINFO);
234
235                 munmap(map, st.st_size);
236         } else {
237                 free(zone);
238                 zone = NULL;
239         }
240
241 done:
242         close(fd);
243
244         DBG("localtime zone %s", zone);
245
246         return zone;
247 }