mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 05:23:41 +08:00
db1c5a7445
Release inherited Params locks correctly across fork and exec. Preserve qualified lateral armed intent through temporary faults, restore the Big UI CEM gauge, improve speed typography, and hide unavailable CEM initialization text. Include matching device artifacts and focused regression coverage.
255 lines
6.8 KiB
C++
255 lines
6.8 KiB
C++
#include "common/params.h"
|
|
|
|
#include <dirent.h>
|
|
#include <sys/file.h>
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <csignal>
|
|
#include <unordered_map>
|
|
|
|
#include "common/params_keys.h"
|
|
#include "common/queue.h"
|
|
#include "common/swaglog.h"
|
|
#include "common/util.h"
|
|
#include "common/hardware/hw.h"
|
|
|
|
namespace {
|
|
|
|
volatile sig_atomic_t params_do_exit = 0;
|
|
void params_sig_handler(int signal) {
|
|
params_do_exit = 1;
|
|
}
|
|
|
|
int fsync_dir(const std::string &path) {
|
|
int result = -1;
|
|
int fd = HANDLE_EINTR(open(path.c_str(), O_RDONLY, 0755));
|
|
if (fd >= 0) {
|
|
result = HANDLE_EINTR(fsync(fd));
|
|
HANDLE_EINTR(close(fd));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool create_params_path(const std::string ¶m_path, const std::string &key_path) {
|
|
// Make sure params path exists
|
|
if (!util::file_exists(param_path) && !util::create_directories(param_path, 0775)) {
|
|
return false;
|
|
}
|
|
|
|
// See if the symlink exists, otherwise create it
|
|
if (!util::file_exists(key_path)) {
|
|
// 1) Create temp folder
|
|
// 2) Symlink it to temp link
|
|
// 3) Move symlink to <params>/d
|
|
|
|
std::string tmp_path = param_path + "/.tmp_XXXXXX";
|
|
// this should be OK since mkdtemp just replaces characters in place
|
|
char *tmp_dir = mkdtemp((char *)tmp_path.c_str());
|
|
if (tmp_dir == NULL) {
|
|
return false;
|
|
}
|
|
|
|
std::string link_path = std::string(tmp_dir) + ".link";
|
|
if (symlink(tmp_dir, link_path.c_str()) != 0) {
|
|
return false;
|
|
}
|
|
|
|
// don't return false if it has been created by other
|
|
if (rename(link_path.c_str(), key_path.c_str()) != 0 && errno != EEXIST) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string ensure_params_path(const std::string &prefix, const std::string &path = {}) {
|
|
std::string params_path = path.empty() ? Path::params() : path;
|
|
if (!create_params_path(params_path, params_path + prefix)) {
|
|
throw std::runtime_error(util::string_format(
|
|
"Failed to ensure params path, errno=%d, path=%s, param_prefix=%s",
|
|
errno, params_path.c_str(), prefix.c_str()));
|
|
}
|
|
return params_path;
|
|
}
|
|
|
|
class FileLock {
|
|
public:
|
|
FileLock(const std::string &fn) : owner_pid_(getpid()) {
|
|
fd_ = HANDLE_EINTR(open(fn.c_str(), O_CREAT | O_CLOEXEC, 0775));
|
|
locked_ = fd_ >= 0 && HANDLE_EINTR(flock(fd_, LOCK_EX)) == 0;
|
|
if (!locked_) {
|
|
LOGE("Failed to lock file %s, errno=%d", fn.c_str(), errno);
|
|
}
|
|
}
|
|
~FileLock() {
|
|
if (fd_ >= 0) {
|
|
// flock follows the open file description across fork. Release explicitly
|
|
// in the acquiring process even while children retain their copies.
|
|
if (locked_ && owner_pid_ == getpid()) {
|
|
HANDLE_EINTR(flock(fd_, LOCK_UN));
|
|
}
|
|
close(fd_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
const pid_t owner_pid_;
|
|
int fd_ = -1;
|
|
bool locked_ = false;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
Params::Params(const std::string &path) {
|
|
params_prefix = "/" + util::getenv("OPENPILOT_PREFIX", "d");
|
|
params_path = ensure_params_path(params_prefix, path);
|
|
}
|
|
|
|
Params::~Params() {
|
|
if (future.valid()) {
|
|
future.wait();
|
|
}
|
|
assert(queue.empty());
|
|
}
|
|
|
|
std::vector<std::string> Params::allKeys() const {
|
|
std::vector<std::string> ret;
|
|
for (auto &p : keys) {
|
|
ret.push_back(p.first);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool Params::checkKey(const std::string &key) {
|
|
return keys.find(key) != keys.end();
|
|
}
|
|
|
|
ParamKeyFlag Params::getKeyFlag(const std::string &key) {
|
|
return static_cast<ParamKeyFlag>(keys[key].flags);
|
|
}
|
|
|
|
ParamKeyType Params::getKeyType(const std::string &key) {
|
|
return keys[key].type;
|
|
}
|
|
|
|
std::optional<std::string> Params::getKeyDefaultValue(const std::string &key) {
|
|
return keys[key].default_value;
|
|
}
|
|
|
|
int Params::put(const char* key, const char* value, size_t value_size) {
|
|
// 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
|
|
std::string tmp_path = params_path + "/.tmp_value_XXXXXX";
|
|
int tmp_fd = mkstemp((char*)tmp_path.c_str());
|
|
if (tmp_fd < 0) return -1;
|
|
|
|
int result = -1;
|
|
do {
|
|
// Write value to temp.
|
|
ssize_t bytes_written = HANDLE_EINTR(write(tmp_fd, value, value_size));
|
|
if (bytes_written < 0 || (size_t)bytes_written != value_size) {
|
|
result = -20;
|
|
break;
|
|
}
|
|
|
|
// fsync to force persist the changes.
|
|
if ((result = HANDLE_EINTR(fsync(tmp_fd))) < 0) break;
|
|
|
|
FileLock file_lock(params_path + "/.lock");
|
|
|
|
// Move temp into place.
|
|
if ((result = rename(tmp_path.c_str(), getParamPath(key).c_str())) < 0) break;
|
|
|
|
// fsync parent directory
|
|
result = fsync_dir(getParamPath());
|
|
} while (false);
|
|
|
|
close(tmp_fd);
|
|
if (result != 0) {
|
|
::unlink(tmp_path.c_str());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int Params::remove(const std::string &key) {
|
|
FileLock file_lock(params_path + "/.lock");
|
|
int result = unlink(getParamPath(key).c_str());
|
|
if (result != 0) {
|
|
return result;
|
|
}
|
|
return fsync_dir(getParamPath());
|
|
}
|
|
|
|
std::string Params::get(const std::string &key, bool block) {
|
|
if (!block) {
|
|
return util::read_file(getParamPath(key));
|
|
} else {
|
|
// blocking read until successful
|
|
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);
|
|
|
|
std::string value;
|
|
while (!params_do_exit) {
|
|
if (value = util::read_file(getParamPath(key)); !value.empty()) {
|
|
break;
|
|
}
|
|
util::sleep_for(100); // 0.1 s
|
|
}
|
|
|
|
std::signal(SIGINT, prev_handler_sigint);
|
|
std::signal(SIGTERM, prev_handler_sigterm);
|
|
return value;
|
|
}
|
|
}
|
|
|
|
std::map<std::string, std::string> Params::readAll() {
|
|
FileLock file_lock(params_path + "/.lock");
|
|
return util::read_files_in_dir(getParamPath());
|
|
}
|
|
|
|
void Params::clearAll(ParamKeyFlag key_flag) {
|
|
FileLock file_lock(params_path + "/.lock");
|
|
|
|
// 1) delete params of key_flag
|
|
// 2) delete files that are not defined in the keys.
|
|
if (DIR *d = opendir(getParamPath().c_str())) {
|
|
struct dirent *de = NULL;
|
|
while ((de = readdir(d))) {
|
|
if (de->d_type != DT_DIR) {
|
|
auto it = keys.find(de->d_name);
|
|
if (it == keys.end() || (it->second.flags & key_flag)) {
|
|
unlink(getParamPath(de->d_name).c_str());
|
|
}
|
|
}
|
|
}
|
|
closedir(d);
|
|
}
|
|
|
|
fsync_dir(getParamPath());
|
|
}
|
|
|
|
void Params::putNonBlocking(const std::string &key, const std::string &val) {
|
|
queue.push(std::make_pair(key, val));
|
|
// start thread on demand
|
|
if (!future.valid() || future.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) {
|
|
future = std::async(std::launch::async, &Params::asyncWriteThread, this);
|
|
}
|
|
}
|
|
|
|
void Params::asyncWriteThread() {
|
|
// TODO: write the latest one if a key has multiple values in the queue.
|
|
std::pair<std::string, std::string> p;
|
|
while (queue.try_pop(p, 0)) {
|
|
// Params::put is Thread-Safe
|
|
put(p.first, p.second);
|
|
}
|
|
}
|