| /* SPDX-License-Identifier: MediaTekProprietary */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <ctype.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <dirent.h> //for read directories |
| #include <errno.h> |
| #include <cutils/properties.h> |
| #include "prop_file.h" |
| #include "prop_db.h" |
| #include "prop_debug.h" |
| #include "prop_core.h" |
| #include "prop_svc.h" |
| |
| typedef enum |
| { |
| svc_cmd_type_command=1, |
| svc_cmd_type_service, |
| svc_cmd_type_onproperty, |
| svc_cmd_type_others |
| |
| }prop_file_svc_cmd_t; |
| |
| /*for APSIM, must put in /mnt/sncfg/apsim/*/ |
| #if defined(PARTITION_FOR_APSIM) |
| #define PROP_CORE_PERSISTENT_PROPERTY_DIR "/mnt/sncfg/apsim/property" |
| #define PROP_CORE_PROPERTY_DIR "/mnt/sncfg/apsim/property" |
| #else |
| #define PROP_CORE_PERSISTENT_PROPERTY_DIR "/data/property" |
| #define PROP_CORE_PROPERTY_DIR "/data/property" |
| #endif |
| |
| #define PROP_CORE_SERVICE_DIR "/" |
| #define PROP_CORE_PROP_DEFAULT "/default.prop" |
| #define PROP_CORE_PROP_SYSTEM_BUILD "/system/build.prop" |
| #define PROP_CORE_PROP_VENDOR_BUILD "/vendor/build.prop" |
| #define PROP_CORE_PROP_LOCAL "/data/local.prop" |
| #define PROP_CORE_PROP_FACTORY "/factory/factory.prop" |
| |
| static int prop_file_persist_load_done = 0; |
| static int prop_file_persist_bootup_sync = 0; |
| |
| static void _prop_file_trim_line(char *line) |
| { |
| int len; |
| int index; |
| |
| if (!line) |
| return; |
| |
| len = strlen(line); |
| if (len <= 0) |
| return; |
| |
| for (index=len-1; index >= 0; index-- ) { |
| if ((line[index] == ' ') || |
| (line[index] == '\n')) { |
| line[index] = '\0'; |
| } else { |
| break; |
| } |
| } |
| |
| return; |
| } |
| |
| /* |
| * Filter is used to decide which properties to load: NULL loads all keys, |
| * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match. |
| */ |
| static void _prop_file_load_properties(char *data, const char *filter) |
| { |
| char *key, *value, *eol, *sol, *tmp; |
| size_t flen = 0; |
| |
| if (filter) { |
| flen = strlen(filter); |
| } |
| |
| sol = data; |
| while ((eol = strchr(sol, '\n'))) |
| { |
| key = sol; |
| *eol++ = 0; |
| sol = eol; |
| |
| while (isspace(*key)) key++; |
| if (*key == '#') |
| continue; |
| |
| tmp = eol - 2; |
| while ((tmp > key) && isspace(*tmp)) *tmp-- = 0; |
| |
| { |
| value = strchr(key, '='); |
| if (!value) continue; |
| *value++ = 0; |
| |
| tmp = value - 2; |
| while ((tmp > key) && isspace(*tmp)) *tmp-- = 0; |
| |
| while (isspace(*value)) value++; |
| |
| if (flen > 0) { |
| if (filter[flen - 1] == '*') { |
| if (strncmp(key, filter, flen - 1)) continue; |
| } else { |
| if (strcmp(key, filter)) continue; |
| } |
| } |
| |
| prop_core_set(key, value); |
| } |
| } |
| } |
| |
| static int _prop_file_parse_svc_service(const char *line_buf, char *prop_key, char *daemon_name) |
| { |
| char *key_ptr, *value_ptr, *temp_ptr; |
| int length_key, length_value; |
| |
| key_ptr = strstr(line_buf, PROP_SVC_SERVICE); |
| if (!key_ptr) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Not the service line...Line:%s ", line_buf); |
| return -1; |
| } |
| |
| if (strlen(key_ptr) < 8) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "service length wrong. len:%d Line:%s ", strlen(key_ptr), line_buf); |
| return -1; |
| } |
| |
| /*skip the : and space*/ |
| key_ptr = key_ptr + strlen(PROP_SVC_SERVICE); |
| while (*key_ptr == ' ') { |
| key_ptr++; |
| } |
| |
| temp_ptr = strstr(key_ptr, " "); |
| if (!temp_ptr) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Wrong line...key:%s line:%s ", key_ptr, line_buf); |
| return -1; |
| } |
| |
| length_key = (int)temp_ptr - (int)key_ptr; |
| if (length_key >= PROPERTY_KEY_MAX) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Wrong key len:%d line:%s ", length_key, line_buf); |
| return -1; |
| } |
| |
| strncpy(prop_key, key_ptr, length_key); |
| prop_key[length_key] = '\0'; |
| |
| value_ptr = temp_ptr; |
| while (*value_ptr == ' ') { |
| value_ptr++; |
| } |
| |
| length_value = strlen(value_ptr); |
| if (length_value>= PROP_SVC_ACTION_MAX_SIZE) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Wrong value len:%d line:%s ", length_value, line_buf); |
| return -1; |
| } |
| |
| strcpy(daemon_name, value_ptr); |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " line:%s key:%s value:%s ", line_buf, prop_key, daemon_name); |
| |
| return 0; |
| } |
| |
| static int _prop_file_parse_svc_onproperty(char *line_buf, char *prop_key, char *prop_value) |
| { |
| char *key_ptr, *value_ptr, *temp_ptr; |
| int length_key, length_value; |
| |
| key_ptr = strstr(line_buf, ":"); |
| if (!key_ptr) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Wrong line...Line:%s ", line_buf); |
| return -1; |
| } |
| |
| if (strlen(key_ptr) < 4) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Wrong line len:%d Line:%s ", strlen(key_ptr), line_buf); |
| return -1; |
| } |
| |
| /*skip the : and space*/ |
| key_ptr++; |
| while (*key_ptr == ' ') { |
| key_ptr++; |
| } |
| |
| temp_ptr = strstr(key_ptr, "="); |
| if (!temp_ptr) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Wrong line without '=' line:%s Line:%s ", key_ptr, line_buf); |
| return -1; |
| } |
| |
| while ((temp_ptr >= key_ptr) && *temp_ptr == ' ') { |
| temp_ptr--; |
| } |
| |
| if (temp_ptr <= key_ptr) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Wrong line...2...Line:%s ", line_buf); |
| return -1; |
| } |
| |
| length_key = (int)temp_ptr - (int)key_ptr; |
| |
| strncpy(prop_key, key_ptr, length_key); |
| prop_key[length_key] = '\0'; |
| value_ptr = strstr(temp_ptr, "="); |
| if (strlen(value_ptr) < 2) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Wrong line...3...value:%s ", value_ptr); |
| return -1; |
| } |
| |
| /*skip the = and space*/ |
| value_ptr++; |
| while (*value_ptr == ' ') { |
| value_ptr++; |
| } |
| |
| length_value = strlen(value_ptr); |
| if (length_value>= PROPERTY_VALUE_MAX) |
| length_value = PROPERTY_VALUE_MAX - 1; |
| |
| if ((length_value+length_key) >= PROPERTY_VALUE_MAX) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Wrong value or length. key_len:%d value_len:%d value:%s ", |
| length_key, length_value, value_ptr); |
| return -1; |
| } |
| |
| strncpy(prop_value, value_ptr, length_value); |
| prop_value[length_value] = '\0'; |
| |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " End. key:%s value:%s line_buf:%s ", prop_key, prop_value, value_ptr); |
| |
| return 0; |
| |
| } |
| |
| prop_file_svc_cmd_t _prop_file_parse_svc_type(char *line_buf) |
| { |
| char on_property[] = "on property:"; |
| char service[] = "service "; |
| prop_file_svc_cmd_t cur_command_type; |
| |
| if (strncmp(line_buf, on_property, strlen(on_property)) == 0) { |
| cur_command_type = svc_cmd_type_onproperty; |
| } else if (strncmp(line_buf, service, strlen(service)) == 0) { |
| cur_command_type = svc_cmd_type_service; |
| } else if ( (line_buf[0] == ' ') && |
| (line_buf[1] == ' ') && |
| (line_buf[2] == ' ') && |
| (line_buf[3] == ' ') && |
| (line_buf[4] != ' ')) { |
| /*how many space?*/ |
| cur_command_type = svc_cmd_type_command; |
| } else { |
| cur_command_type = svc_cmd_type_others; |
| } |
| |
| return cur_command_type; |
| |
| } |
| |
| /* reads a file, making sure it is terminated with \n \0 */ |
| void *_prop_file_read_file(const char *fn, unsigned *_sz) |
| { |
| char *data = NULL; |
| int sz; |
| int fd; |
| |
| data = 0; |
| fd = open(fn, O_RDONLY); |
| if (fd < 0) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "... :%s ", fn); |
| return 0; |
| } |
| |
| sz = lseek(fd, 0, SEEK_END); |
| if (sz < 0) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "... :%s ", fn); |
| goto oops; |
| } |
| |
| if (lseek(fd, 0, SEEK_SET) != 0) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "... :%s ", fn); |
| goto oops; |
| } |
| |
| data = (char*) malloc(sz + 2); |
| if (data == 0) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "... :%s ", fn); |
| goto oops; |
| } |
| |
| if (read(fd, data, sz) != sz) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "... :%s ", fn); |
| goto oops; |
| } |
| |
| close(fd); |
| data[sz] = '\n'; |
| data[sz+1] = 0; |
| if(_sz) *_sz = sz; |
| |
| return data; |
| |
| oops: |
| close(fd); |
| if (data != 0) |
| free(data); |
| |
| return 0; |
| } |
| |
| static void _prop_file_load_dir_svc(void) |
| { |
| #define LINE_BUFFER_MAX_SIZE 256 |
| DIR *dir; |
| struct dirent* entry; |
| char folder_path[264]; |
| FILE *read_file; |
| char line_buf[LINE_BUFFER_MAX_SIZE]; |
| prop_file_svc_cmd_t cur_command_type; |
| prop_svc_t *cur_svc; |
| prop_svc_t svc_buf; |
| char *act_ptr; |
| char act_name[PROP_SVC_ACTION_MAX_SIZE]; |
| int index; |
| |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " Begin. "); |
| |
| dir = opendir(PROP_CORE_SERVICE_DIR); |
| if (!dir) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Unable to open default property directory %s errno: %d", PROP_CORE_SERVICE_DIR, errno); |
| return; |
| } |
| |
| while ((entry = readdir(dir)) != NULL) { |
| if (!strstr(entry->d_name, ".rc")) |
| continue; |
| |
| sprintf(folder_path, "%s/%s", PROP_CORE_SERVICE_DIR, entry->d_name); |
| |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " Begin parse the line. file-name:%s ", folder_path); |
| |
| read_file = fopen(folder_path , "r" ); |
| if (!read_file) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Unable to open default file %s errno: %d\n", folder_path, errno); |
| continue; |
| } |
| |
| cur_svc = NULL; |
| while (fgets(line_buf, LINE_BUFFER_MAX_SIZE-1, read_file ) != NULL) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " line:%s len:%d", line_buf, strlen(line_buf)); |
| |
| /*trim out '\n'...*/ |
| _prop_file_trim_line(line_buf); |
| |
| /*filter out #*/ |
| index = 0; |
| while (line_buf[index] == ' ') index++; |
| if (line_buf[index] == '#') |
| continue; |
| |
| cur_command_type = _prop_file_parse_svc_type(line_buf); |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " line_buf:%s len:%d command_type:%d ", line_buf, strlen(line_buf), cur_command_type); |
| |
| switch (cur_command_type) { |
| case svc_cmd_type_command: |
| if (cur_svc) { |
| act_ptr = line_buf; |
| while (*act_ptr == ' ') |
| act_ptr++; |
| |
| if (strlen(act_ptr) >= PROP_SVC_ACTION_MAX_SIZE) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Action length exceed MAX %s ", act_ptr); |
| } else { |
| if (prop_svc_add_act(cur_svc->svc_name, cur_svc->svc_key, cur_svc->svc_value, act_ptr) < 0) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Can not add the action.name:%s %s ", cur_svc->svc_key, act_ptr); |
| } |
| } |
| } else { |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, "Wrong command, because of no prefix. Line:%s ", line_buf); |
| } |
| break; |
| |
| case svc_cmd_type_service: |
| cur_svc = &svc_buf; |
| memset(cur_svc, 0x0, sizeof(prop_svc_t)); |
| INIT_LIST(&(cur_svc->act_list)); |
| INIT_LIST(&(cur_svc->slist)); |
| strcpy(cur_svc->svc_name, entry->d_name); |
| cur_svc->svc_type = PROP_SVC_TYPE_SERVICE; |
| if (_prop_file_parse_svc_service(line_buf, cur_svc->svc_key, act_name) < 0) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Unable to parse the name %s\n", line_buf); |
| cur_svc = NULL; |
| } else { |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " service! name:%s daemon:%s ", cur_svc->svc_key, act_name); |
| if (prop_svc_add(cur_svc) < 0) |
| { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Unable to add the on property"); |
| cur_svc = NULL; |
| } else { |
| if (prop_svc_add_act(cur_svc->svc_name, cur_svc->svc_key, NULL, act_name) < 0) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Unable to add the property"); |
| cur_svc = NULL; |
| } |
| } |
| } |
| |
| /*Init the cur_svc, since only need the first line for service*/ |
| cur_svc = NULL; |
| break; |
| |
| case svc_cmd_type_onproperty: |
| cur_svc = &svc_buf; |
| /*parse key and value*/ |
| memset(cur_svc, 0x0, sizeof(prop_svc_t)); |
| INIT_LIST(&(cur_svc->act_list)); |
| INIT_LIST(&(cur_svc->slist)); |
| strcpy(cur_svc->svc_name, entry->d_name); |
| cur_svc->svc_type = PROP_SVC_TYPE_ON_PROPERTY; |
| if (_prop_file_parse_svc_onproperty(line_buf, cur_svc->svc_key, cur_svc->svc_value) < 0) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Unable to parse the name %s ", line_buf); |
| cur_svc = NULL; |
| } else { |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " onproperty!line_buf:%s key:%s value:%s ", |
| line_buf, cur_svc->svc_key, cur_svc->svc_value); |
| if (prop_svc_add(cur_svc) < 0) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Unable to add the onproperty"); |
| cur_svc = NULL; |
| } |
| } |
| |
| break; |
| |
| case svc_cmd_type_others: |
| default: |
| cur_svc = NULL; |
| break; |
| } |
| } |
| |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " File end. "); |
| fclose ( read_file ); |
| } |
| |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " Directory end. "); |
| closedir(dir); |
| } |
| |
| static void _prop_file_load_props_file(const char *file_path) |
| { |
| char *data; |
| unsigned sz; |
| |
| data = _prop_file_read_file(file_path, &sz); |
| if(0 == data) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Unable to read file data. File:%s ", file_path); |
| return; |
| } |
| |
| _prop_file_load_properties(data, NULL); |
| free(data); |
| |
| return; |
| } |
| |
| static void _prop_file_load_dir_props(void) |
| { |
| DIR *dir; |
| struct dirent* entry; |
| char folder_path[256]; |
| char *data; |
| unsigned sz; |
| |
| dir = opendir(PROP_CORE_PROPERTY_DIR); |
| if (!dir) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Unable to open default property directory %s errno: %d ", PROP_CORE_PROPERTY_DIR, errno); |
| return; |
| } |
| |
| while ((entry = readdir(dir)) != NULL) { |
| if (!strstr(entry->d_name, ".prop")) |
| continue; |
| |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " *.prop file. file:%s ", entry->d_name); |
| |
| sprintf(folder_path, "%s/%s", PROP_CORE_PROPERTY_DIR, entry->d_name); |
| data = _prop_file_read_file(folder_path, &sz); |
| if(data != 0) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Unable to read file data. File:%s ", folder_path); |
| _prop_file_load_properties(data, NULL); |
| free(data); |
| } |
| } |
| closedir(dir); |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " End. "); |
| } |
| |
| static void _prop_file_load_dir_persistent(void) |
| { |
| DIR *dir; |
| int dir_fd; |
| struct dirent* entry; |
| char value[PROPERTY_VALUE_MAX]; |
| int fd, length; |
| |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " Begin. "); |
| |
| dir = opendir(PROP_CORE_PERSISTENT_PROPERTY_DIR); |
| if (!dir) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Unable to open persistent property directory %s errno: %d\n", PROP_CORE_PERSISTENT_PROPERTY_DIR, errno); |
| return; |
| } |
| |
| dir_fd = dirfd(dir); |
| while ((entry = readdir(dir)) != NULL) { |
| if (strncmp("persist.", entry->d_name, strlen("persist."))) |
| continue; |
| |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " persist.* file. file:%s ", entry->d_name); |
| |
| /* open the file and read the property value */ |
| fd = openat(dir_fd, entry->d_name, O_RDONLY); |
| if (fd < 0) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Unable to open persistent property file. %s errno: %d ", |
| entry->d_name, errno); |
| continue; |
| } |
| |
| length = read(fd, value, sizeof(value) - 1); |
| close(fd); |
| |
| if (length >= 0) { |
| value[length] = 0; |
| prop_core_set(entry->d_name, value); |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " name:%s value:%s ", entry->d_name, value); |
| } else { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Unable to read persistent property file %s errno: %d\n", |
| entry->d_name, errno); |
| } |
| } |
| closedir(dir); |
| PROP_DEBUG_PRINT(PROP_DEBUG_TRACE, " End. "); |
| } |
| |
| void prop_file_write_persistent(const char *name, const char *value) |
| { |
| #define PROP_FILE_PATH_MAX 256 |
| char temp_path[PROP_FILE_PATH_MAX]; |
| char path[PROP_FILE_PATH_MAX]; |
| FILE *file_ptr; |
| |
| /*avoid to overwrite by default value*/ |
| if (!prop_file_persist_load_done) { |
| snprintf(path, sizeof(path), "%s/%s", PROP_CORE_PERSISTENT_PROPERTY_DIR, name); |
| if (0 == access(path, F_OK)){ |
| return; |
| } |
| |
| prop_file_persist_bootup_sync = 1; |
| } |
| |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "key:%s value:%s ", name, value); |
| |
| snprintf(temp_path, sizeof(temp_path), "%s/.temp.xxx", PROP_CORE_PERSISTENT_PROPERTY_DIR); |
| |
| file_ptr = fopen(temp_path , "w"); |
| if (!file_ptr) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Unable to write persistent property to temp file %s errno: %d", temp_path, errno); |
| return; |
| } |
| |
| fwrite(value, 1, strlen(value), file_ptr); |
| fflush(file_ptr); |
| fclose(file_ptr); |
| |
| snprintf(path, sizeof(path), "%s/%s", PROP_CORE_PERSISTENT_PROPERTY_DIR, name); |
| if (rename(temp_path, path) < 0) { |
| PROP_DEBUG_PRINT(PROP_DEBUG_ERROR, "Unable to rename persistent property file %s to %s ", temp_path, path); |
| } |
| |
| /*bootup performance*/ |
| if (prop_file_persist_load_done) { |
| sync(); |
| } |
| } |
| |
| void prop_file_load_props(void) |
| { |
| char temp_path[128]; |
| prop_file_persist_load_done = 0; |
| |
| /*make sure folder exist*/ |
| snprintf(temp_path, sizeof(temp_path), "mkdir -p %s", PROP_CORE_PERSISTENT_PROPERTY_DIR); |
| system(temp_path); |
| |
| _prop_file_load_props_file(PROP_CORE_PROP_DEFAULT); |
| _prop_file_load_props_file(PROP_CORE_PROP_SYSTEM_BUILD); |
| _prop_file_load_props_file(PROP_CORE_PROP_VENDOR_BUILD); |
| _prop_file_load_props_file(PROP_CORE_PROP_FACTORY); |
| _prop_file_load_props_file(PROP_CORE_PROP_LOCAL); |
| _prop_file_load_dir_persistent(); |
| |
| if (prop_file_persist_bootup_sync){ |
| sync(); |
| } |
| prop_file_persist_load_done = 1; |
| } |
| |
| void prop_file_load_svc(void) |
| { |
| _prop_file_load_dir_svc(); |
| } |
| |