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