Commit | Line | Data |
---|---|---|
d42e7319 JB |
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 | } |