Home | History | Annotate | Download | only in init
      1 /*
      2  * Copyright (C) 2007 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 #include <inttypes.h>
     18 #include <stdio.h>
     19 #include <stdlib.h>
     20 #include <unistd.h>
     21 #include <string.h>
     22 #include <ctype.h>
     23 #include <fcntl.h>
     24 #include <stdarg.h>
     25 #include <dirent.h>
     26 #include <limits.h>
     27 #include <errno.h>
     28 #include <sys/poll.h>
     29 
     30 #include <memory>
     31 #include <vector>
     32 
     33 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
     34 #include <sys/_system_properties.h>
     35 
     36 #include <sys/socket.h>
     37 #include <sys/un.h>
     38 #include <sys/select.h>
     39 #include <sys/types.h>
     40 #include <netinet/in.h>
     41 #include <sys/mman.h>
     42 
     43 #include <selinux/android.h>
     44 #include <selinux/selinux.h>
     45 #include <selinux/label.h>
     46 
     47 #include <android-base/file.h>
     48 #include <android-base/properties.h>
     49 #include <android-base/stringprintf.h>
     50 #include <android-base/strings.h>
     51 #include <fs_mgr.h>
     52 #include "bootimg.h"
     53 
     54 #include "property_service.h"
     55 #include "init.h"
     56 #include "util.h"
     57 #include "log.h"
     58 
     59 using android::base::StringPrintf;
     60 
     61 #define PERSISTENT_PROPERTY_DIR  "/data/property"
     62 #define RECOVERY_MOUNT_POINT "/recovery"
     63 
     64 static int persistent_properties_loaded = 0;
     65 
     66 static int property_set_fd = -1;
     67 
     68 void property_init() {
     69     if (__system_property_area_init()) {
     70         LOG(ERROR) << "Failed to initialize property area";
     71         exit(1);
     72     }
     73 }
     74 
     75 static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* cr) {
     76 
     77     if (!sctx) {
     78       return false;
     79     }
     80 
     81     if (!sehandle_prop) {
     82       return false;
     83     }
     84 
     85     char* tctx = nullptr;
     86     if (selabel_lookup(sehandle_prop, &tctx, name.c_str(), 1) != 0) {
     87       return false;
     88     }
     89 
     90     property_audit_data audit_data;
     91 
     92     audit_data.name = name.c_str();
     93     audit_data.cr = cr;
     94 
     95     bool has_access = (selinux_check_access(sctx, tctx, "property_service", "set", &audit_data) == 0);
     96 
     97     freecon(tctx);
     98     return has_access;
     99 }
    100 
    101 static int check_control_mac_perms(const char *name, char *sctx, struct ucred *cr)
    102 {
    103     /*
    104      *  Create a name prefix out of ctl.<service name>
    105      *  The new prefix allows the use of the existing
    106      *  property service backend labeling while avoiding
    107      *  mislabels based on true property prefixes.
    108      */
    109     char ctl_name[PROP_VALUE_MAX+4];
    110     int ret = snprintf(ctl_name, sizeof(ctl_name), "ctl.%s", name);
    111 
    112     if (ret < 0 || (size_t) ret >= sizeof(ctl_name))
    113         return 0;
    114 
    115     return check_mac_perms(ctl_name, sctx, cr);
    116 }
    117 
    118 static void write_persistent_property(const char *name, const char *value)
    119 {
    120     char tempPath[PATH_MAX];
    121     char path[PATH_MAX];
    122     int fd;
    123 
    124     snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR);
    125     fd = mkstemp(tempPath);
    126     if (fd < 0) {
    127         PLOG(ERROR) << "Unable to write persistent property to temp file " << tempPath;
    128         return;
    129     }
    130     write(fd, value, strlen(value));
    131     fsync(fd);
    132     close(fd);
    133 
    134     snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
    135     if (rename(tempPath, path)) {
    136         PLOG(ERROR) << "Unable to rename persistent property file " << tempPath << " to " << path;
    137         unlink(tempPath);
    138     }
    139 }
    140 
    141 bool is_legal_property_name(const std::string& name) {
    142     size_t namelen = name.size();
    143 
    144     if (namelen < 1) return false;
    145     if (name[0] == '.') return false;
    146     if (name[namelen - 1] == '.') return false;
    147 
    148     /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
    149     /* Don't allow ".." to appear in a property name */
    150     for (size_t i = 0; i < namelen; i++) {
    151         if (name[i] == '.') {
    152             // i=0 is guaranteed to never have a dot. See above.
    153             if (name[i-1] == '.') return false;
    154             continue;
    155         }
    156         if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
    157         if (name[i] >= 'a' && name[i] <= 'z') continue;
    158         if (name[i] >= 'A' && name[i] <= 'Z') continue;
    159         if (name[i] >= '0' && name[i] <= '9') continue;
    160         return false;
    161     }
    162 
    163     return true;
    164 }
    165 
    166 uint32_t property_set(const std::string& name, const std::string& value) {
    167     size_t valuelen = value.size();
    168 
    169     if (!is_legal_property_name(name)) {
    170         LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
    171         return PROP_ERROR_INVALID_NAME;
    172     }
    173 
    174     if (valuelen >= PROP_VALUE_MAX) {
    175         LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
    176                    << "value too long";
    177         return PROP_ERROR_INVALID_VALUE;
    178     }
    179 
    180     if (name == "selinux.restorecon_recursive" && valuelen > 0) {
    181         if (restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
    182             LOG(ERROR) << "Failed to restorecon_recursive " << value;
    183         }
    184     }
    185 
    186     prop_info* pi = (prop_info*) __system_property_find(name.c_str());
    187     if (pi != nullptr) {
    188         // ro.* properties are actually "write-once".
    189         if (android::base::StartsWith(name, "ro.")) {
    190             LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
    191                        << "property already set";
    192             return PROP_ERROR_READ_ONLY_PROPERTY;
    193         }
    194 
    195         __system_property_update(pi, value.c_str(), valuelen);
    196     } else {
    197         int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
    198         if (rc < 0) {
    199             LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
    200                        << "__system_property_add failed";
    201             return PROP_ERROR_SET_FAILED;
    202         }
    203     }
    204 
    205     // Don't write properties to disk until after we have read all default
    206     // properties to prevent them from being overwritten by default values.
    207     if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
    208         write_persistent_property(name.c_str(), value.c_str());
    209     }
    210     property_changed(name, value);
    211     return PROP_SUCCESS;
    212 }
    213 
    214 class SocketConnection {
    215  public:
    216   SocketConnection(int socket, const struct ucred& cred)
    217       : socket_(socket), cred_(cred) {}
    218 
    219   ~SocketConnection() {
    220     close(socket_);
    221   }
    222 
    223   bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) {
    224     return RecvFully(value, sizeof(*value), timeout_ms);
    225   }
    226 
    227   bool RecvChars(char* chars, size_t size, uint32_t* timeout_ms) {
    228     return RecvFully(chars, size, timeout_ms);
    229   }
    230 
    231   bool RecvString(std::string* value, uint32_t* timeout_ms) {
    232     uint32_t len = 0;
    233     if (!RecvUint32(&len, timeout_ms)) {
    234       return false;
    235     }
    236 
    237     if (len == 0) {
    238       *value = "";
    239       return true;
    240     }
    241 
    242     // http://b/35166374: don't allow init to make arbitrarily large allocations.
    243     if (len > 0xffff) {
    244       LOG(ERROR) << "sys_prop: RecvString asked to read huge string: " << len;
    245       errno = ENOMEM;
    246       return false;
    247     }
    248 
    249     std::vector<char> chars(len);
    250     if (!RecvChars(&chars[0], len, timeout_ms)) {
    251       return false;
    252     }
    253 
    254     *value = std::string(&chars[0], len);
    255     return true;
    256   }
    257 
    258   bool SendUint32(uint32_t value) {
    259     int result = TEMP_FAILURE_RETRY(send(socket_, &value, sizeof(value), 0));
    260     return result == sizeof(value);
    261   }
    262 
    263   int socket() {
    264     return socket_;
    265   }
    266 
    267   const struct ucred& cred() {
    268     return cred_;
    269   }
    270 
    271  private:
    272   bool PollIn(uint32_t* timeout_ms) {
    273     struct pollfd ufds[1];
    274     ufds[0].fd = socket_;
    275     ufds[0].events = POLLIN;
    276     ufds[0].revents = 0;
    277     while (*timeout_ms > 0) {
    278       Timer timer;
    279       int nr = poll(ufds, 1, *timeout_ms);
    280       uint64_t millis = timer.duration_ms();
    281       *timeout_ms = (millis > *timeout_ms) ? 0 : *timeout_ms - millis;
    282 
    283       if (nr > 0) {
    284         return true;
    285       }
    286 
    287       if (nr == 0) {
    288         // Timeout
    289         break;
    290       }
    291 
    292       if (nr < 0 && errno != EINTR) {
    293         PLOG(ERROR) << "sys_prop: error waiting for uid " << cred_.uid << " to send property message";
    294         return false;
    295       } else { // errno == EINTR
    296         // Timer rounds milliseconds down in case of EINTR we want it to be rounded up
    297         // to avoid slowing init down by causing EINTR with under millisecond timeout.
    298         if (*timeout_ms > 0) {
    299           --(*timeout_ms);
    300         }
    301       }
    302     }
    303 
    304     LOG(ERROR) << "sys_prop: timeout waiting for uid " << cred_.uid << " to send property message.";
    305     return false;
    306   }
    307 
    308   bool RecvFully(void* data_ptr, size_t size, uint32_t* timeout_ms) {
    309     size_t bytes_left = size;
    310     char* data = static_cast<char*>(data_ptr);
    311     while (*timeout_ms > 0 && bytes_left > 0) {
    312       if (!PollIn(timeout_ms)) {
    313         return false;
    314       }
    315 
    316       int result = TEMP_FAILURE_RETRY(recv(socket_, data, bytes_left, MSG_DONTWAIT));
    317       if (result <= 0) {
    318         return false;
    319       }
    320 
    321       bytes_left -= result;
    322       data += result;
    323     }
    324 
    325     return bytes_left == 0;
    326   }
    327 
    328   int socket_;
    329   struct ucred cred_;
    330 
    331   DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
    332 };
    333 
    334 static void handle_property_set(SocketConnection& socket,
    335                                 const std::string& name,
    336                                 const std::string& value,
    337                                 bool legacy_protocol) {
    338   const char* cmd_name = legacy_protocol ? "PROP_MSG_SETPROP" : "PROP_MSG_SETPROP2";
    339   if (!is_legal_property_name(name)) {
    340     LOG(ERROR) << "sys_prop(" << cmd_name << "): illegal property name \"" << name << "\"";
    341     socket.SendUint32(PROP_ERROR_INVALID_NAME);
    342     return;
    343   }
    344 
    345   struct ucred cr = socket.cred();
    346   char* source_ctx = nullptr;
    347   getpeercon(socket.socket(), &source_ctx);
    348 
    349   if (android::base::StartsWith(name, "ctl.")) {
    350     if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
    351       handle_control_message(name.c_str() + 4, value.c_str());
    352       if (!legacy_protocol) {
    353         socket.SendUint32(PROP_SUCCESS);
    354       }
    355     } else {
    356       LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
    357                  << " service ctl [" << value << "]"
    358                  << " uid:" << cr.uid
    359                  << " gid:" << cr.gid
    360                  << " pid:" << cr.pid;
    361       if (!legacy_protocol) {
    362         socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
    363       }
    364     }
    365   } else {
    366     if (check_mac_perms(name, source_ctx, &cr)) {
    367       uint32_t result = property_set(name, value);
    368       if (!legacy_protocol) {
    369         socket.SendUint32(result);
    370       }
    371     } else {
    372       LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;
    373       if (!legacy_protocol) {
    374         socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
    375       }
    376     }
    377   }
    378 
    379   freecon(source_ctx);
    380 }
    381 
    382 static void handle_property_set_fd() {
    383     static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
    384 
    385     int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
    386     if (s == -1) {
    387         return;
    388     }
    389 
    390     struct ucred cr;
    391     socklen_t cr_size = sizeof(cr);
    392     if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
    393         close(s);
    394         PLOG(ERROR) << "sys_prop: unable to get SO_PEERCRED";
    395         return;
    396     }
    397 
    398     SocketConnection socket(s, cr);
    399     uint32_t timeout_ms = kDefaultSocketTimeout;
    400 
    401     uint32_t cmd = 0;
    402     if (!socket.RecvUint32(&cmd, &timeout_ms)) {
    403         PLOG(ERROR) << "sys_prop: error while reading command from the socket";
    404         socket.SendUint32(PROP_ERROR_READ_CMD);
    405         return;
    406     }
    407 
    408     switch (cmd) {
    409     case PROP_MSG_SETPROP: {
    410         char prop_name[PROP_NAME_MAX];
    411         char prop_value[PROP_VALUE_MAX];
    412 
    413         if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
    414             !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
    415           PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
    416           return;
    417         }
    418 
    419         prop_name[PROP_NAME_MAX-1] = 0;
    420         prop_value[PROP_VALUE_MAX-1] = 0;
    421 
    422         handle_property_set(socket, prop_value, prop_value, true);
    423         break;
    424       }
    425 
    426     case PROP_MSG_SETPROP2: {
    427         std::string name;
    428         std::string value;
    429         if (!socket.RecvString(&name, &timeout_ms) ||
    430             !socket.RecvString(&value, &timeout_ms)) {
    431           PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket";
    432           socket.SendUint32(PROP_ERROR_READ_DATA);
    433           return;
    434         }
    435 
    436         handle_property_set(socket, name, value, false);
    437         break;
    438       }
    439 
    440     default:
    441         LOG(ERROR) << "sys_prop: invalid command " << cmd;
    442         socket.SendUint32(PROP_ERROR_INVALID_CMD);
    443         break;
    444     }
    445 }
    446 
    447 static bool load_properties_from_file(const char *, const char *);
    448 
    449 /*
    450  * Filter is used to decide which properties to load: NULL loads all keys,
    451  * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
    452  */
    453 static void load_properties(char *data, const char *filter)
    454 {
    455     char *key, *value, *eol, *sol, *tmp, *fn;
    456     size_t flen = 0;
    457 
    458     if (filter) {
    459         flen = strlen(filter);
    460     }
    461 
    462     sol = data;
    463     while ((eol = strchr(sol, '\n'))) {
    464         key = sol;
    465         *eol++ = 0;
    466         sol = eol;
    467 
    468         while (isspace(*key)) key++;
    469         if (*key == '#') continue;
    470 
    471         tmp = eol - 2;
    472         while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
    473 
    474         if (!strncmp(key, "import ", 7) && flen == 0) {
    475             fn = key + 7;
    476             while (isspace(*fn)) fn++;
    477 
    478             key = strchr(fn, ' ');
    479             if (key) {
    480                 *key++ = 0;
    481                 while (isspace(*key)) key++;
    482             }
    483 
    484             load_properties_from_file(fn, key);
    485 
    486         } else {
    487             value = strchr(key, '=');
    488             if (!value) continue;
    489             *value++ = 0;
    490 
    491             tmp = value - 2;
    492             while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
    493 
    494             while (isspace(*value)) value++;
    495 
    496             if (flen > 0) {
    497                 if (filter[flen - 1] == '*') {
    498                     if (strncmp(key, filter, flen - 1)) continue;
    499                 } else {
    500                     if (strcmp(key, filter)) continue;
    501                 }
    502             }
    503 
    504             property_set(key, value);
    505         }
    506     }
    507 }
    508 
    509 // Filter is used to decide which properties to load: NULL loads all keys,
    510 // "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
    511 static bool load_properties_from_file(const char* filename, const char* filter) {
    512     Timer t;
    513     std::string data;
    514     if (!read_file(filename, &data)) {
    515         PLOG(WARNING) << "Couldn't load properties from " << filename;
    516         return false;
    517     }
    518     data.push_back('\n');
    519     load_properties(&data[0], filter);
    520     LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
    521     return true;
    522 }
    523 
    524 static void load_persistent_properties() {
    525     persistent_properties_loaded = 1;
    526 
    527     std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(PERSISTENT_PROPERTY_DIR), closedir);
    528     if (!dir) {
    529         PLOG(ERROR) << "Unable to open persistent property directory \""
    530                     << PERSISTENT_PROPERTY_DIR << "\"";
    531         return;
    532     }
    533 
    534     struct dirent* entry;
    535     while ((entry = readdir(dir.get())) != NULL) {
    536         if (strncmp("persist.", entry->d_name, strlen("persist."))) {
    537             continue;
    538         }
    539         if (entry->d_type != DT_REG) {
    540             continue;
    541         }
    542 
    543         // Open the file and read the property value.
    544         int fd = openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW);
    545         if (fd == -1) {
    546             PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
    547             continue;
    548         }
    549 
    550         struct stat sb;
    551         if (fstat(fd, &sb) == -1) {
    552             PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
    553             close(fd);
    554             continue;
    555         }
    556 
    557         // File must not be accessible to others, be owned by root/root, and
    558         // not be a hard link to any other file.
    559         if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 || sb.st_nlink != 1) {
    560             PLOG(ERROR) << "skipping insecure property file " << entry->d_name
    561                         << " (uid=" << sb.st_uid << " gid=" << sb.st_gid
    562                         << " nlink=" << sb.st_nlink << " mode=" << std::oct << sb.st_mode << ")";
    563             close(fd);
    564             continue;
    565         }
    566 
    567         char value[PROP_VALUE_MAX];
    568         int length = read(fd, value, sizeof(value) - 1);
    569         if (length >= 0) {
    570             value[length] = 0;
    571             property_set(entry->d_name, value);
    572         } else {
    573             PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
    574         }
    575         close(fd);
    576     }
    577 }
    578 
    579 // persist.sys.usb.config values can't be combined on build-time when property
    580 // files are split into each partition.
    581 // So we need to apply the same rule of build/make/tools/post_process_props.py
    582 // on runtime.
    583 static void update_sys_usb_config() {
    584     bool is_debuggable = android::base::GetBoolProperty("ro.debuggable", false);
    585     std::string config = android::base::GetProperty("persist.sys.usb.config", "");
    586     if (config.empty()) {
    587         property_set("persist.sys.usb.config", is_debuggable ? "adb" : "none");
    588     } else if (is_debuggable && config.find("adb") == std::string::npos &&
    589                config.length() + 4 < PROP_VALUE_MAX) {
    590         config.append(",adb");
    591         property_set("persist.sys.usb.config", config);
    592     }
    593 }
    594 
    595 void property_load_boot_defaults() {
    596     if (!load_properties_from_file("/system/etc/prop.default", NULL)) {
    597         // Try recovery path
    598         if (!load_properties_from_file("/prop.default", NULL)) {
    599             // Try legacy path
    600             load_properties_from_file("/default.prop", NULL);
    601         }
    602     }
    603     load_properties_from_file("/odm/default.prop", NULL);
    604     load_properties_from_file("/vendor/default.prop", NULL);
    605 
    606     update_sys_usb_config();
    607 }
    608 
    609 static void load_override_properties() {
    610     if (ALLOW_LOCAL_PROP_OVERRIDE) {
    611         load_properties_from_file("/data/local.prop", NULL);
    612     }
    613 }
    614 
    615 /* When booting an encrypted system, /data is not mounted when the
    616  * property service is started, so any properties stored there are
    617  * not loaded.  Vold triggers init to load these properties once it
    618  * has mounted /data.
    619  */
    620 void load_persist_props(void) {
    621     load_override_properties();
    622     /* Read persistent properties after all default values have been loaded. */
    623     load_persistent_properties();
    624     property_set("ro.persistent_properties.ready", "true");
    625 }
    626 
    627 void load_recovery_id_prop() {
    628     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
    629                                                                fs_mgr_free_fstab);
    630     if (!fstab) {
    631         PLOG(ERROR) << "unable to read default fstab";
    632         return;
    633     }
    634 
    635     fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), RECOVERY_MOUNT_POINT);
    636     if (rec == NULL) {
    637         LOG(ERROR) << "/recovery not specified in fstab";
    638         return;
    639     }
    640 
    641     int fd = open(rec->blk_device, O_RDONLY);
    642     if (fd == -1) {
    643         PLOG(ERROR) << "error opening block device " << rec->blk_device;
    644         return;
    645     }
    646 
    647     boot_img_hdr hdr;
    648     if (android::base::ReadFully(fd, &hdr, sizeof(hdr))) {
    649         std::string hex = bytes_to_hex(reinterpret_cast<uint8_t*>(hdr.id), sizeof(hdr.id));
    650         property_set("ro.recovery_id", hex.c_str());
    651     } else {
    652         PLOG(ERROR) << "error reading /recovery";
    653     }
    654 
    655     close(fd);
    656 }
    657 
    658 void load_system_props() {
    659     load_properties_from_file("/system/build.prop", NULL);
    660     load_properties_from_file("/odm/build.prop", NULL);
    661     load_properties_from_file("/vendor/build.prop", NULL);
    662     load_properties_from_file("/factory/factory.prop", "ro.*");
    663     load_recovery_id_prop();
    664 }
    665 
    666 void start_property_service() {
    667     property_set("ro.property_service.version", "2");
    668 
    669     property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
    670                                     0666, 0, 0, NULL);
    671     if (property_set_fd == -1) {
    672         PLOG(ERROR) << "start_property_service socket creation failed";
    673         exit(1);
    674     }
    675 
    676     listen(property_set_fd, 8);
    677 
    678     register_epoll_handler(property_set_fd, handle_property_set_fd);
    679 }
    680