Files
salmanoff/hcore/senseApis/senseApiManager.cpp
T
hayodea 09caf314f1 Eliminate the C FFI; Namespace lib API and DeviceManager
We decided to get rid of the C FFI for libs. It was becoming too intricate
and complicated. It was becoming a technical burden and expanding into
too much extra code. It's unfortunate, but we'll have to give up on getting
out-of-tree hot-loadable libraries the easy way.

It's possible to still do it with cross compilation or by keeping track
of the libstdc++ version that the running harikoff binary was compiled
against. Then we can ensure that our loadable lib code is linked against
that same libstdc++ code and this should ensure ABI stability.
2025-01-13 21:57:11 -04:00

255 lines
7.0 KiB
C++

#include <iostream>
#include <stdexcept>
#include <optional>
#include <filesystem>
#include <senseApis/senseApiManager.h>
#include <senseApis/senseApiLib.h>
#include <opts.h>
#include <user/senseApiDesc.h>
#include <deviceManager/deviceManager.h>
namespace fs = std::filesystem;
namespace hk {
namespace sense_api {
static std::optional<std::string> findLibraryPath(
const std::string& libraryPath)
{
std::vector<std::string> searchPaths = {
fs::current_path().string(),
fs::path("/proc/self/exe").parent_path().string()
};
if (!OptionParser::getOptions().senseApiLibPath.empty())
{
searchPaths.insert(
searchPaths.begin(), OptionParser::getOptions().senseApiLibPath);
}
for (const auto& path : searchPaths)
{
fs::path fullPath = fs::path(path) / libraryPath;
if (fs::exists(fullPath))
{
return fullPath.string();
}
}
std::cerr << std::string(__func__) + ": library '"
+ libraryPath + "' isn't in search bespoke search paths: ";
for (const auto& path : searchPaths) {
std::cerr << path << " ";
}
std::cerr << std::endl;
std::cerr << "Trying to load " + libraryPath + " from system default "
"search paths\n";
return std::nullopt;
}
SenseApiLib& SenseApiManager::loadSenseApiLib(const std::string& libraryPath)
{
std::optional<std::string> fullPath = findLibraryPath(libraryPath);
std::string resolvedPath = fullPath.value_or(libraryPath);
// Clear any existing error
dlerror();
auto dlopen_handle = std::unique_ptr<void, SenseApiLib::DlCloser>(
dlopen(resolvedPath.c_str(), RTLD_LAZY));
if (!dlopen_handle && fullPath.has_value())
{
// Fallback to using the supplied libraryPath
dlerror();
dlopen_handle.reset(dlopen(libraryPath.c_str(), RTLD_LAZY));
}
if (!dlopen_handle)
{
const char *dlerr = dlerror();
std::string error = (dlerr
? dlerr
: "Unknown error while opening shlib");
throw std::runtime_error(
std::string(__func__) + ": Cannot load library '"
+ libraryPath + "': "
+ error);
}
// Initialize getSenseApiDescriptor
auto func = reinterpret_cast<getSenseApiDescFn *>(
dlsym(dlopen_handle.get(), HK_GET_SENSE_API_DESC_FN_NAME_STR));
if (!func)
{
throw std::runtime_error(
std::string(__func__) + ": dlsym('"
HK_GET_SENSE_API_DESC_FN_NAME_STR "') failed for library '"
+ libraryPath + "'");
}
const SenseApiDesc &libApiDesc = func();
auto lib = std::make_unique<SenseApiLib>(
libraryPath, dlopen_handle.release(), func);
lib->setSenseApiDesc(libApiDesc);
senseApiLibs.push_back(std::move(lib));
return *senseApiLibs.back();
}
std::optional<std::reference_wrapper<SenseApiLib>>
SenseApiManager::getSenseApiLib(const std::string& libraryPath)
{
auto it = std::find_if(senseApiLibs.begin(), senseApiLibs.end(),
[&libPath = libraryPath](const std::unique_ptr<SenseApiLib>& lib) {
return lib->libraryPath == libPath;
}
);
if (it != senseApiLibs.end()) { return **it; }
return std::nullopt;
}
std::optional<std::reference_wrapper<SenseApiLib>>
SenseApiManager::getSenseApiLibByApiName(const std::string& apiName)
{
auto it = std::find_if(senseApiLibs.begin(), senseApiLibs.end(),
[&apiName](const std::unique_ptr<SenseApiLib>& lib) {
return lib->senseApiDesc.name == apiName;
}
);
if (it != senseApiLibs.end()) { return **it; }
return std::nullopt;
}
void SenseApiManager::unloadSenseApiLib(const std::string& libraryPath)
{
auto it = std::find_if(senseApiLibs.begin(), senseApiLibs.end(),
[&lpath = libraryPath](const std::unique_ptr<SenseApiLib>& lib) {
return lib->libraryPath == lpath;
}
);
if (it != senseApiLibs.end())
{
senseApiLibs.erase(it);
return;
}
std::cerr << std::string(__func__) + ": Library not found: "
<< libraryPath << '\n';
}
void SenseApiManager::unloadAllSenseApiLibs(void)
{
senseApiLibs.clear();
}
void SenseApiManager::loadAllSenseApiLibsFromOptions()
{
const auto& options = OptionParser::getOptions();
for (const auto& libPath : options.senseApiLibs) {
loadSenseApiLib(libPath);
}
}
std::string SenseApiManager::stringifyLibs() const
{
std::string result;
for (const auto& lib : senseApiLibs) {
result += lib->stringify() + "\n";
}
return result;
}
void SenseApiManager::initializeSenseApiLib(SenseApiLib& lib)
{
if (!lib.senseApiDesc.sal_mgmt_libOps.initializeInd)
{
throw std::runtime_error(
std::string(__func__) + ": initializeInd() is NULL for library '"
+ lib.libraryPath + "'");
}
lib.senseApiDesc.sal_mgmt_libOps.initializeInd();
}
void SenseApiManager::finalizeSenseApiLib(SenseApiLib& lib)
{
if (!lib.senseApiDesc.sal_mgmt_libOps.finalizeInd)
{
throw std::runtime_error(
std::string(__func__) + ": finalizeInd() is NULL for library '"
+ lib.libraryPath + "'");
}
lib.senseApiDesc.sal_mgmt_libOps.finalizeInd();
}
void SenseApiManager::initializeAllSenseApiLibs(void)
{
for (auto& lib : senseApiLibs) {
initializeSenseApiLib(*lib);
}
}
void SenseApiManager::finalizeAllSenseApiLibs(void)
{
for (auto& lib : senseApiLibs) {
finalizeSenseApiLib(*lib);
}
}
void SenseApiManager::attachSenseDevice(const device::SenseDeviceSpec& spec)
{
auto libOpt = getSenseApiLibByApiName(spec.api);
if (!libOpt)
{
throw std::runtime_error(
std::string(__func__) + ": No library found for API '"
+ spec.api + "'");
}
auto& lib = libOpt.value().get();
if (!lib.senseApiDesc.sal_mgmt_libOps.attachDeviceReq)
{
throw std::runtime_error(
std::string(__func__) + ": attachDeviceReq() is NULL for library '"
+ lib.libraryPath + "'");
}
lib.senseApiDesc.sal_mgmt_libOps.attachDeviceReq(&spec);
}
void SenseApiManager::detachSenseDevice(const device::SenseDeviceSpec& spec)
{
auto libOpt = getSenseApiLibByApiName(spec.api);
if (!libOpt)
{
throw std::runtime_error(
std::string(__func__) + ": No library found for API '"
+ spec.api + "'");
}
auto& lib = libOpt.value().get();
if (!lib.senseApiDesc.sal_mgmt_libOps.detachDeviceReq)
{
throw std::runtime_error(
std::string(__func__) + ": detachDeviceReq() is NULL for library '"
+ lib.libraryPath + "'");
}
lib.senseApiDesc.sal_mgmt_libOps.detachDeviceReq(&spec);
}
void SenseApiManager::attachAllSenseDevicesFromSpecs(void)
{
for (const auto& spec : device::DeviceManager::senseDeviceSpecs) {
attachSenseDevice(*spec);
}
}
void SenseApiManager::detachAllSenseDevices(void)
{
for (const auto& spec : device::DeviceManager::senseDeviceSpecs) {
detachSenseDevice(*spec);
}
}
} // namespace sense_api
} // namespace hk