plugins: add plugin manager

Change-Id: I2946387c8f30151a9ce2b979e5d395600bfa7dae
This commit is contained in:
ayounes
2020-02-03 09:20:52 -05:00
committed by Adrien Béraud
parent 2e1fa4ff49
commit 0bef03f191
16 changed files with 1602 additions and 5 deletions

View File

@ -208,6 +208,7 @@ namespace std {
%include "datatransfer.i"
%include "presencemanager.i"
%include "videomanager.i"
%include "plugin_manager_interface.i"
#include "dring/callmanager_interface.h"

View File

@ -0,0 +1,20 @@
%header %{
#include "dring/dring.h"
#include "dring/plugin_manager_interface.h"
%}
namespace DRing {
bool loadPlugin(const std::string& path);
bool unloadPlugin(const std::string& path);
void togglePlugin(const std::string& path, bool toggle);
std::map<std::string,std::string> getPluginDetails(const std::string& path);
std::vector<std::map<std::string,std::string>> getPluginPreferences(const std::string& path);
bool setPluginPreference(const std::string& path, const std::string& key, const std::string& value);
std::map<std::string,std::string> getPluginPreferencesValues(const std::string& path);
bool resetPluginPreferencesValues(const std::string& path);
std::vector<std::string> listAvailablePlugins();
std::vector<std::string> listLoadedPlugins();
int installPlugin(const std::string& jplPath, bool force);
int uninstallPlugin(const std::string& pluginRootPath);
}

View File

@ -161,7 +161,15 @@ libring_la_SOURCES = \
generic_io.h \
scheduled_executor.h \
scheduled_executor.cpp \
transport/peer_channel.h
transport/peer_channel.h \
plugin/pluginloader.h \
plugin/pluginloaderdl.cpp \
plugin/pluginmanager.h \
plugin/pluginmanager.cpp \
plugin/mediahandler.h \
plugin/callservicemanager.h \
plugin/jamipluginmanager.h \
plugin/jamipluginmanager.cpp
if HAVE_WIN32
libring_la_SOURCES += \

View File

@ -17,6 +17,7 @@ libclient_la_SOURCES = \
callmanager.cpp \
configurationmanager.cpp \
datatransfer.cpp \
plugin_manager_interface.cpp \
$(PRESENCE_SRC) \
$(VIDEO_SRC)

View File

@ -0,0 +1,83 @@
/*
* Copyright (C) 2004-2020 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "plugin_manager_interface.h"
#include "manager.h"
#include "plugin/jamipluginmanager.h"
#include "logger.h"
#include <iostream>
namespace DRing {
//
bool
loadPlugin(const std::string& path){
return jami::Manager::instance().getJamiPluginManager().loadPlugin(path);
}
bool
unloadPlugin(const std::string& path){
return jami::Manager::instance().getJamiPluginManager().unloadPlugin(path);
}
void
togglePlugin(const std::string& path, bool toggle){
jami::Manager::instance().getJamiPluginManager().togglePlugin(path,toggle);
}
std::map<std::string,std::string>
getPluginDetails(const std::string& path){
return jami::Manager::instance().getJamiPluginManager().getPluginDetails(path);
}
std::vector<std::map<std::string,std::string>>
getPluginPreferences(const std::string& path){
return jami::Manager::instance().getJamiPluginManager().getPluginPreferences(path);
}
bool
setPluginPreference(const std::string& path, const std::string& key, const std::string& value) {
return jami::Manager::instance().getJamiPluginManager().setPluginPreference(path, key, value);
}
std::map<std::string,std::string>
getPluginPreferencesValues(const std::string& path){
return jami::Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(path);
}
bool
resetPluginPreferencesValues(const std::string& path){
return jami::Manager::instance().getJamiPluginManager().resetPluginPreferencesValuesMap(path);
}
std::vector<std::string>
listAvailablePlugins() {
return jami::Manager::instance().getJamiPluginManager().listAvailablePlugins();
}
std::vector<std::string>
listLoadedPlugins() {
return jami::Manager::instance().getJamiPluginManager().listLoadedPlugins();
}
int installPlugin(const std::string& jplPath, bool force) {
return jami::Manager::instance().getJamiPluginManager().installPlugin(jplPath, force);
}
int uninstallPlugin(const std::string& pluginRootPath) {
return jami::Manager::instance().getJamiPluginManager().uninstallPlugin(pluginRootPath);
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2004-2020 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "def.h"
#include <string>
#include <vector>
#include <map>
namespace DRing {
DRING_PUBLIC bool loadPlugin(const std::string& path);
DRING_PUBLIC bool unloadPlugin(const std::string& path);
DRING_PUBLIC void togglePlugin(const std::string& path, bool toggle);
DRING_PUBLIC std::map<std::string,std::string> getPluginDetails(const std::string& path);
DRING_PUBLIC std::vector<std::map<std::string,std::string>> getPluginPreferences(const std::string& path);
DRING_PUBLIC bool setPluginPreference(const std::string& path, const std::string& key, const std::string& value);
DRING_PUBLIC std::map<std::string,std::string> getPluginPreferencesValues(const std::string& path);
DRING_PUBLIC bool resetPluginPreferencesValues(const std::string& path);
DRING_PUBLIC std::vector<std::string> listAvailablePlugins();
DRING_PUBLIC std::vector<std::string> listLoadedPlugins();
DRING_PUBLIC int installPlugin(const std::string& jplPath, bool force);
DRING_PUBLIC int uninstallPlugin(const std::string& pluginRootPath);
}

View File

@ -46,12 +46,14 @@
#ifndef _WIN32
#include <sys/stat.h> // mode_t
#define DIR_SEPARATOR_STR "/" // Directory separator char
#define DIR_SEPARATOR_CH '/' // Directory separator string
#define DIR_SEPARATOR_STR "/" // Directory separator string
#define DIR_SEPARATOR_CH '/' // Directory separator char
#define DIR_SEPARATOR_STR_ESC "\\/" // Escaped directory separator string
#else
#define mode_t unsigned
#define DIR_SEPARATOR_STR "\\" // Directory separator char
#define DIR_SEPARATOR_CH '\\' // Directory separator string
#define DIR_SEPARATOR_STR "\\" // Directory separator string
#define DIR_SEPARATOR_CH '\\' // Directory separator char
#define DIR_SEPARATOR_STR_ESC "\\\\" // Escaped directory separator string
#endif
namespace jami { namespace fileutils {

View File

@ -63,6 +63,7 @@ using random_device = dht::crypto::random_device;
#include "audio/sound/tonelist.h"
#include "audio/sound/dtmf.h"
#include "audio/ringbufferpool.h"
#include "plugin/jamipluginmanager.h"
#ifdef ENABLE_VIDEO
#include "client/videomanager.h"
@ -415,6 +416,8 @@ struct Manager::ManagerPimpl
#endif
std::unique_ptr<SIPVoIPLink> sipLink_;
/* Jami Plugin Manager */
JamiPluginManager jami_plugin_manager;
};
Manager::ManagerPimpl::ManagerPimpl(Manager& base)
@ -3099,6 +3102,11 @@ Manager::sipVoIPLink() const
}
JamiPluginManager& Manager::getJamiPluginManager() const
{
return pimpl_->jami_plugin_manager;
}
std::map<std::string, std::string>
Manager::getNearbyPeers(const std::string& accountID)
{

View File

@ -61,6 +61,7 @@ class IceTransportFactory;
class DataTransferFacade;
class JamiAccount;
class SIPVoIPLink;
class JamiPluginManager;
static constexpr uint64_t DRING_ID_MAX_VAL = 9007199254740992;
@ -904,6 +905,7 @@ class DRING_TESTABLE Manager {
std::vector<DRing::Message> getLastMessages(const std::string& accountID, const uint64_t& base_timestamp);
SIPVoIPLink& sipVoIPLink() const;
JamiPluginManager& getJamiPluginManager() const;
private:
Manager();

109
src/plugin/jamiplugin.h Normal file
View File

@ -0,0 +1,109 @@
/*
* Copyright (C) 2004-2019 Savoir-faire Linux Inc.
*
* Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA.
*/
#pragma once
#include <inttypes.h>
#ifdef __cplusplus
#define EXTERNAL_C_LINKAGE extern "C"
#define C_INTERFACE_START EXTERNAL_C_LINKAGE {
#define C_INTERFACE_END }
#else
#define C_LINKAGE
#define C_INTERFACE_START
#define C_INTERFACE_END
#endif
#define JAMI_PLUGIN_ABI_VERSION 1 /* 0 doesn't exist, considered as error */
#define JAMI_PLUGIN_API_VERSION 1 /* 0 doesn't exist, considered as error */
C_INTERFACE_START;
typedef struct JAMI_PluginVersion {
/* plugin is not loadable if this number differs from one
* stored in the plugin loader */
uint32_t abi;
/* a difference on api number may be acceptable, see the loader code */
uint32_t api;
} JAMI_PluginVersion;
struct JAMI_PluginAPI;
/* JAMI_PluginCreateFunc parameters */
typedef struct JAMI_PluginObjectParams {
const JAMI_PluginAPI *pluginApi; /* this API */
const char *type;
} JAMI_PluginObjectParams;
typedef void *(*JAMI_PluginCreateFunc)(JAMI_PluginObjectParams *params,
void *closure);
typedef void (*JAMI_PluginDestroyFunc)(void *object, void *closure);
/* JAMI_PluginAPI.registerObjectFactory data */
typedef struct JAMI_PluginObjectFactory {
JAMI_PluginVersion version;
void *closure; /* closure for create */
JAMI_PluginCreateFunc create;
JAMI_PluginDestroyFunc destroy;
} JAMI_PluginObjectFactory;
/* Plugins exposed API prototype */
typedef int32_t (*JAMI_PluginFunc)(const JAMI_PluginAPI *api, const char *name,
void *data);
/* JAMI_PluginInitFunc parameters.
* This structure is filled by the Plugin manager.
* For backware compatibility, never c
*/
typedef struct JAMI_PluginAPI {
JAMI_PluginVersion version; /* structure version, always the first data */
void *context; /* opaque structure used by next functions */
/* API usable by plugin implementors */
JAMI_PluginFunc registerObjectFactory;
JAMI_PluginFunc invokeService;
JAMI_PluginFunc manageComponent;
} JAMI_PluginAPI;
typedef void (*JAMI_PluginExitFunc)(void);
typedef JAMI_PluginExitFunc (*JAMI_PluginInitFunc)(const JAMI_PluginAPI *api);
C_INTERFACE_END;
#define JAMI_DYN_INIT_FUNC_NAME "JAMI_dynPluginInit"
#define JAMI_PLUGIN_INIT_STATIC(fname, pname) JAMI_PLUGIN_INIT(fname, pname)
#define JAMI_PLUGIN_INIT_DYNAMIC(pname) \
JAMI_PLUGIN_INIT(JAMI_dynPluginInit, pname)
/* Define here platform dependent way to export a declaration x to the dynamic
* loading system.
*/
/* Default case (like POSIX/.so) */
#define JAMI_PLUGIN_INIT(fname, pname) \
(EXTERNAL_C_LINKAGE JAMI_PluginExitFunc fname(const JAMI_PluginAPI *pname))
#define JAMI_PLUGIN_EXIT(fname) (EXTERNAL_C_LINKAGE void fname(void))

View File

@ -0,0 +1,452 @@
/*
* Copyright (C) 2004-2020 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "jamipluginmanager.h"
#include "logger.h"
#include <sstream>
#include <fstream>
#include <regex>
#include <stdexcept>
//Manager
#include "manager.h"
extern "C" {
#include <archive.h>
}
#include <json/json.h>
#include <msgpack.hpp>
#if defined(__arm__)
#if defined(__ARM_ARCH_7A__)
#define ABI "armeabi-v7a"
#else
#define ABI "armeabi"
#endif
#elif defined(__i386__)
#define ABI "x86"
#elif defined(__x86_64__)
#define ABI "x86_64"
#elif defined(__mips64) /* mips64el-* toolchain defines __mips__ too */
#define ABI "mips64"
#elif defined(__mips__)
#define ABI "mips"
#elif defined(__aarch64__)
#define ABI "arm64-v8a"
#else
#define ABI "unknown"
#endif
#define PLUGIN_ALREADY_INSTALLED 100 /* Plugin already installed with the same version */
#define PLUGIN_OLD_VERSION 200 /* Plugin already installed with a newer version */
namespace jami {
std::map<std::string, std::string> checkManifestJsonContentValidity(const Json::Value& root) {
std::string name = root.get("name", "").asString();
std::string description = root.get("description", "").asString();
std::string version = root.get("version", "").asString();
if(!name.empty() || !version.empty()){
return {
{"name", name},
{"description", description},
{"version", version},
};
} else {
throw std::runtime_error("plugin manifest file: bad format");
}
}
std::map<std::string, std::string> checkManifestValidity(std::istream& stream) {
Json::Value root;
Json::CharReaderBuilder rbuilder;
rbuilder["collectComments"] = false;
std::string errs;
bool ok = Json::parseFromStream(rbuilder, stream, &root, &errs);
if(ok) {
return checkManifestJsonContentValidity(root);
} else{
throw std::runtime_error("failed to parse the plugin manifest file");
}
}
std::map<std::string, std::string> checkManifestValidity(const std::vector<uint8_t>& vec) {
Json::Value root;
std::unique_ptr<Json::CharReader> json_Reader(Json::CharReaderBuilder{}.newCharReader());
std::string errs;
bool ok = json_Reader->parse(reinterpret_cast<const char *>(vec.data()),
reinterpret_cast<const char *>(vec.data()+vec.size()),
&root,&errs);
if(ok) {
return checkManifestJsonContentValidity(root);
} else{
throw std::runtime_error("failed to parse the plugin manifest file");
}
}
static const std::regex DATA_REGEX("^data" DIR_SEPARATOR_STR_ESC ".+");
static const std::regex SO_REGEX("([a-z0-9]+(?:[_-]?[a-z0-9]+)*)" DIR_SEPARATOR_STR_ESC "([a-z0-9_]+\\.(so|dll))");
std::pair<bool,const std::string>
uncompressJplFunction(const std::string& relativeFileName)
{
std::smatch match;
if (relativeFileName == "manifest.json" || std::regex_match(relativeFileName, DATA_REGEX)){
return std::make_pair(true, relativeFileName);
} else if (regex_search(relativeFileName, match, SO_REGEX) == true) {
if (match.str(1) == ABI) {
return std::make_pair(true, match.str(2));
}
}
return std::make_pair(false, std::string{""});
}
std::string convertArrayToString(const Json::Value& jsonArray)
{
std::string stringArray = "[";
for(int i=0; i< static_cast<int>(jsonArray.size()) - 1; i++) {
if(jsonArray[i].isString()) {
stringArray+=jsonArray[i].asString()+",";
} else if(jsonArray[i].isArray()) {
stringArray+=convertArrayToString(jsonArray[i])+",";
}
}
int lastIndex = static_cast<int>(jsonArray.size()) - 1;
if(jsonArray[lastIndex].isString()) {
stringArray+=jsonArray[lastIndex].asString();
}
stringArray+="]";
return stringArray;
}
std::map<std::string, std::string> parsePreferenceConfig(const Json::Value &jsonPreference, const std::string &type)
{
std::map<std::string, std::string> preferenceMap;
const auto& members = jsonPreference.getMemberNames();
// Insert other fields
for(const auto& member : members) {
const Json::Value& value = jsonPreference[member];
if(value.isString()) {
preferenceMap.emplace(member, jsonPreference[member].asString());
} else if (value.isArray()) {
preferenceMap.emplace(member, convertArrayToString(jsonPreference[member]));
}
}
return preferenceMap;
}
std::map<std::string, std::string> JamiPluginManager::getPluginDetails(const std::string &rootPath)
{
auto detailsIt = pluginDetailsMap_.find(rootPath);
if (detailsIt != pluginDetailsMap_.end()) {
return detailsIt->second;
}
std::map<std::string, std::string> details = parseManifestFile(manifestPath(rootPath));
details["iconPath"] = rootPath + DIR_SEPARATOR_CH + "data" + DIR_SEPARATOR_CH + "icon.png";
details["soPath"] = rootPath + DIR_SEPARATOR_CH + "lib" + details["name"] + ".so";
detailsIt = pluginDetailsMap_.emplace(rootPath, std::move(details)).first;
return detailsIt->second;
}
std::vector<std::string> JamiPluginManager::listAvailablePlugins()
{
std::string pluginsPath = fileutils::get_data_dir() + DIR_SEPARATOR_CH + "plugins";
std::vector<std::string> pluginsPaths = fileutils::readDirectory(pluginsPath);
std::for_each(pluginsPaths.begin(), pluginsPaths.end(),
[&pluginsPath](std::string& x){ x = pluginsPath + DIR_SEPARATOR_CH + x;});
auto predicate = [this](std::string path){ return !checkPluginValidity(path);};
auto returnIterator = std::remove_if(pluginsPaths.begin(),pluginsPaths.end(),predicate);
pluginsPaths.erase(returnIterator,std::end(pluginsPaths));
return pluginsPaths;
}
int JamiPluginManager::installPlugin(const std::string &jplPath, bool force)
{
int r{0};
if(fileutils::isFile(jplPath)) {
try{
auto manifestMap = readPluginManifestFromArchive(jplPath);
std::string name = manifestMap["name"];
std::string version = manifestMap["version"];
const std::string destinationDir{fileutils::get_data_dir()
+ DIR_SEPARATOR_CH + "plugins"
+ DIR_SEPARATOR_CH + name};
// Find if there is an existing version of this plugin
const auto alreadyInstalledManifestMap = parseManifestFile(manifestPath(destinationDir));
if (!alreadyInstalledManifestMap.empty()) {
if (force) {
r = uninstallPlugin(destinationDir);
if(r == 0) {
archiver::uncompressArchive(jplPath, destinationDir, uncompressJplFunction);
}
} else {
std::string installedVersion = alreadyInstalledManifestMap.at("version");
if (version > installedVersion) {
r = uninstallPlugin(destinationDir);
if(r == 0) {
archiver::uncompressArchive(jplPath, destinationDir, uncompressJplFunction);
}
} else if (version == installedVersion){
// An error code of 100 to know that this version is the same as the one installed
r = PLUGIN_ALREADY_INSTALLED;
} else {
// An error code of 100 to know that this version is older than the one installed
r = PLUGIN_OLD_VERSION;
}
}
} else {
archiver::uncompressArchive(jplPath, destinationDir, uncompressJplFunction);
}
} catch(const std::exception& e) {
JAMI_ERR() << e.what();
}
}
return r;
}
int JamiPluginManager::uninstallPlugin(const std::string &rootPath)
{
if(checkPluginValidity(rootPath)) {
return fileutils::removeAll(rootPath);
} else {
return -1;
}
}
bool JamiPluginManager::loadPlugin(const std::string &rootPath)
{
try {
return pm_.load(getPluginDetails(rootPath).at("soPath"));
} catch(const std::exception& e) {
JAMI_ERR() << e.what();
return false;
}
}
bool JamiPluginManager::unloadPlugin(const std::string &rootPath)
{
try {
return pm_.unload(getPluginDetails(rootPath).at("soPath"));
} catch(const std::exception& e) {
JAMI_ERR() << e.what();
return false;
}
}
void JamiPluginManager::togglePlugin(const std::string &rootPath, bool toggle)
{
try {
std::string soPath = getPluginDetails(rootPath).at("soPath");
// remove the previous plugin object if it was registered
pm_.destroyPluginComponents(soPath);
// If toggle, register a new instance of the plugin
// function
if(toggle){
pm_.callPluginInitFunction(soPath);
}
} catch (const std::exception& e) {
JAMI_ERR() << e.what();
}
}
std::vector<std::string> JamiPluginManager::listLoadedPlugins() const
{
std::vector<std::string> loadedSoPlugins = pm_.listLoadedPlugins();
std::vector<std::string> loadedPlugins{};
loadedPlugins.reserve(loadedSoPlugins.size());
std::transform(loadedSoPlugins.begin(), loadedSoPlugins.end(), std::back_inserter(loadedPlugins),
[this](const std::string& soPath) {
return getRootPathFromSoPath(soPath);
});
return loadedPlugins;
}
std::vector<std::map<std::string, std::string> > JamiPluginManager::getPluginPreferences(const std::string &rootPath)
{
const std::string preferenceFilePath = getPreferencesConfigFilePath(rootPath);
std::ifstream file(preferenceFilePath);
Json::Value root;
Json::CharReaderBuilder rbuilder;
rbuilder["collectComments"] = false;
std::string errs;
std::set<std::string> keys;
std::vector<std::map<std::string, std::string>> preferences;
if(file) {
bool ok = Json::parseFromStream(rbuilder, file, &root, &errs);
if(ok && root.isArray()) {
for(int i=0; i< static_cast<int>(root.size()); i++) {
const Json::Value jsonPreference = root[i];
std::string category = jsonPreference.get("category", "NoCategory").asString();
std::string type = jsonPreference.get("type", "None").asString();
std::string key = jsonPreference.get("key", "None").asString();
if(type != "None" && key != "None") {
if(keys.find(key) == keys.end()) {
const auto& preferenceAttributes = parsePreferenceConfig(jsonPreference, type);
// If the parsing of the attributes was successful, commit the map and the key
if(!preferenceAttributes.empty()) {
preferences.push_back(std::move(preferenceAttributes));
keys.insert(key);
}
}
}
}
} else {
JAMI_ERR() << "PluginPreferencesParser:: Failed to parse preferences.json for plugin: "
<< preferenceFilePath;
}
}
return preferences;
}
bool JamiPluginManager::setPluginPreference(const std::string &rootPath, const std::string &key, const std::string &value)
{
bool returnValue = true;
std::map<std::string, std::string> pluginPreferencesMap = getPluginPreferencesValuesMap(rootPath);
// Using [] instead of insert to get insert or update effect
pluginPreferencesMap[key] = value;
{
const std::string preferencesValuesFilePath = pluginPreferencesValuesFilePath(rootPath);
std::ofstream fs(preferencesValuesFilePath, std::ios::binary);
if(!fs.good()) {
return false;
}
try {
std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferencesValuesFilePath));
msgpack::pack(fs, pluginPreferencesMap);
} catch (const std::exception& e) {
returnValue = false;
JAMI_ERR() << e.what();
}
}
return returnValue;
}
std::map<std::string, std::string> JamiPluginManager::getPluginPreferencesValuesMap(const std::string &rootPath)
{
const std::string preferencesValuesFilePath = pluginPreferencesValuesFilePath(rootPath);
std::ifstream file(preferencesValuesFilePath, std::ios::binary);
std::map<std::string, std::string> rmap;
// If file is accessible
if(file.good()) {
std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferencesValuesFilePath));
// Get file size
std::string str;
file.seekg(0, std::ios::end);
size_t fileSize = static_cast<size_t>(file.tellg());
// If not empty
if(fileSize > 0) {
// Read whole file content and put it in the string str
str.reserve(static_cast<size_t>(file.tellg()));
file.seekg(0, std::ios::beg);
str.assign((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
file.close();
try {
// Unpack the string
msgpack::object_handle oh = msgpack::unpack(str.data(), str.size());
// Deserialized object is valid during the msgpack::object_handle instance is alive.
msgpack::object deserialized = oh.get();
deserialized.convert(rmap);
} catch (const std::exception& e) {
JAMI_ERR() << e.what();
}
}
}
return rmap;
}
bool JamiPluginManager::resetPluginPreferencesValuesMap(const std::string &rootPath)
{
bool returnValue = true;
std::map<std::string, std::string> pluginPreferencesMap{};
{
const std::string preferencesValuesFilePath = pluginPreferencesValuesFilePath(rootPath);
std::ofstream fs(preferencesValuesFilePath, std::ios::binary);
if(!fs.good()) {
return false;
}
try {
std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferencesValuesFilePath));
msgpack::pack(fs, pluginPreferencesMap);
} catch (const std::exception& e) {
returnValue = false;
JAMI_ERR() << e.what();
}
}
return returnValue;
}
std::map<std::string, std::string> JamiPluginManager::readPluginManifestFromArchive(const std::string &jplPath)
{
try {
return checkManifestValidity(archiver::readFileFromArchive(jplPath,"manifest.json"));
} catch (const std::exception& e) {
JAMI_ERR() << e.what();
}
}
std::map<std::string, std::string> JamiPluginManager::parseManifestFile(const std::string &manifestFilePath)
{
std::ifstream file(manifestFilePath);
if(file) {
try {
return checkManifestValidity(file);
} catch (const std::exception& e) {
JAMI_ERR() << e.what();
}
}
return {};
}
void JamiPluginManager::registerServices()
{
// Register pluginPreferences
pm_.registerService("getPluginPreferences", [this](const DLPlugin* plugin, void* data) {
auto ppp = static_cast<std::map<std::string, std::string>*>(data);
*ppp = getPluginPreferencesValuesMap(
getRootPathFromSoPath(plugin->getPath()));
return 0;
});
pm_.registerService("getPluginDataPath", [this](const DLPlugin* plugin, void* data) {
auto dataPath_ = static_cast<std::string*>(data);
dataPath_->assign(dataPath(plugin->getPath()));
return 0;
});
}
}

