1 /*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "properties"
18 // #define LOG_NDEBUG 0
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include <unistd.h>
24 #include <cutils/sockets.h>
25 #include <errno.h>
26 #include <assert.h>
27
28 #include <cutils/properties.h>
29 #include <stdbool.h>
30 #include <inttypes.h>
31 #include "loghack.h"
32
property_get_bool(const char * key,int8_t default_value)33 int8_t property_get_bool(const char *key, int8_t default_value) {
34 if (!key) {
35 return default_value;
36 }
37
38 int8_t result = default_value;
39 char buf[PROPERTY_VALUE_MAX] = {'\0',};
40
41 int len = property_get(key, buf, "");
42 if (len == 1) {
43 char ch = buf[0];
44 if (ch == '0' || ch == 'n') {
45 result = false;
46 } else if (ch == '1' || ch == 'y') {
47 result = true;
48 }
49 } else if (len > 1) {
50 if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {
51 result = false;
52 } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) {
53 result = true;
54 }
55 }
56
57 return result;
58 }
59
60 // Convert string property to int (default if fails); return default value if out of bounds
property_get_imax(const char * key,intmax_t lower_bound,intmax_t upper_bound,intmax_t default_value)61 static intmax_t property_get_imax(const char *key, intmax_t lower_bound, intmax_t upper_bound,
62 intmax_t default_value) {
63 if (!key) {
64 return default_value;
65 }
66
67 intmax_t result = default_value;
68 char buf[PROPERTY_VALUE_MAX] = {'\0',};
69 char *end = NULL;
70
71 int len = property_get(key, buf, "");
72 if (len > 0) {
73 int tmp = errno;
74 errno = 0;
75
76 // Infer base automatically
77 result = strtoimax(buf, &end, /*base*/0);
78 if ((result == INTMAX_MIN || result == INTMAX_MAX) && errno == ERANGE) {
79 // Over or underflow
80 result = default_value;
81 ALOGV("%s(%s,%" PRIdMAX ") - overflow", __FUNCTION__, key, default_value);
82 } else if (result < lower_bound || result > upper_bound) {
83 // Out of range of requested bounds
84 result = default_value;
85 ALOGV("%s(%s,%" PRIdMAX ") - out of range", __FUNCTION__, key, default_value);
86 } else if (end == buf) {
87 // Numeric conversion failed
88 result = default_value;
89 ALOGV("%s(%s,%" PRIdMAX ") - numeric conversion failed",
90 __FUNCTION__, key, default_value);
91 }
92
93 errno = tmp;
94 }
95
96 return result;
97 }
98
property_get_int64(const char * key,int64_t default_value)99 int64_t property_get_int64(const char *key, int64_t default_value) {
100 return (int64_t)property_get_imax(key, INT64_MIN, INT64_MAX, default_value);
101 }
102
property_get_int32(const char * key,int32_t default_value)103 int32_t property_get_int32(const char *key, int32_t default_value) {
104 return (int32_t)property_get_imax(key, INT32_MIN, INT32_MAX, default_value);
105 }
106
107 #ifdef HAVE_LIBC_SYSTEM_PROPERTIES
108
109 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
110 #include <sys/_system_properties.h>
111
property_set(const char * key,const char * value)112 int property_set(const char *key, const char *value)
113 {
114 return __system_property_set(key, value);
115 }
116
property_get(const char * key,char * value,const char * default_value)117 int property_get(const char *key, char *value, const char *default_value)
118 {
119 int len;
120
121 len = __system_property_get(key, value);
122 if(len > 0) {
123 return len;
124 }
125 if(default_value) {
126 len = strlen(default_value);
127 if (len >= PROPERTY_VALUE_MAX) {
128 len = PROPERTY_VALUE_MAX - 1;
129 }
130 memcpy(value, default_value, len);
131 value[len] = '\0';
132 }
133 return len;
134 }
135
136 struct property_list_callback_data
137 {
138 void (*propfn)(const char *key, const char *value, void *cookie);
139 void *cookie;
140 };
141
property_list_callback(const prop_info * pi,void * cookie)142 static void property_list_callback(const prop_info *pi, void *cookie)
143 {
144 char name[PROP_NAME_MAX];
145 char value[PROP_VALUE_MAX];
146 struct property_list_callback_data *data = cookie;
147
148 __system_property_read(pi, name, value);
149 data->propfn(name, value, data->cookie);
150 }
151
property_list(void (* propfn)(const char * key,const char * value,void * cookie),void * cookie)152 int property_list(
153 void (*propfn)(const char *key, const char *value, void *cookie),
154 void *cookie)
155 {
156 struct property_list_callback_data data = { propfn, cookie };
157 return __system_property_foreach(property_list_callback, &data);
158 }
159
160 #elif defined(HAVE_SYSTEM_PROPERTY_SERVER)
161
162 /*
163 * The Linux simulator provides a "system property server" that uses IPC
164 * to set/get/list properties. The file descriptor is shared by all
165 * threads in the process, so we use a mutex to ensure that requests
166 * from multiple threads don't get interleaved.
167 */
168 #include <stdio.h>
169 #include <sys/types.h>
170 #include <sys/socket.h>
171 #include <sys/un.h>
172 #include <pthread.h>
173
174 static pthread_once_t gInitOnce = PTHREAD_ONCE_INIT;
175 static pthread_mutex_t gPropertyFdLock = PTHREAD_MUTEX_INITIALIZER;
176 static int gPropFd = -1;
177
178 /*
179 * Connect to the properties server.
180 *
181 * Returns the socket descriptor on success.
182 */
connectToServer(const char * fileName)183 static int connectToServer(const char* fileName)
184 {
185 int sock = -1;
186 int cc;
187
188 struct sockaddr_un addr;
189
190 sock = socket(AF_UNIX, SOCK_STREAM, 0);
191 if (sock < 0) {
192 ALOGW("UNIX domain socket create failed (errno=%d)\n", errno);
193 return -1;
194 }
195
196 /* connect to socket; fails if file doesn't exist */
197 strcpy(addr.sun_path, fileName); // max 108 bytes
198 addr.sun_family = AF_UNIX;
199 cc = connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
200 if (cc < 0) {
201 // ENOENT means socket file doesn't exist
202 // ECONNREFUSED means socket exists but nobody is listening
203 //ALOGW("AF_UNIX connect failed for '%s': %s\n",
204 // fileName, strerror(errno));
205 close(sock);
206 return -1;
207 }
208
209 return sock;
210 }
211
212 /*
213 * Perform one-time initialization.
214 */
init(void)215 static void init(void)
216 {
217 assert(gPropFd == -1);
218
219 gPropFd = connectToServer(SYSTEM_PROPERTY_PIPE_NAME);
220 if (gPropFd < 0) {
221 //ALOGW("not connected to system property server\n");
222 } else {
223 //ALOGV("Connected to system property server\n");
224 }
225 }
226
property_get(const char * key,char * value,const char * default_value)227 int property_get(const char *key, char *value, const char *default_value)
228 {
229 char sendBuf[1+PROPERTY_KEY_MAX];
230 char recvBuf[1+PROPERTY_VALUE_MAX];
231 int len = -1;
232
233 //ALOGV("PROPERTY GET [%s]\n", key);
234
235 pthread_once(&gInitOnce, init);
236 if (gPropFd < 0) {
237 /* this mimics the behavior of the device implementation */
238 if (default_value != NULL) {
239 strcpy(value, default_value);
240 len = strlen(value);
241 }
242 return len;
243 }
244
245 if (strlen(key) >= PROPERTY_KEY_MAX) return -1;
246
247 memset(sendBuf, 0xdd, sizeof(sendBuf)); // placate valgrind
248
249 sendBuf[0] = (char) kSystemPropertyGet;
250 strcpy(sendBuf+1, key);
251
252 pthread_mutex_lock(&gPropertyFdLock);
253 if (write(gPropFd, sendBuf, sizeof(sendBuf)) != sizeof(sendBuf)) {
254 pthread_mutex_unlock(&gPropertyFdLock);
255 return -1;
256 }
257 if (read(gPropFd, recvBuf, sizeof(recvBuf)) != sizeof(recvBuf)) {
258 pthread_mutex_unlock(&gPropertyFdLock);
259 return -1;
260 }
261 pthread_mutex_unlock(&gPropertyFdLock);
262
263 /* first byte is 0 if value not defined, 1 if found */
264 if (recvBuf[0] == 0) {
265 if (default_value != NULL) {
266 strcpy(value, default_value);
267 len = strlen(value);
268 } else {
269 /*
270 * If the value isn't defined, hand back an empty string and
271 * a zero length, rather than a failure. This seems wrong,
272 * since you can't tell the difference between "undefined" and
273 * "defined but empty", but it's what the device does.
274 */
275 value[0] = '\0';
276 len = 0;
277 }
278 } else if (recvBuf[0] == 1) {
279 strcpy(value, recvBuf+1);
280 len = strlen(value);
281 } else {
282 ALOGE("Got strange response to property_get request (%d)\n",
283 recvBuf[0]);
284 assert(0);
285 return -1;
286 }
287 //ALOGV("PROP [found=%d def='%s'] (%d) [%s]: [%s]\n",
288 // recvBuf[0], default_value, len, key, value);
289
290 return len;
291 }
292
293
property_set(const char * key,const char * value)294 int property_set(const char *key, const char *value)
295 {
296 char sendBuf[1+PROPERTY_KEY_MAX+PROPERTY_VALUE_MAX];
297 char recvBuf[1];
298 int result = -1;
299
300 //ALOGV("PROPERTY SET [%s]: [%s]\n", key, value);
301
302 pthread_once(&gInitOnce, init);
303 if (gPropFd < 0)
304 return -1;
305
306 if (strlen(key) >= PROPERTY_KEY_MAX) return -1;
307 if (strlen(value) >= PROPERTY_VALUE_MAX) return -1;
308
309 memset(sendBuf, 0xdd, sizeof(sendBuf)); // placate valgrind
310
311 sendBuf[0] = (char) kSystemPropertySet;
312 strcpy(sendBuf+1, key);
313 strcpy(sendBuf+1+PROPERTY_KEY_MAX, value);
314
315 pthread_mutex_lock(&gPropertyFdLock);
316 if (write(gPropFd, sendBuf, sizeof(sendBuf)) != sizeof(sendBuf)) {
317 pthread_mutex_unlock(&gPropertyFdLock);
318 return -1;
319 }
320 if (read(gPropFd, recvBuf, sizeof(recvBuf)) != sizeof(recvBuf)) {
321 pthread_mutex_unlock(&gPropertyFdLock);
322 return -1;
323 }
324 pthread_mutex_unlock(&gPropertyFdLock);
325
326 if (recvBuf[0] != 1)
327 return -1;
328 return 0;
329 }
330
property_list(void (* propfn)(const char * key,const char * value,void * cookie),void * cookie)331 int property_list(void (*propfn)(const char *key, const char *value, void *cookie),
332 void *cookie)
333 {
334 //ALOGV("PROPERTY LIST\n");
335 pthread_once(&gInitOnce, init);
336 if (gPropFd < 0)
337 return -1;
338
339 return 0;
340 }
341
342 #else
343
344 /* SUPER-cheesy place-holder implementation for Win32 */
345
346 #include <cutils/threads.h>
347
348 static mutex_t env_lock = MUTEX_INITIALIZER;
349
property_get(const char * key,char * value,const char * default_value)350 int property_get(const char *key, char *value, const char *default_value)
351 {
352 char ename[PROPERTY_KEY_MAX + 6];
353 char *p;
354 int len;
355
356 len = strlen(key);
357 if(len >= PROPERTY_KEY_MAX) return -1;
358 memcpy(ename, "PROP_", 5);
359 memcpy(ename + 5, key, len + 1);
360
361 mutex_lock(&env_lock);
362
363 p = getenv(ename);
364 if(p == 0) p = "";
365 len = strlen(p);
366 if(len >= PROPERTY_VALUE_MAX) {
367 len = PROPERTY_VALUE_MAX - 1;
368 }
369
370 if((len == 0) && default_value) {
371 len = strlen(default_value);
372 memcpy(value, default_value, len + 1);
373 } else {
374 memcpy(value, p, len);
375 value[len] = 0;
376 }
377
378 mutex_unlock(&env_lock);
379
380 return len;
381 }
382
383
property_set(const char * key,const char * value)384 int property_set(const char *key, const char *value)
385 {
386 char ename[PROPERTY_KEY_MAX + 6];
387 char *p;
388 int len;
389 int r;
390
391 if(strlen(value) >= PROPERTY_VALUE_MAX) return -1;
392
393 len = strlen(key);
394 if(len >= PROPERTY_KEY_MAX) return -1;
395 memcpy(ename, "PROP_", 5);
396 memcpy(ename + 5, key, len + 1);
397
398 mutex_lock(&env_lock);
399 #ifdef HAVE_MS_C_RUNTIME
400 {
401 char temp[256];
402 snprintf( temp, sizeof(temp), "%s=%s", ename, value);
403 putenv(temp);
404 r = 0;
405 }
406 #else
407 r = setenv(ename, value, 1);
408 #endif
409 mutex_unlock(&env_lock);
410
411 return r;
412 }
413
property_list(void (* propfn)(const char * key,const char * value,void * cookie),void * cookie)414 int property_list(void (*propfn)(const char *key, const char *value, void *cookie),
415 void *cookie)
416 {
417 return 0;
418 }
419
420 #endif
421