#include #include #include #include #include #include #include #include #include #include #include #include namespace fs = std::filesystem; namespace smo { namespace sense_api { /** * @brief Searches for a library in predefined locations * @param libraryPath The name or path of the library to find * @return Optional containing the full path if found in search paths, nullopt * if not * * Searches for the library in the following locations in order: * 1. Custom path specified by --sense-api-lib-path option (if provided) * 2. Current working directory * 3. Directory containing the executable * * If the library is not found in any of these locations, returns nullopt and * falls back to system default library search paths (LD_LIBRARY_PATH, etc.) */ static std::optional searchForLibInSmoSearchPaths( const std::string& libraryPath) { std::vector searchPaths = { fs::current_path().string(), fs::path("/proc/self/exe").parent_path().string() }; const auto& options = OptionParser::getOptions(); if (!options.senseApiLibPath.empty()) { // Insert all sense API library paths at the beginning of search paths searchPaths.insert( searchPaths.begin(), options.senseApiLibPath.begin(), options.senseApiLibPath.end()); } 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; } /* Local static function to wrap ComponentThread::getSelf for SmoCallbacks */ static std::shared_ptr ComponentThread_getSelf() { return ComponentThread::getSelf(); } /* Hooks to be provided to senseApiLibs, enabling them to call into Salmanoff * code. */ static SmoCallbacks smoCallbacks = { .searchForLibInSmoSearchPaths = searchForLibInSmoSearchPaths, .ComponentThread_getSelf = ComponentThread_getSelf }; /* Static file-scope threading model object for senseApi libraries */ static SmoThreadingModelDesc smoThreadingModelDesc = { .componentThread = nullptr }; std::optional SenseApiManager::searchForLibInSmoSearchPaths( const std::string& libraryPath) { return ::smo::sense_api::searchForLibInSmoSearchPaths(libraryPath); } SenseApiLib& SenseApiManager::loadSenseApiLib( const std::string& libraryPath, std::shared_ptr& componentThread ) { std::optional fullPath = searchForLibInSmoSearchPaths( libraryPath); std::string resolvedPath = fullPath.value_or(libraryPath); // Clear any existing error dlerror(); auto dlopen_handle = std::unique_ptr( 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( dlsym(dlopen_handle.get(), SMO_GET_SENSE_API_DESC_FN_NAME_STR)); if (!func) { throw std::runtime_error( std::string(__func__) + ": dlsym('" SMO_GET_SENSE_API_DESC_FN_NAME_STR "') failed for library '" + libraryPath + "'"); } // Check if the static threading model obj is null and initialize if needed if (!smoThreadingModelDesc.componentThread) { smoThreadingModelDesc.componentThread = componentThread; } const SenseApiDesc &libApiDesc = func( smoCallbacks, smoThreadingModelDesc); auto lib = std::make_shared( libraryPath, dlopen_handle.release(), func); lib->setSenseApiDesc(libApiDesc); senseApiLibs.push_back(lib); return *senseApiLibs.back(); } std::optional> SenseApiManager::getSenseApiLib(const std::string& libraryPath) { auto it = std::find_if(senseApiLibs.begin(), senseApiLibs.end(), [&libPath = libraryPath](const std::shared_ptr& lib) { return lib->libraryPath == libPath; } ); if (it != senseApiLibs.end()) { return *it; } return std::nullopt; } std::optional> SenseApiManager::getSenseApiLibByApiName(const std::string& apiName) { auto it = std::find_if(senseApiLibs.begin(), senseApiLibs.end(), [&apiName](const std::shared_ptr& 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::shared_ptr& 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( std::shared_ptr& componentThread ) { const auto& options = OptionParser::getOptions(); for (const auto& libPath : options.senseApiLibs) { loadSenseApiLib(libPath, componentThread); } } 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::attachSenseDeviceReq( const std::shared_ptr& spec, attachSenseDeviceReqCbFn cb ) { /** FIXME: * We should acquire a spinlock here to ensure that the device isn't added * in the interim while the async op executes. */ 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(); 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, cb); } void SenseApiManager::detachSenseDeviceReq( const std::shared_ptr& spec, detachSenseDeviceReqCbFn cb ) { /** FIXME: * We should acquire a spinlock here to ensure that the device isn't removed * in the interim while the async op executes. */ 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(); 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, cb); } class SenseApiManager::AttachAllSenseDevicesFromSpecsReq : public AsynchronousContinuation { public: AttachAllSenseDevicesFromSpecsReq( const unsigned int totalNSpecs, attachAllSenseDevicesFromSpecsReqCbFn cb) : AsynchronousContinuation(std::move(cb)), loop(totalNSpecs) {} // Callback methods for the attachment sequence void attachAllSenseDevicesFromSpecsReq1( std::shared_ptr context, bool success, std::shared_ptr spec ) { if (!success) { std::cerr << __func__ << ": Failed to attach device: " << spec->deviceIdentifier << "\n"; } if (!context->loop.incrementSuccessOrFailureAndTestForCompletionDueTo( success)) { return; } if (OptionParser::getOptions().verbose) { std::cout << __func__ << ": " << context->loop.nSucceeded.load() << " devices attached, " << context->loop.nFailed.load() << " devices failed\n"; } context->originalCbFn(context->loop); } public: AsynchronousLoop loop; }; void SenseApiManager::attachAllSenseDevicesFromSpecsReq( attachAllSenseDevicesFromSpecsReqCbFn cb ) { // Create the attachment request object to hold state and callbacks auto request = std::make_shared( device::DeviceManager::deviceAttachmentSpecs.size(), std::move(cb)); if (request->loop.nTotalIsZero()) { cb(request->loop); return; } for (const auto& spec : device::DeviceManager::deviceAttachmentSpecs) { try { attachSenseDeviceReq( spec, std::bind( &AttachAllSenseDevicesFromSpecsReq::attachAllSenseDevicesFromSpecsReq1, request.get(), request, std::placeholders::_1, std::placeholders::_2)); } catch (const std::exception& e) { std::cerr << __func__ << ": Exception: " << e.what() << "\n"; if (request->loop.incrementSuccessOrFailureAndTestForCompletionDueTo(false)) { cb(request->loop); } } } } class SenseApiManager::DetachAllSenseDevicesReq : public AttachAllSenseDevicesFromSpecsReq { public: using AttachAllSenseDevicesFromSpecsReq::AttachAllSenseDevicesFromSpecsReq; void detachAllSenseDevicesReq1( std::shared_ptr context, bool success, std::shared_ptr spec ) { if (!success) { std::cerr << __func__ << ": Failed to detach device: " << spec->deviceIdentifier << "\n"; } if (!context->loop.incrementSuccessOrFailureAndTestForCompletionDueTo( success)) { return; } if (OptionParser::getOptions().verbose) { std::cout << __func__ << ": " << context->loop.nSucceeded.load() << " devices detached, " << context->loop.nFailed.load() << " devices failed\n"; } context->originalCbFn(context->loop); } }; void SenseApiManager::detachAllSenseDevicesReq( detachAllSenseDevicesReqCbFn cb ) { auto request = std::make_shared( device::DeviceManager::deviceAttachmentSpecs.size(), std::move(cb)); if (request->loop.nTotalIsZero()) { cb(request->loop); return; } for (const auto& spec : device::DeviceManager::deviceAttachmentSpecs) { try { detachSenseDeviceReq( spec, std::bind( &DetachAllSenseDevicesReq::detachAllSenseDevicesReq1, request.get(), request, std::placeholders::_1, std::placeholders::_2)); } catch (const std::exception& e) { std::cerr << __func__ << ": Exception: " << e.what() << "\n"; if (request->loop.incrementSuccessOrFailureAndTestForCompletionDueTo(false)) { cb(request->loop); } } } } } // namespace sense_api } // namespace smo