View File

@ -0,0 +1,195 @@
/*
* Copyright (C) 2004-2020 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "noncopyable.h"
#include "fileutils.h"
#include "archiver.h"
#include "pluginmanager.h"
#include <vector>
#include <map>
#include <string>
#include <algorithm>
namespace jami {
class JamiPluginManager
{
public:
JamiPluginManager() {
registerServices();
}
// TODO : improve getPluginDetails
/**
* @brief getPluginDetails
* Parses a manifest file and returns :
* The tuple (name, description, version, icon path, so path)
* The icon should ideally be 192x192 pixels or better 512x512 pixels
* In order to match with android specifications
* https://developer.android.com/google-play/resources/icon-design-specifications
* Saves the result in a map
* @param plugin rootPath (folder of the plugin)
* @return map where the keyset is {"name", "description", "iconPath"}
*/
std::map<std::string, std::string> getPluginDetails(const std::string& rootPath);
/**
* @brief listAvailablePlugins
* Lists available plugins with valid manifest files
* @return list of plugin directory names
*/
std::vector<std::string> listAvailablePlugins();
/**
* @brief installPlugin
* Checks if the plugin has a valid manifest, installs the plugin if not previously installed
* or if installing a newer version of it
* If force is true, we force install the plugin
* @param jplPath
* @param force
* @return + 0 if success
* 100 if already installed with similar version
* 200 if already installed with newer version
* libarchive error codes otherwise
*/
int installPlugin(const std::string& jplPath, bool force);
/**
* @brief uninstallPlugin
* Checks if the plugin has a valid manifest then removes plugin folder
* @param rootPath
* @return 0 if success
*/
int uninstallPlugin(const std::string& rootPath);
/**
* @brief loadPlugin
* @param rootPath of the plugin folder
* @return true is success
*/
bool loadPlugin(const std::string& rootPath);
/**
* @brief unloadPlugin
* @param rootPath of the plugin folder
* @return true is success
*/
bool unloadPlugin(const std::string& rootPath);
/**
* @brief togglePlugin
* @param rootPath of the plugin folder
* @param toggle: if true, register a new instance of the plugin
* else, remove the existing instance
* N.B: before adding a new instance, remove any existing one
*/
void togglePlugin(const std::string& rootPath, bool toggle);
/**
* @brief listLoadedPlugins
* @return vector of rootpaths of the loaded plugins
*/
std::vector<std::string> listLoadedPlugins() const;
std::vector<std::map<std::string,std::string>> getPluginPreferences(const std::string& rootPath);
bool setPluginPreference(const std::string& rootPath,
const std::string& key,
const std::string& value);
std::map<std::string,std::string>
getPluginPreferencesValuesMap(const std::string& rootPath);
bool resetPluginPreferencesValuesMap(const std::string& rootPath);
private:
NON_COPYABLE(JamiPluginManager);
/**
* @brief checkPluginValidity
* Checks if the plugin has a manifest file with a name and a version
* @return true if valid
*/
bool checkPluginValidity(const std::string& rootPath) {
return !parseManifestFile(manifestPath(rootPath)).empty();
}
/**
* @brief readPluginManifestFromArchive
* Reads the manifest file content without uncompressing the whole archive
* Maps the manifest data to a map(string, string)
* @param jplPath
* @return manifest map
*/
std::map<std::string, std::string> readPluginManifestFromArchive(const std::string &jplPath);
/**
* @brief parseManifestFile, parses the manifest file of an installed plugin
* @param manifestFilePath
* @return manifest map
*/
std::map<std::string, std::string> parseManifestFile(const std::string &manifestFilePath);
std::string manifestPath(const std::string& rootPath) {
return rootPath + DIR_SEPARATOR_CH + "manifest.json";
}
std::string getRootPathFromSoPath(const std::string& soPath) const {
return soPath.substr(0,soPath.find_last_of(DIR_SEPARATOR_CH));
}
std::string manifestPath(const std::string& rootPath) const {
return rootPath + DIR_SEPARATOR_CH + "manifest.json";
}
std::string dataPath(const std::string& pluginSoPath) const {
return getRootPathFromSoPath(pluginSoPath) + DIR_SEPARATOR_CH + "data";
}
/**
* @brief getPreferencesConfigFilePath
* Returns the plugin preferences config file path from the plugin root path
* This is entirely defined by how the plugin files are structured
* @param plugin rootPath
* @return path of the preferences config
*/
std::string getPreferencesConfigFilePath(const std::string& rootPath) const {
return rootPath + DIR_SEPARATOR_CH + "data" + DIR_SEPARATOR_CH + "preferences.json";
}
/**
* @brief pluginPreferencesValuesFilePath
* Returns the plugin preferences values file path from the plugin root path
* This is entirely defined by how the plugin files are structured
* @param plugin rootPath
* @return path of the preferences values
*/
std::string pluginPreferencesValuesFilePath(const std::string& rootPath) const {
return rootPath + DIR_SEPARATOR_CH + "preferences.msgpack";
}
void registerServices();
private:
PluginManager pm_;
std::map<std::string, std::map<std::string, std::string>> pluginDetailsMap_;
};
}

