comparison OrthancServer/Plugins/Engine/PluginsManager.cpp @ 4044:d25f4c0fa160 framework

splitting code into OrthancFramework and OrthancServer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 10 Jun 2020 20:30:34 +0200
parents Plugins/Engine/PluginsManager.cpp@2a170a8f1faf
children 05b8fd21089c
comparison
equal deleted inserted replaced
4043:6c6239aec462 4044:d25f4c0fa160
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-2020 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 "../../OrthancServer/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 "../../Core/HttpServer/HttpOutput.h"
42 #include "../../Core/Logging.h"
43 #include "../../Core/OrthancException.h"
44 #include "../../Core/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__)
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 {
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
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
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
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
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
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 LOG(INFO) << 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
209 PluginsManager::PluginsManager()
210 {
211 }
212
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
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
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
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 LOG(INFO) << "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 LOG(INFO) << "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
324 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
336 bool PluginsManager::HasPlugin(const std::string& name) const
337 {
338 return plugins_.find(name) != plugins_.end();
339 }
340
341
342 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
356 std::string PluginsManager::GetPluginName(SharedLibrary& library)
357 {
358 return CallGetName(library);
359 }
360 }