1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "include/types.h"
5 #include "ClassHandler.h"
6 #include "common/errno.h"
7 #include "common/ceph_context.h"
13 #if defined(__FreeBSD__)
14 #include <sys/param.h>
17 #include "common/config.h"
18 #include "common/debug.h"
20 #define dout_subsys ceph_subsys_osd
22 #define dout_prefix *_dout
25 #define CLS_PREFIX "libcls_"
26 #define CLS_SUFFIX ".so"
29 void ClassHandler::add_embedded_class(const string& cname)
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;
37 int ClassHandler::open_class(const string& cname, ClassData **pcls)
39 Mutex::Locker lock(mutex);
40 ClassData *cls = _get_class(cname, true);
43 if (cls->status != ClassData::CLASS_OPEN) {
44 int r = _load_class(cls);
52 int ClassHandler::open_all_classes()
54 ldout(cct, 10) << __func__ << dendl;
55 DIR *dir = ::opendir(cct->_conf->osd_class_dir.c_str());
59 struct dirent *pde = nullptr;
61 while ((pde = ::readdir(dir))) {
62 if (pde->d_name[0] == '.')
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;
72 // skip classes that aren't in 'osd class load list'
73 r = open_class(cname, &cls);
74 if (r < 0 && r != -EPERM)
83 void ClassHandler::shutdown()
85 for (auto& cls : classes) {
86 if (cls.second.handle) {
87 dlclose(cls.second.handle);
94 * Check if @cname is in the whitespace delimited list @list, or the @list
95 * contains the wildcard "*".
97 * This is expensive but doesn't consume memory for an index, and is performed
98 * only once when a class is loaded.
100 bool ClassHandler::in_class_list(const std::string& cname,
101 const std::string& list)
103 std::istringstream ss(list);
104 std::istream_iterator<std::string> begin{ss};
105 std::istream_iterator<std::string> end{};
107 const std::vector<std::string> targets{cname, "*"};
109 auto it = std::find_first_of(begin, end,
110 targets.begin(), targets.end());
115 ClassHandler::ClassData *ClassHandler::_get_class(const string& cname,
119 map<string, ClassData>::iterator iter = classes.find(cname);
121 if (iter != classes.end()) {
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;
128 cls = &classes[cname];
129 ldout(cct, 10) << "_get_class adding new class name " << cname << " " << cls << dendl;
132 cls->whitelisted = in_class_list(cname, cct->_conf->osd_class_default_list);
137 int ClassHandler::_load_class(ClassData *cls)
140 if (cls->status == ClassData::CLASS_OPEN)
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(),
149 ldout(cct, 10) << "_load_class " << cls->name << " from " << fname << dendl;
151 cls->handle = dlopen(fname, RTLD_NOW);
154 int r = ::stat(fname, &st);
157 ldout(cct, 0) << __func__ << " could not stat class " << fname
158 << ": " << cpp_strerror(r) << dendl;
160 ldout(cct, 0) << "_load_class could not open class " << fname
161 << " (dlopen failed): " << dlerror() << dendl;
164 cls->status = ClassData::CLASS_MISSING;
168 cls_deps_t *(*cls_deps)();
169 cls_deps = (cls_deps_t *(*)())dlsym(cls->handle, "class_deps");
171 cls_deps_t *deps = cls_deps();
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);
184 // resolve dependencies
185 set<ClassData*>::iterator p = cls->missing_dependencies.begin();
186 while (p != cls->missing_dependencies.end()) {
188 int r = _load_class(dc);
190 cls->status = ClassData::CLASS_MISSING_DEPS;
194 ldout(cct, 10) << "_load_class " << cls->name << " satisfied dependency " << dc->name << dendl;
195 cls->missing_dependencies.erase(p++);
199 void (*cls_init)() = (void (*)())dlsym(cls->handle, "__cls_init");
201 cls->status = ClassData::CLASS_INITIALIZING;
205 ldout(cct, 10) << "_load_class " << cls->name << " success" << dendl;
206 cls->status = ClassData::CLASS_OPEN;
212 ClassHandler::ClassData *ClassHandler::register_class(const char *cname)
214 assert(mutex.is_locked());
216 ClassData *cls = _get_class(cname, false);
217 ldout(cct, 10) << "register_class " << cname << " status " << cls->status << dendl;
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;
226 void ClassHandler::unregister_class(ClassHandler::ClassData *cls)
228 /* FIXME: do we really need this one? */
231 ClassHandler::ClassMethod *ClassHandler::ClassData::register_method(const char *mname,
233 cls_method_call_t func)
235 /* no need for locking, called under the class_init mutex */
237 lderr(handler->cct) << "register_method " << name << "." << mname
238 << " flags " << flags << " " << (void*)func
239 << " FAILED -- flags must be non-zero" << dendl;
242 ldout(handler->cct, 10) << "register_method " << name << "." << mname << " flags " << flags << " " << (void*)func << dendl;
243 ClassMethod& method = methods_map[mname];
246 method.flags = flags;
251 ClassHandler::ClassMethod *ClassHandler::ClassData::register_cxx_method(const char *mname,
253 cls_method_cxx_call_t func)
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;
260 method.flags = flags;
265 ClassHandler::ClassFilter *ClassHandler::ClassData::register_cxx_filter(
266 const std::string &filter_name,
267 cls_cxx_filter_factory_t fn)
269 ClassFilter &filter = filters_map[filter_name];
271 filter.name = filter_name;
276 ClassHandler::ClassMethod *ClassHandler::ClassData::_get_method(const char *mname)
278 map<string, ClassHandler::ClassMethod>::iterator iter = methods_map.find(mname);
279 if (iter == methods_map.end())
281 return &(iter->second);
284 int ClassHandler::ClassData::get_method_flags(const char *mname)
286 Mutex::Locker l(handler->mutex);
287 ClassMethod *method = _get_method(mname);
290 return method->flags;
293 void ClassHandler::ClassData::unregister_method(ClassHandler::ClassMethod *method)
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())
299 methods_map.erase(iter);
302 void ClassHandler::ClassMethod::unregister()
304 cls->unregister_method(this);
307 void ClassHandler::ClassData::unregister_filter(ClassHandler::ClassFilter *filter)
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())
313 filters_map.erase(iter);
316 void ClassHandler::ClassFilter::unregister()
318 cls->unregister_filter(this);
321 int ClassHandler::ClassMethod::exec(cls_method_context_t ctx, bufferlist& indata, bufferlist& outdata)
326 ret = cxx_func(ctx, &indata, &outdata);
331 ret = func(ctx, indata.c_str(), indata.length(), &out, &olen);
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);