Imported Upstream version 0.1.0+git20131207+e452e83
[deb_libhybris.git] / hybris / properties / cache.c
1 /*
2 * Copyright (c) 2012 Carsten Munk <carsten.munk@gmail.com>
3 * Copyright (c) 2008 The Android Open Source Project
4 * Copyright (c) 2013 Simon Busch <morphis@gravedo.de>
5 * Copyright (c) 2013 Canonical Ltd
6 * Copyright (c) 2013 Jolla Ltd. <robin.burchell@jollamobile.com>
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <fcntl.h>
26 #include <sys/stat.h>
27
28 #include <hybris/properties/properties.h>
29 #include "properties_p.h"
30
31 struct hybris_prop_value
32 {
33 char *key;
34 char *value;
35 };
36
37 /* surely enough for anyone */
38 #define MAX_PROPS 1000
39
40 /* the sorted prop array */
41 static struct hybris_prop_value prop_array[MAX_PROPS];
42
43 /* the current highest index in prop_array */
44 static int max_prop;
45
46 /* helpers */
47 static int prop_qcmp(const void *a, const void *b);
48 static struct hybris_prop_value *cache_find_internal(const char *key);
49 static void cache_add_internal(const char *key, const char *value);
50 static void cache_repopulate_internal(FILE *f);
51 static void cache_empty_internal();
52 static void cache_repopulate_cmdline_internal();
53
54 /* the inode/mtime of the prop cache, used for invalidation */
55 static ino_t static_prop_inode;
56 static time_t static_prop_mtime;
57
58
59 /* public:
60 * find a prop value from the file cache.
61 *
62 * the return value is the value of the given property key, or NULL if the
63 * property key is not found. the returned value is owned by the caller,
64 * and must be freed.
65 */
66 char *hybris_propcache_find(const char *key)
67 {
68 struct stat st;
69 FILE *f = fopen("/system/build.prop", "r");
70 char *ret = NULL;
71
72 if (!f)
73 return NULL;
74
75 /* before searching, we must first determine whether our cache is valid. if
76 * it isn't, we must discard our results and re-create the cache.
77 *
78 * we use fstat here to avoid a race between stat and something else
79 * touching the file.
80 */
81 if (fstat(fileno(f), &st) != 0) {
82 perror("cache_find can't stat build.prop");
83 goto out;
84 }
85
86 /* TODO: is there any better way to detect changes? */
87 if (static_prop_inode != st.st_ino ||
88 static_prop_mtime != st.st_mtime) {
89 static_prop_inode = st.st_ino;
90 static_prop_mtime = st.st_mtime;
91
92 /* cache is stale. fill it back up with fresh data first. */
93 cache_empty_internal();
94 cache_repopulate_internal(f);
95 cache_repopulate_cmdline_internal();
96
97 /* sort by keys */
98 qsort(prop_array, max_prop, sizeof(struct hybris_prop_value), prop_qcmp);
99 }
100
101 /* then look up the key and do a copy if we get a result */
102 struct hybris_prop_value *prop = cache_find_internal(key);
103 if (prop)
104 ret = strdup(prop->value);
105
106 out:
107 fclose(f);
108 return ret;
109 }
110
111 /* private:
112 * empties the prop cache, ready for repopulation
113 */
114 static void cache_empty_internal()
115 {
116 int i;
117 for (i = 0; i < max_prop; ++i) {
118 free(prop_array[i].key);
119 free(prop_array[i].value);
120 }
121
122 max_prop = 0;
123 }
124
125 /* private:
126 * compares two hybris_prop_value by key, so as to maintain a qsorted array of
127 * props, and search the array.
128 */
129 static int prop_qcmp(const void *a, const void *b)
130 {
131 struct hybris_prop_value *aa = (struct hybris_prop_value *)a;
132 struct hybris_prop_value *bb = (struct hybris_prop_value *)b;
133
134 return strcmp(aa->key, bb->key);
135 }
136
137 /* private:
138 * find a given key in the in-memory prop cache.
139 *
140 * returns the value of the given property key, or NULL if the property is not
141 * found. Note that this does not pass ownership of the hybris_prop_value or the
142 * data inside it.
143 */
144 static struct hybris_prop_value *cache_find_internal(const char *key)
145 {
146 struct hybris_prop_value prop_key;
147 prop_key.key = (char*)key;
148
149 return bsearch(&prop_key, prop_array, max_prop, sizeof(struct hybris_prop_value), prop_qcmp);
150 }
151
152 /* private:
153 * add a given property to the in-memory prop cache for later retrieval.
154 *
155 * both `key' and `value' are copied from the caller.
156 */
157 static void cache_add_internal(const char *key, const char *value)
158 {
159 /* Skip values that can be bigger than value max */
160 if (strlen(value) >= PROP_VALUE_MAX -1)
161 return;
162
163 /* preserve current behavior of first prop key => match */
164 if (cache_find_internal(key))
165 return;
166
167 prop_array[max_prop].key = strdup(key);
168 prop_array[max_prop++].value = strdup(value);
169
170 if (max_prop >= MAX_PROPS) {
171 fprintf(stderr, "libhybris: ran out of props, increase MAX_PROPS");
172 exit(1);
173 }
174 }
175
176 /* private:
177 * repopulates the prop cache from a given file `f'.
178 */
179 static void cache_repopulate_internal(FILE *f)
180 {
181 char buf[1024];
182 char *mkey, *value;
183
184 while (fgets(buf, 1024, f) != NULL) {
185 if (strchr(buf, '\r'))
186 *(strchr(buf, '\r')) = '\0';
187 if (strchr(buf, '\n'))
188 *(strchr(buf, '\n')) = '\0';
189
190 mkey = strtok(buf, "=");
191
192 if (!mkey)
193 continue;
194
195 value = strtok(NULL, "=");
196 if (!value)
197 continue;
198
199 cache_add_internal(mkey, value);
200 }
201 }
202
203 /* private:
204 * repopulate the prop cache from /proc/cmdline
205 */
206 static void cache_repopulate_cmdline_internal()
207 {
208 /* Find a key value from the kernel command line, which is parsed
209 * by Android at init (on an Android working system) */
210 char cmdline[1024];
211 char *ptr;
212 int fd;
213
214 fd = open("/proc/cmdline", O_RDONLY);
215 if (fd >= 0) {
216 int n = read(fd, cmdline, 1023);
217 if (n < 0) n = 0;
218
219 /* get rid of trailing newline, it happens */
220 if (n > 0 && cmdline[n-1] == '\n') n--;
221
222 cmdline[n] = 0;
223 close(fd);
224 } else {
225 cmdline[0] = 0;
226 }
227
228 ptr = cmdline;
229
230 while (ptr && *ptr) {
231 char *x = strchr(ptr, ' ');
232 if (x != 0) *x++ = 0;
233
234 char *name = ptr;
235 ptr = x;
236
237 char *value = strchr(name, '=');
238 int name_len = strlen(name);
239
240 if (value == 0) continue;
241 *value++ = 0;
242 if (name_len == 0) continue;
243
244 if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
245 const char *boot_prop_name = name + 12;
246 char prop[PROP_NAME_MAX];
247 snprintf(prop, sizeof(prop) -1, "ro.%s", boot_prop_name);
248
249 cache_add_internal(prop, value);
250 }
251 }
252 }