1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2    Copyright (c) 2018-2021 The plumed team
3    (see the PEOPLE file at the root of the distribution for a list of names)
4 
5    See http://www.plumed.org for more information.
6 
7    This file is part of plumed, version 2.
8 
9    plumed is free software: you can redistribute it and/or modify
10    it under the terms of the GNU Lesser General Public License as published by
11    the Free Software Foundation, either version 3 of the License, or
12    (at your option) any later version.
13 
14    plumed is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU Lesser General Public License for more details.
18 
19    You should have received a copy of the GNU Lesser General Public License
20    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
21 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 #include "PlumedHandle.h"
23 #include "core/PlumedMain.h"
24 #include "Tools.h"
25 #include <cstring>
26 #ifdef __PLUMED_HAS_DLOPEN
27 #include <dlfcn.h>
28 #endif
29 
30 namespace PLMD
31 {
32 
~DlHandle()33 PlumedHandle::DlHandle::~DlHandle() {
34 #ifdef __PLUMED_HAS_DLOPEN
35   if(handle) dlclose(handle);
36 #endif
37 }
38 
PlumedHandle()39 PlumedHandle::PlumedHandle():
40   local(new PlumedMain)
41 {
42 }
43 
PlumedHandle(const char * kernel)44 PlumedHandle::PlumedHandle(const char* kernel)
45 #ifdef __PLUMED_HAS_DLOPEN
46   :
47   handle([&]() {
48   dlerror();
49   int mode = RTLD_LOCAL | RTLD_NOW;
50 #ifdef RTLD_DEEPBIND
51 // Needed on Linux to avoid namespace clashes
52   mode |= RTLD_DEEPBIND;
53 #endif
54   void* h=::dlopen(kernel,mode);
55 // try to remove the "Kernel" string.
56 // needed to load old versions
57   if(!h) {
58     std::string k(kernel);
59     auto i=k.rfind("Kernel");
60     if(i!=std::string::npos) {
61       k=k.substr(0,i) + k.substr(i+6);
62       h=::dlopen(k.c_str(),mode);
63     }
64   }
65   plumed_assert(h) << "there was a problem loading kernel "<<kernel <<"\n"<<dlerror();
66   return DlHandle(h);
67 // once the DlHandle has been constructed we know that later exceptions will also call dlclose().
68 }()),
69 symbol_((plumed_symbol_table_type*) dlsym(handle,"plumed_symbol_table")),
__anonfd38ce500202() 70 create_([&]() {
71   if(symbol_) {
72     plumed_assert(symbol_->functions.create);
73     return symbol_->functions.create;
74   }
75   void* c=nullptr;
76   if(!c) c=dlsym(handle,"plumedmain_create");
77   if(!c) c=dlsym(handle,"plumed_plumedmain_create");
78   plumed_assert(c) << "in kernel "<<kernel<<" I could not find (plumed_)plumedmain_create";
79   plumed_create_pointer cc;
80   *(void **)(&cc)=c;
81   return cc;
82 }()),
__anonfd38ce500302() 83 cmd_([&]() {
84   if(symbol_) {
85     plumed_assert(symbol_->functions.cmd);
86     return symbol_->functions.cmd;
87   }
88   void* c=nullptr;
89   if(!c) c=dlsym(handle,"plumedmain_cmd");
90   if(!c) c=dlsym(handle,"plumed_plumedmain_cmd");
91   plumed_assert(c) << "in kernel "<<kernel<<" I could not find (plumed_)plumedmain_cmd";
92   plumed_cmd_pointer cc;
93   *(void **)(&cc)=c;
94   return cc;
95 }()),
__anonfd38ce500402() 96 finalize_([&]() {
97   if(symbol_) {
98     plumed_assert(symbol_->functions.finalize);
99     return symbol_->functions.finalize;
100   }
101   void* f=nullptr;
102   if(!f) f=dlsym(handle,"plumedmain_finalize");
103   if(!f) f=dlsym(handle,"plumed_plumedmain_finalize");
104   plumed_assert(f) << "in kernel "<<kernel<<" I could not find (plumed_)plumedmain_finalize";
105   plumed_finalize_pointer ff;
106   *(void **)(&ff)=f;
107   return ff;
108 }()),
109 p(create_())
110 // No exceptions thrown past this point.
111 // Thus, destructor PlumedHandle::~PlumedHandle() will always be called and p will always be finalized.
112 {}
113 #else
114 {
115   plumed_error() << "You are trying to dynamically load a kernel, but PLUMED was compiled without dlopen";
116 }
117 #endif
118 
~PlumedHandle()119 PlumedHandle::~PlumedHandle() {
120 #ifdef __PLUMED_HAS_DLOPEN
121   if(p) finalize_(p);
122 #endif
123 }
124 
dlopen(const char * path)125 PlumedHandle PlumedHandle::dlopen(const char* path) {
126   return PlumedHandle(path);
127 }
128 
cmd(const char * key,const void * ptr)129 void PlumedHandle::cmd(const char*key,const void*ptr) {
130   if(local) local->cmd(key,ptr);
131   else if(p && cmd_) cmd_(p,key,ptr);
132   else plumed_error() << "should never arrive here (either one or the other should work)";
133 }
134 
135 }
136