80
src/plugin/pluginloader.h Normal file
View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2004-2020 Savoir-faire Linux Inc.
*
* Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA.
*/
#pragma once
#include "jamiplugin.h"
#include <dlfcn.h>
#include <string>
#include <memory>
namespace jami {
class Plugin {
public:
virtual ~Plugin() = default;
static Plugin *load(const std::string &path, std::string &error);
virtual void *getSymbol(const char *name) const = 0;
virtual JAMI_PluginInitFunc getInitFunction() const {
return reinterpret_cast<JAMI_PluginInitFunc>(
getSymbol(JAMI_DYN_INIT_FUNC_NAME));
}
protected:
Plugin() = default;
};
class DLPlugin : public Plugin {
public:
DLPlugin(void *handle, const std::string& path) : handle_(handle, ::dlclose), path_{path} {
api_.context = this;
}
virtual ~DLPlugin() { unload();}
bool unload() {
if(!handle_){
return false;
}
return ::dlclose(handle_.release());
}
void *getSymbol(const char *name) const {
if (!handle_)
return nullptr;
return ::dlsym(handle_.get(), name);
}
const std::string& getPath() const {
return path_;
}
public:
void* apiContext_;
JAMI_PluginAPI api_;
private:
std::unique_ptr<void, int (*)(void *)> handle_;
const std::string path_;
};
} // namespace jami

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2004-2018 Savoir-faire Linux Inc.
*
* Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA.
*/
#include "pluginloader.h"
namespace jami {
Plugin *Plugin::load(const std::string &path, std::string &error) {
if (path.empty()) {
error = "Empty path";
return nullptr;
}
// Clear any existing error
::dlerror();
void *handle = ::dlopen(path.c_str(), RTLD_NOW);
if (!handle) {
error += "Failed to load \"" + path + '"';
std::string dlError = ::dlerror();
if (dlError.size())
error += " (" + dlError + ")";
return nullptr;
}
return new DLPlugin(handle, path);
}
} // namespace jami

