1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *  * Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 *  * Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in
12 *    the documentation and/or other materials provided with the
13 *    distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28#include <stdio.h>
29#include <stdint.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <stddef.h>
33#include <errno.h>
34#include <poll.h>
35#include <fcntl.h>
36#include <stdbool.h>
37#include <string.h>
38
39#include <sys/mman.h>
40
41#include <sys/socket.h>
42#include <sys/un.h>
43#include <sys/select.h>
44#include <sys/stat.h>
45#include <sys/types.h>
46#include <netinet/in.h>
47#include <unistd.h>
48
49#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
50#include <sys/_system_properties.h>
51
52#include <sys/atomics.h>
53#include <bionic_atomic_inline.h>
54
55#define ALIGN(x, a) (((x) + (a - 1)) & ~(a - 1))
56
57struct prop_area {
58    unsigned bytes_used;
59    unsigned volatile serial;
60    unsigned magic;
61    unsigned version;
62    unsigned reserved[28];
63    char data[0];
64};
65
66typedef struct prop_area prop_area;
67
68struct prop_info {
69    unsigned volatile serial;
70    char value[PROP_VALUE_MAX];
71    char name[0];
72};
73
74typedef struct prop_info prop_info;
75
76/*
77 * Properties are stored in a hybrid trie/binary tree structure.
78 * Each property's name is delimited at '.' characters, and the tokens are put
79 * into a trie structure.  Siblings at each level of the trie are stored in a
80 * binary tree.  For instance, "ro.secure"="1" could be stored as follows:
81 *
82 * +-----+   children    +----+   children    +--------+
83 * |     |-------------->| ro |-------------->| secure |
84 * +-----+               +----+               +--------+
85 *                       /    \                /   |
86 *                 left /      \ right   left /    |  prop   +===========+
87 *                     v        v            v     +-------->| ro.secure |
88 *                  +-----+   +-----+     +-----+            +-----------+
89 *                  | net |   | sys |     | com |            |     1     |
90 *                  +-----+   +-----+     +-----+            +===========+
91 */
92
93typedef volatile uint32_t prop_off_t;
94struct prop_bt {
95    uint8_t namelen;
96    uint8_t reserved[3];
97
98    prop_off_t prop;
99
100    prop_off_t left;
101    prop_off_t right;
102
103    prop_off_t children;
104
105    char name[0];
106};
107
108typedef struct prop_bt prop_bt;
109
110static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
111static char property_filename[PATH_MAX] = PROP_FILENAME;
112static bool compat_mode = false;
113
114prop_area *__system_property_area__ = NULL;
115
116size_t pa_data_size;
117size_t pa_size;
118
119static int get_fd_from_env(void)
120{
121    char *env = getenv("ANDROID_PROPERTY_WORKSPACE");
122
123    if (!env) {
124        return -1;
125    }
126
127    return atoi(env);
128}
129
130static int map_prop_area_rw()
131{
132    prop_area *pa;
133    int fd;
134    int ret;
135
136    /* dev is a tmpfs that we can use to carve a shared workspace
137     * out of, so let's do that...
138     */
139    fd = open(property_filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC |
140            O_EXCL, 0444);
141    if (fd < 0) {
142        if (errno == EACCES) {
143            /* for consistency with the case where the process has already
144             * mapped the page in and segfaults when trying to write to it
145             */
146            abort();
147        }
148        return -1;
149    }
150
151    ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
152    if (ret < 0)
153        goto out;
154
155    if (ftruncate(fd, PA_SIZE) < 0)
156        goto out;
157
158    pa_size = PA_SIZE;
159    pa_data_size = pa_size - sizeof(prop_area);
160    compat_mode = false;
161
162    pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
163    if(pa == MAP_FAILED)
164        goto out;
165
166    memset(pa, 0, pa_size);
167    pa->magic = PROP_AREA_MAGIC;
168    pa->version = PROP_AREA_VERSION;
169    /* reserve root node */
170    pa->bytes_used = sizeof(prop_bt);
171
172    /* plug into the lib property services */
173    __system_property_area__ = pa;
174
175    close(fd);
176    return 0;
177
178out:
179    close(fd);
180    return -1;
181}
182
183int __system_property_set_filename(const char *filename)
184{
185    size_t len = strlen(filename);
186    if (len >= sizeof(property_filename))
187        return -1;
188
189    strcpy(property_filename, filename);
190    return 0;
191}
192
193int __system_property_area_init()
194{
195    return map_prop_area_rw();
196}
197
198static int map_prop_area()
199{
200    bool fromFile = true;
201    int result = -1;
202    int fd;
203    int ret;
204
205    fd = open(property_filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
206    if (fd >= 0) {
207        /* For old kernels that don't support O_CLOEXEC */
208        ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
209        if (ret < 0)
210            goto cleanup;
211    }
212
213    if ((fd < 0) && (errno == ENOENT)) {
214        /*
215         * For backwards compatibility, if the file doesn't
216         * exist, we use the environment to get the file descriptor.
217         * For security reasons, we only use this backup if the kernel
218         * returns ENOENT. We don't want to use the backup if the kernel
219         * returns other errors such as ENOMEM or ENFILE, since it
220         * might be possible for an external program to trigger this
221         * condition.
222         */
223        fd = get_fd_from_env();
224        fromFile = false;
225    }
226
227    if (fd < 0) {
228        return -1;
229    }
230
231    struct stat fd_stat;
232    if (fstat(fd, &fd_stat) < 0) {
233        goto cleanup;
234    }
235
236    if ((fd_stat.st_uid != 0)
237            || (fd_stat.st_gid != 0)
238            || ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0)
239            || (fd_stat.st_size < sizeof(prop_area)) ) {
240        goto cleanup;
241    }
242
243    pa_size = fd_stat.st_size;
244    pa_data_size = pa_size - sizeof(prop_area);
245    prop_area *pa = mmap(NULL, pa_size, PROT_READ, MAP_SHARED, fd, 0);
246
247    if (pa == MAP_FAILED) {
248        goto cleanup;
249    }
250
251    if((pa->magic != PROP_AREA_MAGIC) || (pa->version != PROP_AREA_VERSION &&
252                pa->version != PROP_AREA_VERSION_COMPAT)) {
253        munmap(pa, pa_size);
254        goto cleanup;
255    }
256
257    if (pa->version == PROP_AREA_VERSION_COMPAT) {
258        compat_mode = true;
259    }
260
261    result = 0;
262
263    __system_property_area__ = pa;
264
265cleanup:
266    if (fromFile) {
267        close(fd);
268    }
269
270    return result;
271}
272
273int __system_properties_init()
274{
275    return map_prop_area();
276}
277
278static void *new_prop_obj(size_t size, prop_off_t *off)
279{
280    prop_area *pa = __system_property_area__;
281    size = ALIGN(size, sizeof(uint32_t));
282
283    if (pa->bytes_used + size > pa_data_size)
284        return NULL;
285
286    *off = pa->bytes_used;
287    __system_property_area__->bytes_used += size;
288    return __system_property_area__->data + *off;
289}
290
291static prop_bt *new_prop_bt(const char *name, uint8_t namelen, prop_off_t *off)
292{
293    prop_off_t off_tmp;
294    prop_bt *bt = new_prop_obj(sizeof(prop_bt) + namelen + 1, &off_tmp);
295    if (bt) {
296        memcpy(bt->name, name, namelen);
297        bt->name[namelen] = '\0';
298        bt->namelen = namelen;
299        ANDROID_MEMBAR_FULL();
300        *off = off_tmp;
301    }
302
303    return bt;
304}
305
306static prop_info *new_prop_info(const char *name, uint8_t namelen,
307        const char *value, uint8_t valuelen, prop_off_t *off)
308{
309    prop_off_t off_tmp;
310    prop_info *info = new_prop_obj(sizeof(prop_info) + namelen + 1, &off_tmp);
311    if (info) {
312        memcpy(info->name, name, namelen);
313        info->name[namelen] = '\0';
314        info->serial = (valuelen << 24);
315        memcpy(info->value, value, valuelen);
316        info->value[valuelen] = '\0';
317        ANDROID_MEMBAR_FULL();
318        *off = off_tmp;
319    }
320
321    return info;
322}
323
324static void *to_prop_obj(prop_off_t off)
325{
326    if (off > pa_data_size)
327        return NULL;
328
329    return __system_property_area__->data + off;
330}
331
332static prop_bt *root_node()
333{
334    return to_prop_obj(0);
335}
336
337static int cmp_prop_name(const char *one, uint8_t one_len, const char *two,
338        uint8_t two_len)
339{
340    if (one_len < two_len)
341        return -1;
342    else if (one_len > two_len)
343        return 1;
344    else
345        return strncmp(one, two, one_len);
346}
347
348static prop_bt *find_prop_bt(prop_bt *bt, const char *name, uint8_t namelen,
349        bool alloc_if_needed)
350{
351    while (true) {
352        int ret;
353        if (!bt)
354            return bt;
355        ret = cmp_prop_name(name, namelen, bt->name, bt->namelen);
356
357        if (ret == 0) {
358            return bt;
359        } else if (ret < 0) {
360            if (bt->left) {
361                bt = to_prop_obj(bt->left);
362            } else {
363                if (!alloc_if_needed)
364                   return NULL;
365
366                bt = new_prop_bt(name, namelen, &bt->left);
367            }
368        } else {
369            if (bt->right) {
370                bt = to_prop_obj(bt->right);
371            } else {
372                if (!alloc_if_needed)
373                   return NULL;
374
375                bt = new_prop_bt(name, namelen, &bt->right);
376            }
377        }
378    }
379}
380
381static const prop_info *find_property(prop_bt *trie, const char *name,
382        uint8_t namelen, const char *value, uint8_t valuelen,
383        bool alloc_if_needed)
384{
385    const char *remaining_name = name;
386
387    while (true) {
388        char *sep = strchr(remaining_name, '.');
389        bool want_subtree = (sep != NULL);
390        uint8_t substr_size;
391
392        prop_bt *root;
393
394        if (want_subtree) {
395            substr_size = sep - remaining_name;
396        } else {
397            substr_size = strlen(remaining_name);
398        }
399
400        if (!substr_size)
401            return NULL;
402
403        if (trie->children) {
404            root = to_prop_obj(trie->children);
405        } else if (alloc_if_needed) {
406            root = new_prop_bt(remaining_name, substr_size, &trie->children);
407        } else {
408            root = NULL;
409        }
410
411        if (!root)
412            return NULL;
413
414        trie = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed);
415        if (!trie)
416            return NULL;
417
418        if (!want_subtree)
419            break;
420
421        remaining_name = sep + 1;
422    }
423
424    if (trie->prop) {
425        return to_prop_obj(trie->prop);
426    } else if (alloc_if_needed) {
427        return new_prop_info(name, namelen, value, valuelen, &trie->prop);
428    } else {
429        return NULL;
430    }
431}
432
433const prop_info *__system_property_find(const char *name)
434{
435    if (__predict_false(compat_mode)) {
436        return __system_property_find_compat(name);
437    }
438    return find_property(root_node(), name, strlen(name), NULL, 0, false);
439}
440
441int __system_property_read(const prop_info *pi, char *name, char *value)
442{
443    unsigned serial, len;
444
445    if (__predict_false(compat_mode)) {
446        return __system_property_read_compat(pi, name, value);
447    }
448
449    for(;;) {
450        serial = pi->serial;
451        while(SERIAL_DIRTY(serial)) {
452            __futex_wait((volatile void *)&pi->serial, serial, 0);
453            serial = pi->serial;
454        }
455        len = SERIAL_VALUE_LEN(serial);
456        memcpy(value, pi->value, len + 1);
457        ANDROID_MEMBAR_FULL();
458        if(serial == pi->serial) {
459            if(name != 0) {
460                strcpy(name, pi->name);
461            }
462            return len;
463        }
464    }
465}
466
467int __system_property_get(const char *name, char *value)
468{
469    const prop_info *pi = __system_property_find(name);
470
471    if(pi != 0) {
472        return __system_property_read(pi, 0, value);
473    } else {
474        value[0] = 0;
475        return 0;
476    }
477}
478
479
480static int send_prop_msg(prop_msg *msg)
481{
482    struct pollfd pollfds[1];
483    struct sockaddr_un addr;
484    socklen_t alen;
485    size_t namelen;
486    int s;
487    int r;
488    int result = -1;
489
490    s = socket(AF_LOCAL, SOCK_STREAM, 0);
491    if(s < 0) {
492        return result;
493    }
494
495    memset(&addr, 0, sizeof(addr));
496    namelen = strlen(property_service_socket);
497    strlcpy(addr.sun_path, property_service_socket, sizeof addr.sun_path);
498    addr.sun_family = AF_LOCAL;
499    alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
500
501    if(TEMP_FAILURE_RETRY(connect(s, (struct sockaddr *) &addr, alen)) < 0) {
502        close(s);
503        return result;
504    }
505
506    r = TEMP_FAILURE_RETRY(send(s, msg, sizeof(prop_msg), 0));
507
508    if(r == sizeof(prop_msg)) {
509        // We successfully wrote to the property server but now we
510        // wait for the property server to finish its work.  It
511        // acknowledges its completion by closing the socket so we
512        // poll here (on nothing), waiting for the socket to close.
513        // If you 'adb shell setprop foo bar' you'll see the POLLHUP
514        // once the socket closes.  Out of paranoia we cap our poll
515        // at 250 ms.
516        pollfds[0].fd = s;
517        pollfds[0].events = 0;
518        r = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */));
519        if (r == 1 && (pollfds[0].revents & POLLHUP) != 0) {
520            result = 0;
521        } else {
522            // Ignore the timeout and treat it like a success anyway.
523            // The init process is single-threaded and its property
524            // service is sometimes slow to respond (perhaps it's off
525            // starting a child process or something) and thus this
526            // times out and the caller thinks it failed, even though
527            // it's still getting around to it.  So we fake it here,
528            // mostly for ctl.* properties, but we do try and wait 250
529            // ms so callers who do read-after-write can reliably see
530            // what they've written.  Most of the time.
531            // TODO: fix the system properties design.
532            result = 0;
533        }
534    }
535
536    close(s);
537    return result;
538}
539
540int __system_property_set(const char *key, const char *value)
541{
542    int err;
543    prop_msg msg;
544
545    if(key == 0) return -1;
546    if(value == 0) value = "";
547    if(strlen(key) >= PROP_NAME_MAX) return -1;
548    if(strlen(value) >= PROP_VALUE_MAX) return -1;
549
550    memset(&msg, 0, sizeof msg);
551    msg.cmd = PROP_MSG_SETPROP;
552    strlcpy(msg.name, key, sizeof msg.name);
553    strlcpy(msg.value, value, sizeof msg.value);
554
555    err = send_prop_msg(&msg);
556    if(err < 0) {
557        return err;
558    }
559
560    return 0;
561}
562
563int __system_property_wait(const prop_info *pi)
564{
565    unsigned n;
566    if(pi == 0) {
567        prop_area *pa = __system_property_area__;
568        n = pa->serial;
569        do {
570            __futex_wait(&pa->serial, n, 0);
571        } while(n == pa->serial);
572    } else {
573        n = pi->serial;
574        do {
575            __futex_wait((volatile void *)&pi->serial, n, 0);
576        } while(n == pi->serial);
577    }
578    return 0;
579}
580
581int __system_property_update(prop_info *pi, const char *value, unsigned int len)
582{
583    prop_area *pa = __system_property_area__;
584
585    if (len >= PROP_VALUE_MAX)
586        return -1;
587
588    pi->serial = pi->serial | 1;
589    ANDROID_MEMBAR_FULL();
590    memcpy(pi->value, value, len + 1);
591    ANDROID_MEMBAR_FULL();
592    pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff);
593    __futex_wake(&pi->serial, INT32_MAX);
594
595    pa->serial++;
596    __futex_wake(&pa->serial, INT32_MAX);
597
598    return 0;
599}
600
601int __system_property_add(const char *name, unsigned int namelen,
602            const char *value, unsigned int valuelen)
603{
604    prop_area *pa = __system_property_area__;
605    const prop_info *pi;
606
607    if (namelen >= PROP_NAME_MAX)
608        return -1;
609    if (valuelen >= PROP_VALUE_MAX)
610        return -1;
611    if (namelen < 1)
612        return -1;
613
614    pi = find_property(root_node(), name, namelen, value, valuelen, true);
615    if (!pi)
616        return -1;
617
618    pa->serial++;
619    __futex_wake(&pa->serial, INT32_MAX);
620    return 0;
621}
622
623unsigned int __system_property_serial(const prop_info *pi)
624{
625    return pi->serial;
626}
627
628unsigned int __system_property_wait_any(unsigned int serial)
629{
630    prop_area *pa = __system_property_area__;
631
632    do {
633        __futex_wait(&pa->serial, serial, 0);
634    } while(pa->serial == serial);
635
636    return pa->serial;
637}
638
639struct find_nth_cookie {
640    unsigned count;
641    unsigned n;
642    const prop_info *pi;
643};
644
645static void find_nth_fn(const prop_info *pi, void *ptr)
646{
647    struct find_nth_cookie *cookie = ptr;
648
649    if (cookie->n == cookie->count)
650        cookie->pi = pi;
651
652    cookie->count++;
653}
654
655const prop_info *__system_property_find_nth(unsigned n)
656{
657    struct find_nth_cookie cookie;
658    int err;
659
660    memset(&cookie, 0, sizeof(cookie));
661    cookie.n = n;
662
663    err = __system_property_foreach(find_nth_fn, &cookie);
664    if (err < 0)
665        return NULL;
666
667    return cookie.pi;
668}
669
670static int foreach_property(prop_off_t off,
671        void (*propfn)(const prop_info *pi, void *cookie), void *cookie)
672{
673    prop_bt *trie = to_prop_obj(off);
674    if (!trie)
675        return -1;
676
677    if (trie->left) {
678        int err = foreach_property(trie->left, propfn, cookie);
679        if (err < 0)
680            return -1;
681    }
682    if (trie->prop) {
683        prop_info *info = to_prop_obj(trie->prop);
684        if (!info)
685            return -1;
686        propfn(info, cookie);
687    }
688    if (trie->children) {
689        int err = foreach_property(trie->children, propfn, cookie);
690        if (err < 0)
691            return -1;
692    }
693    if (trie->right) {
694        int err = foreach_property(trie->right, propfn, cookie);
695        if (err < 0)
696            return -1;
697    }
698
699    return 0;
700}
701
702int __system_property_foreach(void (*propfn)(const prop_info *pi, void *cookie),
703        void *cookie)
704{
705    if (__predict_false(compat_mode)) {
706        return __system_property_foreach_compat(propfn, cookie);
707	}
708    return foreach_property(0, propfn, cookie);
709}
710