Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / osd / ClassHandler.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
2 // vim: ts=8 sw=2 smarttab
3
4 #include "include/types.h"
5 #include "ClassHandler.h"
6 #include "common/errno.h"
7 #include "common/ceph_context.h"
8
9 #include <dlfcn.h>
10
11 #include <map>
12
13 #if defined(__FreeBSD__)
14 #include <sys/param.h>
15 #endif
16
17 #include "common/config.h"
18 #include "common/debug.h"
19
20 #define dout_subsys ceph_subsys_osd
21 #undef dout_prefix
22 #define dout_prefix *_dout
23
24
25 #define CLS_PREFIX "libcls_"
26 #define CLS_SUFFIX ".so"
27
28
29 void ClassHandler::add_embedded_class(const string& cname)
30 {
31   assert(mutex.is_locked());
32   ClassData *cls = _get_class(cname, false);
33   assert(cls->status == ClassData::CLASS_UNKNOWN);
34   cls->status = ClassData::CLASS_INITIALIZING;
35 }
36
37 int ClassHandler::open_class(const string& cname, ClassData **pcls)
38 {
39   Mutex::Locker lock(mutex);
40   ClassData *cls = _get_class(cname, true);
41   if (!cls)
42     return -EPERM;
43   if (cls->status != ClassData::CLASS_OPEN) {
44     int r = _load_class(cls);
45     if (r)
46       return r;
47   }
48   *pcls = cls;
49   return 0;
50 }
51
52 int ClassHandler::open_all_classes()
53 {
54   ldout(cct, 10) << __func__ << dendl;
55   DIR *dir = ::opendir(cct->_conf->osd_class_dir.c_str());
56   if (!dir)
57     return -errno;
58
59   struct dirent *pde = nullptr;
60   int r = 0;
61   while ((pde = ::readdir(dir))) {
62     if (pde->d_name[0] == '.')
63       continue;
64     if (strlen(pde->d_name) > sizeof(CLS_PREFIX) - 1 + sizeof(CLS_SUFFIX) - 1 &&
65         strncmp(pde->d_name, CLS_PREFIX, sizeof(CLS_PREFIX) - 1) == 0 &&
66         strcmp(pde->d_name + strlen(pde->d_name) - (sizeof(CLS_SUFFIX) - 1), CLS_SUFFIX) == 0) {
67       char cname[PATH_MAX + 1];
68       strncpy(cname, pde->d_name + sizeof(CLS_PREFIX) - 1, sizeof(cname) -1);
69       cname[strlen(cname) - (sizeof(CLS_SUFFIX) - 1)] = '\0';
70       ldout(cct, 10) << __func__ << " found " << cname << dendl;
71       ClassData *cls;
72       // skip classes that aren't in 'osd class load list'
73       r = open_class(cname, &cls);
74       if (r < 0 && r != -EPERM)
75         goto out;
76     }
77   }
78  out:
79   closedir(dir);
80   return r;
81 }
82
83 void ClassHandler::shutdown()
84 {
85   for (auto& cls : classes) {
86     if (cls.second.handle) {
87       dlclose(cls.second.handle);
88     }
89   }
90   classes.clear();
91 }
92
93 /*
94  * Check if @cname is in the whitespace delimited list @list, or the @list
95  * contains the wildcard "*".
96  *
97  * This is expensive but doesn't consume memory for an index, and is performed
98  * only once when a class is loaded.
99  */
100 bool ClassHandler::in_class_list(const std::string& cname,
101     const std::string& list)
102 {
103   std::istringstream ss(list);
104   std::istream_iterator<std::string> begin{ss};
105   std::istream_iterator<std::string> end{};
106
107   const std::vector<std::string> targets{cname, "*"};
108
109   auto it = std::find_first_of(begin, end,
110       targets.begin(), targets.end());
111
112   return it != end;
113 }
114
115 ClassHandler::ClassData *ClassHandler::_get_class(const string& cname,
116     bool check_allowed)
117 {
118   ClassData *cls;
119   map<string, ClassData>::iterator iter = classes.find(cname);
120
121   if (iter != classes.end()) {
122     cls = &iter->second;
123   } else {
124     if (check_allowed && !in_class_list(cname, cct->_conf->osd_class_load_list)) {
125       ldout(cct, 0) << "_get_class not permitted to load " << cname << dendl;
126       return NULL;
127     }
128     cls = &classes[cname];
129     ldout(cct, 10) << "_get_class adding new class name " << cname << " " << cls << dendl;
130     cls->name = cname;
131     cls->handler = this;
132     cls->whitelisted = in_class_list(cname, cct->_conf->osd_class_default_list);
133   }
134   return cls;
135 }
136
137 int ClassHandler::_load_class(ClassData *cls)
138 {
139   // already open
140   if (cls->status == ClassData::CLASS_OPEN)
141     return 0;
142
143   if (cls->status == ClassData::CLASS_UNKNOWN ||
144       cls->status == ClassData::CLASS_MISSING) {
145     char fname[PATH_MAX];
146     snprintf(fname, sizeof(fname), "%s/" CLS_PREFIX "%s" CLS_SUFFIX,
147              cct->_conf->osd_class_dir.c_str(),
148              cls->name.c_str());
149     ldout(cct, 10) << "_load_class " << cls->name << " from " << fname << dendl;
150
151     cls->handle = dlopen(fname, RTLD_NOW);
152     if (!cls->handle) {
153       struct stat st;
154       int r = ::stat(fname, &st);
155       if (r < 0) {
156         r = -errno;
157         ldout(cct, 0) << __func__ << " could not stat class " << fname
158                       << ": " << cpp_strerror(r) << dendl;
159       } else {
160         ldout(cct, 0) << "_load_class could not open class " << fname
161                       << " (dlopen failed): " << dlerror() << dendl;
162         r = -EIO;
163       }
164       cls->status = ClassData::CLASS_MISSING;
165       return r;
166     }
167
168     cls_deps_t *(*cls_deps)();
169     cls_deps = (cls_deps_t *(*)())dlsym(cls->handle, "class_deps");
170     if (cls_deps) {
171       cls_deps_t *deps = cls_deps();
172       while (deps) {
173         if (!deps->name)
174           break;
175         ClassData *cls_dep = _get_class(deps->name, false);
176         cls->dependencies.insert(cls_dep);
177         if (cls_dep->status != ClassData::CLASS_OPEN)
178           cls->missing_dependencies.insert(cls_dep);
179         deps++;
180       }
181     }
182   }
183
184   // resolve dependencies
185   set<ClassData*>::iterator p = cls->missing_dependencies.begin();
186   while (p != cls->missing_dependencies.end()) {
187     ClassData *dc = *p;
188     int r = _load_class(dc);
189     if (r < 0) {
190       cls->status = ClassData::CLASS_MISSING_DEPS;
191       return r;
192     }
193
194     ldout(cct, 10) << "_load_class " << cls->name << " satisfied dependency " << dc->name << dendl;
195     cls->missing_dependencies.erase(p++);
196   }
197
198   // initialize
199   void (*cls_init)() = (void (*)())dlsym(cls->handle, "__cls_init");
200   if (cls_init) {
201     cls->status = ClassData::CLASS_INITIALIZING;
202     cls_init();
203   }
204
205   ldout(cct, 10) << "_load_class " << cls->name << " success" << dendl;
206   cls->status = ClassData::CLASS_OPEN;
207   return 0;
208 }
209
210
211
212 ClassHandler::ClassData *ClassHandler::register_class(const char *cname)
213 {
214   assert(mutex.is_locked());
215
216   ClassData *cls = _get_class(cname, false);
217   ldout(cct, 10) << "register_class " << cname << " status " << cls->status << dendl;
218
219   if (cls->status != ClassData::CLASS_INITIALIZING) {
220     ldout(cct, 0) << "class " << cname << " isn't loaded; is the class registering under the wrong name?" << dendl;
221     return NULL;
222   }
223   return cls;
224 }
225
226 void ClassHandler::unregister_class(ClassHandler::ClassData *cls)
227 {
228   /* FIXME: do we really need this one? */
229 }
230
231 ClassHandler::ClassMethod *ClassHandler::ClassData::register_method(const char *mname,
232                                                                     int flags,
233                                                                     cls_method_call_t func)
234 {
235   /* no need for locking, called under the class_init mutex */
236   if (!flags) {
237     lderr(handler->cct) << "register_method " << name << "." << mname
238                         << " flags " << flags << " " << (void*)func
239                         << " FAILED -- flags must be non-zero" << dendl;
240     return NULL;
241   }
242   ldout(handler->cct, 10) << "register_method " << name << "." << mname << " flags " << flags << " " << (void*)func << dendl;
243   ClassMethod& method = methods_map[mname];
244   method.func = func;
245   method.name = mname;
246   method.flags = flags;
247   method.cls = this;
248   return &method;
249 }
250
251 ClassHandler::ClassMethod *ClassHandler::ClassData::register_cxx_method(const char *mname,
252                                                                         int flags,
253                                                                         cls_method_cxx_call_t func)
254 {
255   /* no need for locking, called under the class_init mutex */
256   ldout(handler->cct, 10) << "register_cxx_method " << name << "." << mname << " flags " << flags << " " << (void*)func << dendl;
257   ClassMethod& method = methods_map[mname];
258   method.cxx_func = func;
259   method.name = mname;
260   method.flags = flags;
261   method.cls = this;
262   return &method;
263 }
264
265 ClassHandler::ClassFilter *ClassHandler::ClassData::register_cxx_filter(
266     const std::string &filter_name,
267     cls_cxx_filter_factory_t fn)
268 {
269   ClassFilter &filter = filters_map[filter_name];
270   filter.fn = fn;
271   filter.name = filter_name;
272   filter.cls = this;
273   return &filter;
274 }
275
276 ClassHandler::ClassMethod *ClassHandler::ClassData::_get_method(const char *mname)
277 {
278   map<string, ClassHandler::ClassMethod>::iterator iter = methods_map.find(mname);
279   if (iter == methods_map.end())
280     return NULL;
281   return &(iter->second);
282 }
283
284 int ClassHandler::ClassData::get_method_flags(const char *mname)
285 {
286   Mutex::Locker l(handler->mutex);
287   ClassMethod *method = _get_method(mname);
288   if (!method)
289     return -ENOENT;
290   return method->flags;
291 }
292
293 void ClassHandler::ClassData::unregister_method(ClassHandler::ClassMethod *method)
294 {
295   /* no need for locking, called under the class_init mutex */
296    map<string, ClassMethod>::iterator iter = methods_map.find(method->name);
297    if (iter == methods_map.end())
298      return;
299    methods_map.erase(iter);
300 }
301
302 void ClassHandler::ClassMethod::unregister()
303 {
304   cls->unregister_method(this);
305 }
306
307 void ClassHandler::ClassData::unregister_filter(ClassHandler::ClassFilter *filter)
308 {
309   /* no need for locking, called under the class_init mutex */
310    map<string, ClassFilter>::iterator iter = filters_map.find(filter->name);
311    if (iter == filters_map.end())
312      return;
313    filters_map.erase(iter);
314 }
315
316 void ClassHandler::ClassFilter::unregister()
317 {
318   cls->unregister_filter(this);
319 }
320
321 int ClassHandler::ClassMethod::exec(cls_method_context_t ctx, bufferlist& indata, bufferlist& outdata)
322 {
323   int ret;
324   if (cxx_func) {
325     // C++ call version
326     ret = cxx_func(ctx, &indata, &outdata);
327   } else {
328     // C version
329     char *out = NULL;
330     int olen = 0;
331     ret = func(ctx, indata.c_str(), indata.length(), &out, &olen);
332     if (out) {
333       // assume *out was allocated via cls_alloc (which calls malloc!)
334       buffer::ptr bp = buffer::claim_malloc(olen, out);
335       outdata.push_back(bp);
336     }
337   }
338   return ret;
339 }
340