View File

@ -0,0 +1,355 @@
/*
* Copyright (C) 2004-2020 Savoir-faire Linux Inc.
*
* Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA.
*/
#include "pluginmanager.h"
#include "logger.h"
#include <utility>
namespace jami {
PluginManager::PluginManager() {
pluginApi_.context = reinterpret_cast<void *>(this);
}
PluginManager::~PluginManager() {
for (auto func : exitFuncVec_) {
try {
(*func)();
} catch (...) {
JAMI_ERR() << "Exception caught during plugin exit";
}
}
dynPluginMap_.clear();
exactMatchMap_.clear();
wildCardVec_.clear();
exitFuncVec_.clear();
}
bool PluginManager::load(const std::string &path) {
// TODO: Resolve symbolic links and make path absolute
// Don't load the same dynamic library twice
if (dynPluginMap_.find(path) != dynPluginMap_.end()) {
JAMI_WARN() << "Plugin: already loaded";
return true;
}
std::string error;
std::unique_ptr<Plugin> plugin(Plugin::load(path, error));
if (!plugin) {
JAMI_ERR() << "Plugin: " << error;
return false;
}
const auto &init_func = plugin->getInitFunction();
if (!init_func) {
JAMI_ERR() << "Plugin: no init symbol" << error;
return false;
}
if (!registerPlugin(plugin))
return false;
dynPluginMap_[path] = std::move(plugin);
return true;
}
bool PluginManager::unload(const std::string& path) {
bool returnValue{false};
destroyPluginComponents(path);
PluginMap::iterator it = dynPluginMap_.find(path);
if ( it != dynPluginMap_.end()) {
dynPluginMap_.erase(it);
returnValue = true;
}
return returnValue;
}
std::vector<std::string> PluginManager::listLoadedPlugins() const
{
std::vector<std::string> res{};
for(const auto& pair : dynPluginMap_) {
res.push_back(pair.first);
}
return res;
}
void PluginManager::destroyPluginComponents(const std::string &path)
{
auto itComponents = pluginComponentsMap_.find(path);
if(itComponents != pluginComponentsMap_.end()) {
for(const auto& pair : itComponents->second) {
auto clcm = componentsLifeCycleManagers_.find(pair.first);
if(clcm != componentsLifeCycleManagers_.end()) {
clcm->second.destroyComponent(pair.second);
}
}
}
}
bool PluginManager::callPluginInitFunction(const std::string &path){
bool returnValue{false};
PluginMap::iterator it = dynPluginMap_.find(path);
if ( it != dynPluginMap_.end()) {
// Plugin found
// Since the Plugin was found it is of type DLPlugin with a valid init symbol
std::shared_ptr<DLPlugin> plugin = std::static_pointer_cast<DLPlugin>(it->second);
const auto &initFunc = plugin->getInitFunction();
JAMI_PluginExitFunc exitFunc = nullptr;
try {
// Call Plugin Init function
exitFunc = initFunc(&plugin->api_);
} catch (const std::runtime_error &e) {
JAMI_ERR() << e.what();
return false;
}
if (!exitFunc) {
JAMI_ERR() << "Plugin: init failed";
returnValue = false;
} else {
returnValue = true;
}
}
return returnValue;
}
bool PluginManager::registerPlugin(std::unique_ptr<Plugin>& plugin) {
// Here we know that Plugin is of type DLPlugin with a valid init symbol
const auto &initFunc = plugin->getInitFunction();
JAMI_PluginExitFunc exitFunc = nullptr;
DLPlugin* pluginPtr = static_cast<DLPlugin*>(plugin.get());
pluginPtr->apiContext_ = this;
pluginPtr->api_.version = {JAMI_PLUGIN_ABI_VERSION, JAMI_PLUGIN_API_VERSION};
pluginPtr->api_.registerObjectFactory = registerObjectFactory_;
/**
* Implements JAMI_PluginAPI.invokeService().
* Must be C accessible.
*/
pluginPtr->api_.invokeService = [](const JAMI_PluginAPI *api,
const char *name, void *data) {
auto plugin = static_cast<DLPlugin*>(api->context);
auto manager = reinterpret_cast<PluginManager *>(plugin->apiContext_);
if (!manager) {
JAMI_ERR() << "invokeService called with null plugin API";
return -1;
}
return manager->invokeService(plugin, name, data);
};
/**
* Implements JAMI_PluginAPI.invokeService().
* Must be C accessible.
*/
pluginPtr->api_.manageComponent = [](const JAMI_PluginAPI* api, const char* name, void *data){
auto plugin = static_cast<DLPlugin*>(api->context);
auto manager = reinterpret_cast<PluginManager *>(plugin->apiContext_);
if (!manager) {
JAMI_ERR() << "createComponent called with null plugin API";
return -1;
} else if(!plugin){
JAMI_ERR() << "createComponent called with null context";
return -1;
}
return manager->manageComponent(plugin, name, data);
};
try {
exitFunc = initFunc(&pluginPtr->api_);
} catch (const std::runtime_error &e) {
JAMI_ERR() << e.what();
}
if (!exitFunc) {
tempExactMatchMap_.clear();
tempWildCardVec_.clear();
JAMI_ERR() << "Plugin: init failed";
return false;
}
exitFuncVec_.push_back(exitFunc);
exactMatchMap_.insert(tempExactMatchMap_.begin(), tempExactMatchMap_.end());
wildCardVec_.insert(wildCardVec_.end(), tempWildCardVec_.begin(),
tempWildCardVec_.end());
return true;
}
bool PluginManager::registerService(const std::string &name,
ServiceFunction &&func) {
services_[name] = std::forward<ServiceFunction>(func);
return true;
}
void PluginManager::unRegisterService(const std::string &name) {
services_.erase(name);
}
int32_t PluginManager::invokeService(const DLPlugin* plugin, const std::string &name, void *data) {
const auto &iterFunc = services_.find(name);
if (iterFunc == services_.cend()) {
JAMI_ERR() << "Services not found: " << name;
return -1;
}
const auto &func = iterFunc->second;
try {
return func(plugin, data);
} catch (const std::runtime_error &e) {
JAMI_ERR() << e.what();
return -1;
}
}
int32_t PluginManager::manageComponent(const DLPlugin* plugin, const std::string& name, void *data) {
const auto& iter = componentsLifeCycleManagers_.find(name);
if(iter == componentsLifeCycleManagers_.end()) {
JAMI_ERR() << "Component lifecycle manager not found: " << name;
return -1;
}
const auto& componentLifecycleManager = iter->second;
try {
int32_t r = componentLifecycleManager.takeComponentOwnership(data);
if(r == 0) {
pluginComponentsMap_[plugin->getPath()].emplace_back(name,data);
}
return r;
} catch(const std::runtime_error &e) {
JAMI_ERR() << e.what();
return -1;
}
}
/* WARNING: exposed to plugins through JAMI_PluginAPI */
bool PluginManager::registerObjectFactory(
const char *type, const JAMI_PluginObjectFactory &factoryData) {
if (!type)
return false;
if (!factoryData.create || !factoryData.destroy)
return false;
// Strict compatibility on ABI
if (factoryData.version.abi != pluginApi_.version.abi)
return false;
// Backward compatibility on API
if (factoryData.version.api < pluginApi_.version.api)
return false;
const std::string key(type);
auto deleter = [factoryData](void *o) {
factoryData.destroy(o, factoryData.closure);
};
ObjectFactory factory = {factoryData, deleter};
// wildcard registration?
if (key == "*") {
wildCardVec_.push_back(factory);
return true;
}
// fails on duplicate for exactMatch map
if (exactMatchMap_.find(key) != exactMatchMap_.end())
return false;
exactMatchMap_[key] = factory;
return true;
}
bool PluginManager::registerComponentManager(const std::string &name,
ComponentFunction &&takeOwnership,
ComponentFunction &&destroyComponent)
{
componentsLifeCycleManagers_[name] = {std::forward<ComponentFunction>(takeOwnership),
std::forward<ComponentFunction>(destroyComponent)};
return true;
}
std::unique_ptr<void, PluginManager::ObjectDeleter>
PluginManager::createObject(const std::string &type) {
if (type == "*")
return {nullptr, nullptr};
JAMI_PluginObjectParams op = {
/*.pluginApi = */ &pluginApi_,
/*.type = */ type.c_str(),
};
// Try to find an exact match
const auto &factoryIter = exactMatchMap_.find(type);
if (factoryIter != exactMatchMap_.end()) {
const auto &factory = factoryIter->second;
auto object = factory.data.create(&op, factory.data.closure);
if (object)
return {object, factory.deleter};
}
// Try to find a wildcard match
for (const auto &factory : wildCardVec_) {
auto object = factory.data.create(&op, factory.data.closure);
if (object) {
// promote registration to exactMatch_
// (but keep also wildcard registration for other object types)
int32_t res = registerObjectFactory(op.type, factory.data);
if (res < 0) {
JAMI_ERR() << "failed to register object " << op.type;
return {nullptr, nullptr};
}
return {object, factory.deleter};
}
}
return {nullptr, nullptr};
}
/* WARNING: exposed to plugins through JAMI_PluginAPI */
int32_t PluginManager::registerObjectFactory_(const JAMI_PluginAPI *api,
const char *type, void *data) {
auto manager = reinterpret_cast<PluginManager *>(api->context);
if (!manager) {
JAMI_ERR() << "registerObjectFactory called with null plugin API";
return -1;
}
if (!data) {
JAMI_ERR() << "registerObjectFactory called with null factory data";
return -1;
}
const auto factory = reinterpret_cast<JAMI_PluginObjectFactory *>(data);
return manager->registerObjectFactory(type, *factory) ? 0 : -1;
}
} // namespace jami

