2020-01-18 03:01:02 +08:00
|
|
|
#include "common/params.h"
|
|
|
|
|
|
|
|
#ifndef _GNU_SOURCE
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#endif // _GNU_SOURCE
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <sys/file.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
|
|
#include <map>
|
|
|
|
#include <string>
|
2020-10-13 22:23:23 +08:00
|
|
|
#include <iostream>
|
|
|
|
#include <csignal>
|
2020-06-10 04:46:49 +08:00
|
|
|
#include <string.h>
|
2020-01-18 03:01:02 +08:00
|
|
|
|
|
|
|
#include "common/util.h"
|
|
|
|
#include "common/utilpp.h"
|
|
|
|
|
|
|
|
|
2020-10-15 19:15:22 +08:00
|
|
|
std::string getenv_default(const char* env_var, const char * suffix, const char* default_val) {
|
2020-10-15 04:45:24 +08:00
|
|
|
const char* env_val = getenv(env_var);
|
2020-10-15 19:15:22 +08:00
|
|
|
if (env_val != NULL){
|
|
|
|
return std::string(env_val) + std::string(suffix);
|
|
|
|
} else{
|
|
|
|
return std::string(default_val);
|
|
|
|
}
|
2020-01-18 03:01:02 +08:00
|
|
|
}
|
|
|
|
|
2020-10-15 19:15:22 +08:00
|
|
|
#if defined(QCOM) || defined(QCOM2)
|
|
|
|
const std::string default_params_path = "/data/params";
|
|
|
|
#else
|
|
|
|
const std::string default_params_path = getenv_default("BASEDIR", "persists/params", "/data/params");
|
|
|
|
#endif
|
2020-05-09 10:39:18 +08:00
|
|
|
|
2020-10-15 19:15:22 +08:00
|
|
|
#if defined(QCOM) || defined(QCOM2)
|
|
|
|
const std::string persistent_params_path = "/persist/comma/params";
|
2020-05-09 10:39:18 +08:00
|
|
|
#else
|
2020-10-15 04:45:24 +08:00
|
|
|
const std::string persistent_params_path = default_params_path;
|
2020-05-09 10:39:18 +08:00
|
|
|
#endif
|
|
|
|
|
2020-01-18 03:01:02 +08:00
|
|
|
|
2020-10-13 22:23:23 +08:00
|
|
|
volatile sig_atomic_t params_do_exit = 0;
|
|
|
|
void params_sig_handler(int signal) {
|
|
|
|
std::cout << "got signal" << std::endl;
|
|
|
|
params_do_exit = 1;
|
|
|
|
}
|
|
|
|
|
2020-01-18 03:01:02 +08:00
|
|
|
static int fsync_dir(const char* path){
|
|
|
|
int result = 0;
|
2020-10-13 22:23:23 +08:00
|
|
|
int fd = open(path, O_RDONLY, 0755);
|
2020-01-18 03:01:02 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-13 22:23:23 +08:00
|
|
|
static int mkdir_p(std::string path) {
|
|
|
|
char * _path = (char *)path.c_str();
|
|
|
|
|
2020-10-14 10:14:51 +08:00
|
|
|
mode_t prev_mask = umask(0);
|
2020-10-13 22:23:23 +08:00
|
|
|
for (char *p = _path + 1; *p; p++) {
|
|
|
|
if (*p == '/') {
|
|
|
|
*p = '\0'; // Temporarily truncate
|
2020-10-14 10:14:51 +08:00
|
|
|
if (mkdir(_path, 0777) != 0) {
|
2020-10-13 22:23:23 +08:00
|
|
|
if (errno != EEXIST) return -1;
|
|
|
|
}
|
|
|
|
*p = '/';
|
|
|
|
}
|
|
|
|
}
|
2020-10-14 10:14:51 +08:00
|
|
|
if (mkdir(_path, 0777) != 0) {
|
2020-10-13 22:23:23 +08:00
|
|
|
if (errno != EEXIST) return -1;
|
2020-05-09 10:39:18 +08:00
|
|
|
}
|
2020-10-14 10:14:51 +08:00
|
|
|
umask(prev_mask);
|
2020-05-09 10:39:18 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-10-13 22:23:23 +08:00
|
|
|
static int ensure_dir_exists(std::string path) {
|
|
|
|
// TODO: replace by std::filesystem::create_directories
|
|
|
|
return mkdir_p(path.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Params::Params(bool persistent_param){
|
2020-10-15 04:45:24 +08:00
|
|
|
params_path = persistent_param ? persistent_params_path : default_params_path;
|
2020-10-13 22:23:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Params::Params(std::string path) {
|
|
|
|
params_path = path;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Params::write_db_value(std::string key, std::string dat){
|
|
|
|
return write_db_value(key.c_str(), dat.c_str(), dat.length());
|
|
|
|
}
|
|
|
|
|
|
|
|
int Params::write_db_value(const char* key, const char* value, size_t value_size) {
|
2020-01-18 03:01:02 +08:00
|
|
|
// 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;
|
2020-10-13 22:23:23 +08:00
|
|
|
std::string path;
|
|
|
|
std::string tmp_path;
|
2020-01-18 03:01:02 +08:00
|
|
|
ssize_t bytes_written;
|
2020-05-09 10:39:18 +08:00
|
|
|
|
|
|
|
// Make sure params path exists
|
|
|
|
result = ensure_dir_exists(params_path);
|
|
|
|
if (result < 0) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2020-01-18 03:01:02 +08:00
|
|
|
|
2020-05-09 10:39:18 +08:00
|
|
|
// See if the symlink exists, otherwise create it
|
2020-10-13 22:23:23 +08:00
|
|
|
path = params_path + "/d";
|
2020-05-09 10:39:18 +08:00
|
|
|
struct stat st;
|
2020-10-13 22:23:23 +08:00
|
|
|
if (stat(path.c_str(), &st) == -1) {
|
2020-05-09 10:39:18 +08:00
|
|
|
// Create temp folder
|
2020-10-13 22:23:23 +08:00
|
|
|
path = params_path + "/.tmp_XXXXXX";
|
|
|
|
|
|
|
|
char *t = mkdtemp((char*)path.c_str());
|
|
|
|
if (t == NULL){
|
2020-05-09 10:39:18 +08:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2020-10-13 22:23:23 +08:00
|
|
|
std::string tmp_dir(t);
|
2020-05-09 10:39:18 +08:00
|
|
|
|
|
|
|
// Set permissions
|
2020-10-13 22:23:23 +08:00
|
|
|
result = chmod(tmp_dir.c_str(), 0777);
|
2020-05-09 10:39:18 +08:00
|
|
|
if (result < 0) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Symlink it to temp link
|
2020-10-13 22:23:23 +08:00
|
|
|
tmp_path = tmp_dir + ".link";
|
|
|
|
result = symlink(tmp_dir.c_str(), tmp_path.c_str());
|
2020-05-09 10:39:18 +08:00
|
|
|
if (result < 0) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move symlink to <params>/d
|
2020-10-13 22:23:23 +08:00
|
|
|
path = params_path + "/d";
|
|
|
|
result = rename(tmp_path.c_str(), path.c_str());
|
2020-05-09 10:39:18 +08:00
|
|
|
if (result < 0) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2020-10-18 08:07:40 +08:00
|
|
|
} else {
|
|
|
|
// Ensure permissions are correct in case we didn't create the symlink
|
|
|
|
result = chmod(path.c_str(), 0777);
|
|
|
|
if (result < 0) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2020-01-18 03:01:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Write value to temp.
|
2020-10-13 22:23:23 +08:00
|
|
|
tmp_path = params_path + "/.tmp_value_XXXXXX";
|
|
|
|
tmp_fd = mkstemp((char*)tmp_path.c_str());
|
2020-01-18 03:01:02 +08:00
|
|
|
bytes_written = write(tmp_fd, value, value_size);
|
2020-10-18 03:40:01 +08:00
|
|
|
if (bytes_written < 0 || (size_t)bytes_written != value_size) {
|
2020-01-18 03:01:02 +08:00
|
|
|
result = -20;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build lock path
|
2020-10-13 22:23:23 +08:00
|
|
|
path = params_path + "/.lock";
|
|
|
|
lock_fd = open(path.c_str(), O_CREAT, 0775);
|
2020-01-18 03:01:02 +08:00
|
|
|
|
|
|
|
// Build key path
|
2020-10-13 22:23:23 +08:00
|
|
|
path = params_path + "/d/" + std::string(key);
|
2020-01-18 03:01:02 +08:00
|
|
|
|
|
|
|
// 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.
|
2020-10-13 22:23:23 +08:00
|
|
|
result = rename(tmp_path.c_str(), path.c_str());
|
2020-01-18 03:01:02 +08:00
|
|
|
if (result < 0) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fsync parent directory
|
2020-10-13 22:23:23 +08:00
|
|
|
path = params_path + "/d";
|
|
|
|
result = fsync_dir(path.c_str());
|
2020-01-18 03:01:02 +08:00
|
|
|
if (result < 0) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
// Release lock.
|
|
|
|
if (lock_fd >= 0) {
|
|
|
|
close(lock_fd);
|
|
|
|
}
|
|
|
|
if (tmp_fd >= 0) {
|
|
|
|
if (result < 0) {
|
2020-10-13 22:23:23 +08:00
|
|
|
remove(tmp_path.c_str());
|
2020-01-18 03:01:02 +08:00
|
|
|
}
|
|
|
|
close(tmp_fd);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-10-13 22:23:23 +08:00
|
|
|
int Params::delete_db_value(std::string key) {
|
|
|
|
return delete_db_value(key.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
int Params::delete_db_value(const char* key) {
|
2020-01-18 03:01:02 +08:00
|
|
|
int lock_fd = -1;
|
|
|
|
int result;
|
2020-10-13 22:23:23 +08:00
|
|
|
std::string path;
|
2020-01-18 03:01:02 +08:00
|
|
|
|
|
|
|
// Build lock path, and open lockfile
|
2020-10-13 22:23:23 +08:00
|
|
|
path = params_path + "/.lock";
|
|
|
|
lock_fd = open(path.c_str(), O_CREAT, 0775);
|
2020-01-18 03:01:02 +08:00
|
|
|
|
|
|
|
// Take lock.
|
|
|
|
result = flock(lock_fd, LOCK_EX);
|
|
|
|
if (result < 0) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete value.
|
2020-10-13 22:23:23 +08:00
|
|
|
path = params_path + "/d/" + std::string(key);
|
|
|
|
result = remove(path.c_str());
|
2020-01-18 03:01:02 +08:00
|
|
|
if (result != 0) {
|
|
|
|
result = ERR_NO_VALUE;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fsync parent directory
|
2020-10-13 22:23:23 +08:00
|
|
|
path = params_path + "/d";
|
|
|
|
result = fsync_dir(path.c_str());
|
2020-01-18 03:01:02 +08:00
|
|
|
if (result < 0) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
// Release lock.
|
|
|
|
if (lock_fd >= 0) {
|
|
|
|
close(lock_fd);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-10-13 22:23:23 +08:00
|
|
|
std::string Params::get(std::string key, bool block){
|
|
|
|
char* value;
|
|
|
|
size_t size;
|
|
|
|
int r;
|
2020-01-18 03:01:02 +08:00
|
|
|
|
2020-10-13 22:23:23 +08:00
|
|
|
if (block){
|
|
|
|
r = read_db_value_blocking((const char*)key.c_str(), &value, &size);
|
|
|
|
} else {
|
|
|
|
r = read_db_value((const char*)key.c_str(), &value, &size);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r == 0){
|
|
|
|
std::string s(value, size);
|
|
|
|
free(value);
|
|
|
|
return s;
|
|
|
|
} else {
|
|
|
|
return "";
|
2020-01-18 03:01:02 +08:00
|
|
|
}
|
2020-10-13 22:23:23 +08:00
|
|
|
}
|
2020-01-18 03:01:02 +08:00
|
|
|
|
2020-10-13 22:23:23 +08:00
|
|
|
int Params::read_db_value(const char* key, char** value, size_t* value_sz) {
|
|
|
|
std::string path = params_path + "/d/" + std::string(key);
|
|
|
|
*value = static_cast<char*>(read_file(path.c_str(), value_sz));
|
2020-01-18 03:01:02 +08:00
|
|
|
if (*value == NULL) {
|
2020-09-24 17:51:36 +08:00
|
|
|
return -22;
|
2020-01-18 03:01:02 +08:00
|
|
|
}
|
2020-09-24 17:51:36 +08:00
|
|
|
return 0;
|
2020-01-18 03:01:02 +08:00
|
|
|
}
|
|
|
|
|
2020-10-13 22:23:23 +08:00
|
|
|
int Params::read_db_value_blocking(const char* key, char** value, size_t* value_sz) {
|
|
|
|
params_do_exit = 0;
|
|
|
|
void (*prev_handler_sigint)(int) = std::signal(SIGINT, params_sig_handler);
|
|
|
|
void (*prev_handler_sigterm)(int) = std::signal(SIGTERM, params_sig_handler);
|
|
|
|
|
|
|
|
while (!params_do_exit) {
|
|
|
|
const int result = read_db_value(key, value, value_sz);
|
2020-01-18 03:01:02 +08:00
|
|
|
if (result == 0) {
|
2020-10-13 22:23:23 +08:00
|
|
|
break;
|
2020-01-18 03:01:02 +08:00
|
|
|
} else {
|
2020-10-13 22:23:23 +08:00
|
|
|
usleep(100000); // 0.1 s
|
2020-01-18 03:01:02 +08:00
|
|
|
}
|
|
|
|
}
|
2020-10-13 22:23:23 +08:00
|
|
|
|
|
|
|
std::signal(SIGINT, prev_handler_sigint);
|
|
|
|
std::signal(SIGTERM, prev_handler_sigterm);
|
|
|
|
return params_do_exit; // Return 0 if we had no interrupt
|
2020-01-18 03:01:02 +08:00
|
|
|
}
|
|
|
|
|
2020-10-13 22:23:23 +08:00
|
|
|
int Params::read_db_all(std::map<std::string, std::string> *params) {
|
2020-01-18 03:01:02 +08:00
|
|
|
int err = 0;
|
|
|
|
|
2020-10-13 22:23:23 +08:00
|
|
|
std::string lock_path = params_path + "/.lock";
|
2020-01-18 03:01:02 +08:00
|
|
|
|
|
|
|
int lock_fd = open(lock_path.c_str(), 0);
|
|
|
|
if (lock_fd < 0) return -1;
|
|
|
|
|
2020-09-24 17:51:36 +08:00
|
|
|
err = flock(lock_fd, LOCK_SH);
|
2020-09-28 17:47:38 +08:00
|
|
|
if (err < 0) {
|
|
|
|
close(lock_fd);
|
|
|
|
return err;
|
|
|
|
}
|
2020-01-18 03:01:02 +08:00
|
|
|
|
2020-10-13 22:23:23 +08:00
|
|
|
std::string key_path = params_path + "/d";
|
2020-01-18 03:01:02 +08:00
|
|
|
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);
|
2020-10-13 22:23:23 +08:00
|
|
|
std::string value = util::read_file(key_path + "/" + key);
|
2020-01-18 03:01:02 +08:00
|
|
|
|
|
|
|
(*params)[key] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(d);
|
|
|
|
|
|
|
|
close(lock_fd);
|
|
|
|
return 0;
|
|
|
|
}
|
2020-06-10 04:46:49 +08:00
|
|
|
|
2020-10-13 22:23:23 +08:00
|
|
|
std::vector<char> Params::read_db_bytes(const char* param_name) {
|
2020-06-10 04:46:49 +08:00
|
|
|
std::vector<char> bytes;
|
|
|
|
char* value;
|
|
|
|
size_t sz;
|
2020-10-13 22:23:23 +08:00
|
|
|
int result = read_db_value(param_name, &value, &sz);
|
2020-06-10 04:46:49 +08:00
|
|
|
if (result == 0) {
|
|
|
|
bytes.assign(value, value+sz);
|
|
|
|
free(value);
|
|
|
|
}
|
|
|
|
return bytes;
|
|
|
|
}
|
2020-08-30 08:16:00 +08:00
|
|
|
|
2020-10-13 22:23:23 +08:00
|
|
|
bool Params::read_db_bool(const char* param_name) {
|
|
|
|
std::vector<char> bytes = read_db_bytes(param_name);
|
2020-08-30 08:16:00 +08:00
|
|
|
return bytes.size() > 0 and bytes[0] == '1';
|
|
|
|
}
|