1 /** 2 * Orthanc - A Lightweight, RESTful DICOM Store 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics 4 * Department, University Hospital of Liege, Belgium 5 * Copyright (C) 2017-2021 Osimis S.A., Belgium 6 * 7 * This program is free software: you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation, either version 3 of the 10 * License, or (at your option) any later version. 11 * 12 * In addition, as a special exception, the copyright holders of this 13 * program give permission to link the code of its release with the 14 * OpenSSL project's "OpenSSL" library (or with modified versions of it 15 * that use the same license as the "OpenSSL" library), and distribute 16 * the linked executables. You must obey the GNU General Public License 17 * in all respects for all of the code used other than "OpenSSL". If you 18 * modify file(s) with this exception, you may extend this exception to 19 * your version of the file(s), but you are not obligated to do so. If 20 * you do not wish to do so, delete this exception statement from your 21 * version. If you delete this exception statement from all source files 22 * in the program, then also delete it here. 23 * 24 * This program is distributed in the hope that it will be useful, but 25 * WITHOUT ANY WARRANTY; without even the implied warranty of 26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 27 * General Public License for more details. 28 * 29 * You should have received a copy of the GNU General Public License 30 * along with this program. If not, see <http://www.gnu.org/licenses/>. 31 **/ 32 33 34 #include "../../Sources/PrecompiledHeadersServer.h" 35 #include "PluginsManager.h" 36 37 #if ORTHANC_ENABLE_PLUGINS != 1 38 #error The plugin support is disabled 39 #endif 40 41 #include "../../../OrthancFramework/Sources/HttpServer/HttpOutput.h" 42 #include "../../../OrthancFramework/Sources/Logging.h" 43 #include "../../../OrthancFramework/Sources/OrthancException.h" 44 #include "../../../OrthancFramework/Sources/Toolbox.h" 45 46 #include <cassert> 47 #include <memory> 48 #include <boost/filesystem.hpp> 49 50 #ifdef WIN32 51 #define PLUGIN_EXTENSION ".dll" 52 #elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) 53 #define PLUGIN_EXTENSION ".so" 54 #elif defined(__APPLE__) && defined(__MACH__) 55 #define PLUGIN_EXTENSION ".dylib" 56 #else 57 #error Support your platform here 58 #endif 59 60 61 namespace Orthanc 62 { Plugin(PluginsManager & pluginManager,const std::string & path)63 PluginsManager::Plugin::Plugin(PluginsManager& pluginManager, 64 const std::string& path) : 65 library_(path), 66 pluginManager_(pluginManager) 67 { 68 memset(&context_, 0, sizeof(context_)); 69 context_.pluginsManager = this; 70 context_.orthancVersion = ORTHANC_VERSION; 71 context_.Free = ::free; 72 context_.InvokeService = InvokeService; 73 } 74 75 CallInitialize(SharedLibrary & plugin,const OrthancPluginContext & context)76 static void CallInitialize(SharedLibrary& plugin, 77 const OrthancPluginContext& context) 78 { 79 typedef int32_t (*Initialize) (const OrthancPluginContext*); 80 81 #if defined(_WIN32) 82 Initialize initialize = (Initialize) plugin.GetFunction("OrthancPluginInitialize"); 83 #else 84 /** 85 * gcc would complain about "ISO C++ forbids casting between 86 * pointer-to-function and pointer-to-object" without the trick 87 * below, that is known as "the POSIX.1-2003 (Technical Corrigendum 88 * 1) workaround". See the man page of "dlsym()". 89 * http://www.trilithium.com/johan/2004/12/problem-with-dlsym/ 90 * http://stackoverflow.com/a/14543811/881731 91 **/ 92 93 Initialize initialize; 94 *(void **) (&initialize) = plugin.GetFunction("OrthancPluginInitialize"); 95 #endif 96 97 assert(initialize != NULL); 98 int32_t error = initialize(&context); 99 100 if (error != 0) 101 { 102 LOG(ERROR) << "Error while initializing plugin " << plugin.GetPath() 103 << " (code " << error << ")"; 104 throw OrthancException(ErrorCode_SharedLibrary); 105 } 106 } 107 108 CallFinalize(SharedLibrary & plugin)109 static void CallFinalize(SharedLibrary& plugin) 110 { 111 typedef void (*Finalize) (); 112 113 #if defined(_WIN32) 114 Finalize finalize = (Finalize) plugin.GetFunction("OrthancPluginFinalize"); 115 #else 116 Finalize finalize; 117 *(void **) (&finalize) = plugin.GetFunction("OrthancPluginFinalize"); 118 #endif 119 120 assert(finalize != NULL); 121 finalize(); 122 } 123 124 CallGetName(SharedLibrary & plugin)125 static const char* CallGetName(SharedLibrary& plugin) 126 { 127 typedef const char* (*GetName) (); 128 129 #if defined(_WIN32) 130 GetName getName = (GetName) plugin.GetFunction("OrthancPluginGetName"); 131 #else 132 GetName getName; 133 *(void **) (&getName) = plugin.GetFunction("OrthancPluginGetName"); 134 #endif 135 136 assert(getName != NULL); 137 return getName(); 138 } 139 140 CallGetVersion(SharedLibrary & plugin)141 static const char* CallGetVersion(SharedLibrary& plugin) 142 { 143 typedef const char* (*GetVersion) (); 144 145 #if defined(_WIN32) 146 GetVersion getVersion = (GetVersion) plugin.GetFunction("OrthancPluginGetVersion"); 147 #else 148 GetVersion getVersion; 149 *(void **) (&getVersion) = plugin.GetFunction("OrthancPluginGetVersion"); 150 #endif 151 152 assert(getVersion != NULL); 153 return getVersion(); 154 } 155 156 InvokeService(OrthancPluginContext * context,_OrthancPluginService service,const void * params)157 OrthancPluginErrorCode PluginsManager::InvokeService(OrthancPluginContext* context, 158 _OrthancPluginService service, 159 const void* params) 160 { 161 switch (service) 162 { 163 case _OrthancPluginService_LogError: 164 LOG(ERROR) << reinterpret_cast<const char*>(params); 165 return OrthancPluginErrorCode_Success; 166 167 case _OrthancPluginService_LogWarning: 168 LOG(WARNING) << reinterpret_cast<const char*>(params); 169 return OrthancPluginErrorCode_Success; 170 171 case _OrthancPluginService_LogInfo: 172 CLOG(INFO, PLUGINS) << reinterpret_cast<const char*>(params); 173 return OrthancPluginErrorCode_Success; 174 175 default: 176 break; 177 } 178 179 Plugin* that = reinterpret_cast<Plugin*>(context->pluginsManager); 180 181 for (std::list<IPluginServiceProvider*>::iterator 182 it = that->GetPluginManager().serviceProviders_.begin(); 183 it != that->GetPluginManager().serviceProviders_.end(); ++it) 184 { 185 try 186 { 187 if ((*it)->InvokeService(that->GetSharedLibrary(), service, params)) 188 { 189 return OrthancPluginErrorCode_Success; 190 } 191 } 192 catch (OrthancException& e) 193 { 194 // This service provider has failed 195 if (e.GetErrorCode() != ErrorCode_UnknownResource) // This error code is valid in plugins 196 { 197 LOG(ERROR) << "Exception while invoking plugin service " << service << ": " << e.What(); 198 } 199 200 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); 201 } 202 } 203 204 LOG(ERROR) << "Plugin invoking unknown service: " << service; 205 return OrthancPluginErrorCode_UnknownPluginService; 206 } 207 208 PluginsManager()209 PluginsManager::PluginsManager() 210 { 211 } 212 ~PluginsManager()213 PluginsManager::~PluginsManager() 214 { 215 for (Plugins::iterator it = plugins_.begin(); it != plugins_.end(); ++it) 216 { 217 if (it->second != NULL) 218 { 219 LOG(WARNING) << "Unregistering plugin '" << it->first 220 << "' (version " << it->second->GetVersion() << ")"; 221 222 CallFinalize(it->second->GetSharedLibrary()); 223 delete it->second; 224 } 225 } 226 } 227 228 IsOrthancPlugin(SharedLibrary & library)229 static bool IsOrthancPlugin(SharedLibrary& library) 230 { 231 return (library.HasFunction("OrthancPluginInitialize") && 232 library.HasFunction("OrthancPluginFinalize") && 233 library.HasFunction("OrthancPluginGetName") && 234 library.HasFunction("OrthancPluginGetVersion")); 235 } 236 237 RegisterPlugin(const std::string & path)238 void PluginsManager::RegisterPlugin(const std::string& path) 239 { 240 if (!boost::filesystem::exists(path)) 241 { 242 LOG(ERROR) << "Inexistent path to plugins: " << path; 243 return; 244 } 245 246 if (boost::filesystem::is_directory(path)) 247 { 248 ScanFolderForPlugins(path, false); 249 return; 250 } 251 252 std::unique_ptr<Plugin> plugin(new Plugin(*this, path)); 253 254 if (!IsOrthancPlugin(plugin->GetSharedLibrary())) 255 { 256 LOG(ERROR) << "Plugin " << plugin->GetSharedLibrary().GetPath() 257 << " does not declare the proper entry functions"; 258 throw OrthancException(ErrorCode_SharedLibrary); 259 } 260 261 std::string name(CallGetName(plugin->GetSharedLibrary())); 262 if (plugins_.find(name) != plugins_.end()) 263 { 264 LOG(ERROR) << "Plugin '" << name << "' already registered"; 265 throw OrthancException(ErrorCode_SharedLibrary); 266 } 267 268 plugin->SetVersion(CallGetVersion(plugin->GetSharedLibrary())); 269 LOG(WARNING) << "Registering plugin '" << name 270 << "' (version " << plugin->GetVersion() << ")"; 271 272 CallInitialize(plugin->GetSharedLibrary(), plugin->GetContext()); 273 274 plugins_[name] = plugin.release(); 275 } 276 277 ScanFolderForPlugins(const std::string & folder,bool isRecursive)278 void PluginsManager::ScanFolderForPlugins(const std::string& folder, 279 bool isRecursive) 280 { 281 using namespace boost::filesystem; 282 283 if (!exists(folder)) 284 { 285 return; 286 } 287 288 CLOG(INFO, PLUGINS) << "Scanning folder " << folder << " for plugins"; 289 290 directory_iterator end_it; // default construction yields past-the-end 291 for (directory_iterator it(folder); 292 it != end_it; 293 ++it) 294 { 295 std::string path = it->path().string(); 296 297 if (is_directory(it->status())) 298 { 299 if (isRecursive) 300 { 301 ScanFolderForPlugins(path, true); 302 } 303 } 304 else 305 { 306 std::string extension = boost::filesystem::extension(it->path()); 307 Toolbox::ToLowerCase(extension); 308 309 if (extension == PLUGIN_EXTENSION) 310 { 311 CLOG(INFO, PLUGINS) << "Found a shared library: " << it->path(); 312 313 SharedLibrary plugin(path); 314 if (IsOrthancPlugin(plugin)) 315 { 316 RegisterPlugin(path); 317 } 318 } 319 } 320 } 321 } 322 323 ListPlugins(std::list<std::string> & result) const324 void PluginsManager::ListPlugins(std::list<std::string>& result) const 325 { 326 result.clear(); 327 328 for (Plugins::const_iterator it = plugins_.begin(); 329 it != plugins_.end(); ++it) 330 { 331 result.push_back(it->first); 332 } 333 } 334 335 HasPlugin(const std::string & name) const336 bool PluginsManager::HasPlugin(const std::string& name) const 337 { 338 return plugins_.find(name) != plugins_.end(); 339 } 340 341 GetPluginVersion(const std::string & name) const342 const std::string& PluginsManager::GetPluginVersion(const std::string& name) const 343 { 344 Plugins::const_iterator it = plugins_.find(name); 345 if (it == plugins_.end()) 346 { 347 throw OrthancException(ErrorCode_ParameterOutOfRange); 348 } 349 else 350 { 351 return it->second->GetVersion(); 352 } 353 } 354 355 GetPluginName(SharedLibrary & library)356 std::string PluginsManager::GetPluginName(SharedLibrary& library) 357 { 358 return CallGetName(library); 359 } 360 } 361