#include "common/params.h" #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif // _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "common/util.h" #include "common/utilpp.h" namespace { template T* null_coalesce(T* a, T* b) { return a != NULL ? a : b; } static const char* default_params_path = null_coalesce(const_cast(getenv("PARAMS_PATH")), "/data/params"); #ifdef QCOM static const char* persistent_params_path = null_coalesce(const_cast(getenv("PERSISTENT_PARAMS_PATH")), "/persist/comma/params"); #else static const char* persistent_params_path = default_params_path; #endif } //namespace static int fsync_dir(const char* path){ int result = 0; int fd = open(path, O_RDONLY); if (fd < 0){ result = -1; goto cleanup; } result = fsync(fd); if (result < 0) { goto cleanup; } cleanup: int result_close = 0; if (fd >= 0){ result_close = close(fd); } if (result_close < 0) { return result_close; } else { return result; } } static int ensure_dir_exists(const char* path) { struct stat st; if (stat(path, &st) == -1) { return mkdir(path, 0700); } return 0; } int write_db_value(const char* key, const char* value, size_t value_size, bool persistent_param) { // Information about safely and atomically writing a file: https://lwn.net/Articles/457667/ // 1) Create temp file // 2) Write data to temp file // 3) fsync() the temp file // 4) rename the temp file to the real name // 5) fsync() the containing directory int lock_fd = -1; int tmp_fd = -1; int result; char tmp_path[1024]; char path[1024]; char *tmp_dir; ssize_t bytes_written; const char* params_path = persistent_param ? persistent_params_path : default_params_path; // Make sure params path exists result = ensure_dir_exists(params_path); if (result < 0) { goto cleanup; } result = snprintf(path, sizeof(path), "%s/d", params_path); if (result < 0) { goto cleanup; } // See if the symlink exists, otherwise create it struct stat st; if (stat(path, &st) == -1) { // Create temp folder result = snprintf(path, sizeof(path), "%s/.tmp_XXXXXX", params_path); if (result < 0) { goto cleanup; } tmp_dir = mkdtemp(path); if (tmp_dir == NULL){ goto cleanup; } // Set permissions result = chmod(tmp_dir, 0777); if (result < 0) { goto cleanup; } // Symlink it to temp link result = snprintf(tmp_path, sizeof(tmp_path), "%s.link", tmp_dir); if (result < 0) { goto cleanup; } result = symlink(tmp_dir, tmp_path); if (result < 0) { goto cleanup; } // Move symlink to /d result = snprintf(path, sizeof(path), "%s/d", params_path); if (result < 0) { goto cleanup; } result = rename(tmp_path, path); if (result < 0) { goto cleanup; } } // Write value to temp. result = snprintf(tmp_path, sizeof(tmp_path), "%s/.tmp_value_XXXXXX", params_path); if (result < 0) { goto cleanup; } tmp_fd = mkstemp(tmp_path); bytes_written = write(tmp_fd, value, value_size); if (bytes_written != value_size) { result = -20; goto cleanup; } // Build lock path result = snprintf(path, sizeof(path), "%s/.lock", params_path); if (result < 0) { goto cleanup; } lock_fd = open(path, O_CREAT); // Build key path result = snprintf(path, sizeof(path), "%s/d/%s", params_path, key); if (result < 0) { goto cleanup; } // Take lock. result = flock(lock_fd, LOCK_EX); if (result < 0) { goto cleanup; } // change permissions to 0666 for apks result = fchmod(tmp_fd, 0666); if (result < 0) { goto cleanup; } // fsync to force persist the changes. result = fsync(tmp_fd); if (result < 0) { goto cleanup; } // Move temp into place. result = rename(tmp_path, path); if (result < 0) { goto cleanup; } // fsync parent directory result = snprintf(path, sizeof(path), "%s/d", params_path); if (result < 0) { goto cleanup; } result = fsync_dir(path); if (result < 0) { goto cleanup; } cleanup: // Release lock. if (lock_fd >= 0) { close(lock_fd); } if (tmp_fd >= 0) { if (result < 0) { remove(tmp_path); } close(tmp_fd); } return result; } int delete_db_value(const char* key, bool persistent_param) { int lock_fd = -1; int result; char path[1024]; const char* params_path = persistent_param ? persistent_params_path : default_params_path; // Build lock path, and open lockfile result = snprintf(path, sizeof(path), "%s/.lock", params_path); if (result < 0) { goto cleanup; } lock_fd = open(path, O_CREAT); // Take lock. result = flock(lock_fd, LOCK_EX); if (result < 0) { goto cleanup; } // Build key path result = snprintf(path, sizeof(path), "%s/d/%s", params_path, key); if (result < 0) { goto cleanup; } // Delete value. result = remove(path); if (result != 0) { result = ERR_NO_VALUE; goto cleanup; } // fsync parent directory result = snprintf(path, sizeof(path), "%s/d", params_path); if (result < 0) { goto cleanup; } result = fsync_dir(path); if (result < 0) { goto cleanup; } cleanup: // Release lock. if (lock_fd >= 0) { close(lock_fd); } return result; } int read_db_value(const char* key, char** value, size_t* value_sz, bool persistent_param) { char path[1024]; const char* params_path = persistent_param ? persistent_params_path : default_params_path; int result = snprintf(path, sizeof(path), "%s/d/%s", params_path, key); if (result < 0) { return result; } *value = static_cast(read_file(path, value_sz)); if (*value == NULL) { return -22; } return 0; } void read_db_value_blocking(const char* key, char** value, size_t* value_sz, bool persistent_param) { while (1) { const int result = read_db_value(key, value, value_sz, persistent_param); if (result == 0) { return; } else { // Sleep for 0.1 seconds. usleep(100000); } } } int read_db_all(std::map *params, bool persistent_param) { int err = 0; const char* params_path = persistent_param ? persistent_params_path : default_params_path; std::string lock_path = util::string_format("%s/.lock", params_path); int lock_fd = open(lock_path.c_str(), 0); if (lock_fd < 0) return -1; err = flock(lock_fd, LOCK_SH); if (err < 0) { close(lock_fd); return err; } std::string key_path = util::string_format("%s/d", params_path); DIR *d = opendir(key_path.c_str()); if (!d) { close(lock_fd); return -1; } struct dirent *de = NULL; while ((de = readdir(d))) { if (!isalnum(de->d_name[0])) continue; std::string key = std::string(de->d_name); std::string value = util::read_file(util::string_format("%s/%s", key_path.c_str(), key.c_str())); (*params)[key] = value; } closedir(d); close(lock_fd); return 0; } std::vector read_db_bytes(const char* param_name, bool persistent_param) { std::vector bytes; char* value; size_t sz; int result = read_db_value(param_name, &value, &sz, persistent_param); if (result == 0) { bytes.assign(value, value+sz); free(value); } return bytes; } bool read_db_bool(const char* param_name, bool persistent_param) { std::vector bytes = read_db_bytes(param_name, persistent_param); return bytes.size() > 0 and bytes[0] == '1'; }