192
src/plugin/pluginmanager.h Normal file
View File

@ -0,0 +1,192 @@
/*
* Copyright (C) 2004-2020 Savoir-faire Linux Inc.
*
* Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA.
*/
#pragma once
#include "noncopyable.h"
#include "jamiplugin.h"
#include "pluginloader.h"
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include <inttypes.h>
namespace jami {
class Plugin;
class PluginManager {
public:
using ObjectDeleter = std::function<void(void *)>;
using ServiceFunction = std::function<int32_t(const DLPlugin*, void *)>;
using ComponentFunction = std::function<int32_t(void*)>;
//A vector to a pair<componentType, componentPtr>
using ComponentTypePtrVector = std::vector<std::pair<std::string, void*>>;
private:
struct ObjectFactory {
JAMI_PluginObjectFactory data;
ObjectDeleter deleter;
};
struct ComponentLifeCycleManager{
ComponentFunction takeComponentOwnership;
ComponentFunction destroyComponent;
};
using PluginMap = std::map<std::string, std::shared_ptr<Plugin>>;
using PluginComponentsMap = std::map<std::string, ComponentTypePtrVector>;
using ExitFuncVec = std::vector<JAMI_PluginExitFunc>;
using ObjectFactoryVec = std::vector<ObjectFactory>;
using ObjectFactoryMap = std::map<std::string, ObjectFactory>;
public:
PluginManager();
~PluginManager();
/**
* Load a dynamic plugin by filename.
*
* @param path fully qualified pathname on a loadable plugin binary
* @return true if success
*/
bool load(const std::string &path);
/**
* @brief unloads the plugin with pathname path
* @param path
* @return true if success
*/
bool unload(const std::string& path);
/**
* @brief listLoadedPlugins
* @return vector of strings of so files of the loaded plugins
*/
std::vector<std::string> listLoadedPlugins() const;
/**
* @brief destroyPluginComponents
* @param path
*/
void destroyPluginComponents(const std::string& path);
/**
* @brief callPluginInitFunction
* @param path: plugin path used as an id in the plugin map
* @return true if succes
*/
bool callPluginInitFunction(const std::string& path);
/**
* Register a plugin.
*
* @param initFunc plugin init function
* @return true if success
*/
bool registerPlugin(std::unique_ptr<Plugin>& plugin);
/**
* Register a new service for plugin.
*
* @param name The service name
* @param func The function called by Ring_PluginAPI.invokeService
* @return true if success
*/
bool registerService(const std::string &name, ServiceFunction &&func);
void unRegisterService(const std::string &name);
/**
* Register a new public objects factory.
*
* @param type unique identifier of the object
* @param params object factory details
* @return true if success
*
* Note: type can be the string "*" meaning that the factory
* will be called if no exact match factories are found for a given type.
*/
bool registerObjectFactory(const char *type,
const JAMI_PluginObjectFactory &factory);
/**
* @brief registerComponentManager
* Registers a component manager that will have two functions, one to take
* ownership of the component and the other one to destroy it
* @param name : name of the component manager
* @param takeOwnership function that takes ownership on created objet in memory
* @param destroyComponent desotry the component
* @return true if success
*/
bool registerComponentManager(const std::string& name, ComponentFunction&& takeOwnership,
ComponentFunction&& destroyComponent);
/**
* Create a new plugin's exported object.
*
* @param type unique identifier of the object to create.
* @return unique pointer on created object.
*/
std::unique_ptr<void, ObjectDeleter> createObject(const std::string &type);
private:
NON_COPYABLE(PluginManager);
/**
* Implements JAMI_PluginAPI.registerObjectFactory().
* Must be C accessible.
*/
static int32_t registerObjectFactory_(const JAMI_PluginAPI *api,
const char *type, void *data);
int32_t invokeService(const DLPlugin* plugin, const std::string &name, void *data);
int32_t manageComponent(const DLPlugin* plugin, const std::string& name, void *data);
std::mutex mutex_{};
JAMI_PluginAPI pluginApi_ = {
{JAMI_PLUGIN_ABI_VERSION, JAMI_PLUGIN_API_VERSION},
nullptr, // set by PluginManager constructor
registerObjectFactory_,
nullptr,
nullptr
};
PluginMap dynPluginMap_{}; // Only dynamic loaded plugins
ExitFuncVec exitFuncVec_{};
ObjectFactoryMap exactMatchMap_{};
ObjectFactoryVec wildCardVec_{};
// Storage used during plugin initialisation.
// Will be copied into previous ones only if the initialisation success.
ObjectFactoryMap tempExactMatchMap_{};
ObjectFactoryVec tempWildCardVec_{};
// registered services
std::map<std::string, ServiceFunction> services_{};
// registered component lifecycle managers
std::map<std::string, ComponentLifeCycleManager> componentsLifeCycleManagers_ {};
// references to plugins components, used for cleanup
PluginComponentsMap pluginComponentsMap_ {};
};
} // namespace jami