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"
8 #include "include/dlfcn_compat.h"
12 #if defined(__FreeBSD__)
13 #include <sys/param.h>
16 #include "common/config.h"
17 #include "common/debug.h"
19 #define dout_subsys ceph_subsys_osd
21 #define dout_prefix *_dout
24 #define CLS_PREFIX "libcls_"
25 #define CLS_SUFFIX SHARED_LIB_SUFFIX
31 using ceph::bufferlist
;
34 int ClassHandler::open_class(const string
& cname
, ClassData
**pcls
)
36 std::lock_guard
lock(mutex
);
37 ClassData
*cls
= _get_class(cname
, true);
40 if (cls
->status
!= ClassData::CLASS_OPEN
) {
41 int r
= _load_class(cls
);
49 int ClassHandler::open_all_classes()
51 ldout(cct
, 10) << __func__
<< dendl
;
52 DIR *dir
= ::opendir(cct
->_conf
->osd_class_dir
.c_str());
56 struct dirent
*pde
= nullptr;
58 while ((pde
= ::readdir(dir
))) {
59 if (pde
->d_name
[0] == '.')
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
;
69 // skip classes that aren't in 'osd class load list'
70 r
= open_class(cname
, &cls
);
71 if (r
< 0 && r
!= -EPERM
)
80 void ClassHandler::shutdown()
82 for (auto& cls
: classes
) {
83 if (cls
.second
.handle
) {
84 dlclose(cls
.second
.handle
);
91 * Check if @cname is in the whitespace delimited list @list, or the @list
92 * contains the wildcard "*".
94 * This is expensive but doesn't consume memory for an index, and is performed
95 * only once when a class is loaded.
97 bool ClassHandler::in_class_list(const std::string
& cname
,
98 const std::string
& list
)
100 std::istringstream
ss(list
);
101 std::istream_iterator
<std::string
> begin
{ss
};
102 std::istream_iterator
<std::string
> end
{};
104 const std::vector
<std::string
> targets
{cname
, "*"};
106 auto it
= std::find_first_of(begin
, end
,
107 targets
.begin(), targets
.end());
112 ClassHandler::ClassData
*ClassHandler::_get_class(const string
& cname
,
116 map
<string
, ClassData
>::iterator iter
= classes
.find(cname
);
118 if (iter
!= classes
.end()) {
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
;
125 cls
= &classes
[cname
];
126 ldout(cct
, 10) << "_get_class adding new class name " << cname
<< " " << cls
<< dendl
;
129 cls
->allowed
= in_class_list(cname
, cct
->_conf
->osd_class_default_list
);
134 int ClassHandler::_load_class(ClassData
*cls
)
137 if (cls
->status
== ClassData::CLASS_OPEN
)
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(),
146 ldout(cct
, 10) << "_load_class " << cls
->name
<< " from " << fname
<< dendl
;
148 cls
->handle
= dlopen(fname
, RTLD_NOW
);
151 int r
= ::stat(fname
, &st
);
154 ldout(cct
, 0) << __func__
<< " could not stat class " << fname
155 << ": " << cpp_strerror(r
) << dendl
;
157 ldout(cct
, 0) << "_load_class could not open class " << fname
158 << " (dlopen failed): " << dlerror() << dendl
;
161 cls
->status
= ClassData::CLASS_MISSING
;
165 cls_deps_t
*(*cls_deps
)();
166 cls_deps
= (cls_deps_t
*(*)())dlsym(cls
->handle
, "class_deps");
168 cls_deps_t
*deps
= cls_deps();
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
);
181 // resolve dependencies
182 set
<ClassData
*>::iterator p
= cls
->missing_dependencies
.begin();
183 while (p
!= cls
->missing_dependencies
.end()) {
185 int r
= _load_class(dc
);
187 cls
->status
= ClassData::CLASS_MISSING_DEPS
;
191 ldout(cct
, 10) << "_load_class " << cls
->name
<< " satisfied dependency " << dc
->name
<< dendl
;
192 cls
->missing_dependencies
.erase(p
++);
196 void (*cls_init
)() = (void (*)())dlsym(cls
->handle
, "__cls_init");
198 cls
->status
= ClassData::CLASS_INITIALIZING
;
202 ldout(cct
, 10) << "_load_class " << cls
->name
<< " success" << dendl
;
203 cls
->status
= ClassData::CLASS_OPEN
;
209 ClassHandler::ClassData
*ClassHandler::register_class(const char *cname
)
211 ceph_assert(ceph_mutex_is_locked(mutex
));
213 ClassData
*cls
= _get_class(cname
, false);
214 ldout(cct
, 10) << "register_class " << cname
<< " status " << cls
->status
<< dendl
;
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
;
223 void ClassHandler::unregister_class(ClassHandler::ClassData
*cls
)
225 /* FIXME: do we really need this one? */
228 ClassHandler::ClassMethod
*ClassHandler::ClassData::register_method(const char *mname
,
230 cls_method_call_t func
)
232 /* no need for locking, called under the class_init mutex */
234 lderr(handler
->cct
) << "register_method " << name
<< "." << mname
235 << " flags " << flags
<< " " << (void*)func
236 << " FAILED -- flags must be non-zero" << dendl
;
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
;
244 ClassHandler::ClassMethod
*ClassHandler::ClassData::register_cxx_method(const char *mname
,
246 cls_method_cxx_call_t func
)
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
;
254 ClassHandler::ClassFilter
*ClassHandler::ClassData::register_cxx_filter(
255 const std::string
&filter_name
,
256 cls_cxx_filter_factory_t fn
)
258 ClassFilter
&filter
= filters_map
[filter_name
];
260 filter
.name
= filter_name
;
265 ClassHandler::ClassMethod
*ClassHandler::ClassData::_get_method(
266 const std::string
& mname
)
268 if (auto iter
= methods_map
.find(mname
); iter
!= methods_map
.end()) {
269 return &(iter
->second
);
275 int ClassHandler::ClassData::get_method_flags(const std::string
& mname
)
277 std::lock_guard
l(handler
->mutex
);
278 ClassMethod
*method
= _get_method(mname
);
281 return method
->flags
;
284 void ClassHandler::ClassData::unregister_method(ClassHandler::ClassMethod
*method
)
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())
290 methods_map
.erase(iter
);
293 void ClassHandler::ClassMethod::unregister()
295 cls
->unregister_method(this);
298 void ClassHandler::ClassData::unregister_filter(ClassHandler::ClassFilter
*filter
)
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())
304 filters_map
.erase(iter
);
307 void ClassHandler::ClassFilter::unregister()
309 cls
->unregister_filter(this);
312 int ClassHandler::ClassMethod::exec(cls_method_context_t ctx
, bufferlist
& indata
, bufferlist
& outdata
)
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
>) {
319 ret
= method(ctx
, &indata
, &outdata
);
320 } else if constexpr (std::is_same_v
<method_t
, cls_method_call_t
>) {
324 ret
= method(ctx
, indata
.c_str(), indata
.length(), &out
, &olen
);
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
);
331 static_assert(std::is_same_v
<method_t
, void>);
337 ClassHandler
& ClassHandler::get_instance()
340 // the context is being used solely for:
341 // 1. random number generation (cls_gen_random_bytes)
342 // 2. accessing the configuration
344 static CephContext cct
;
345 static ClassHandler
single(&cct
);
347 static ClassHandler
single(g_ceph_context
);
348 #endif // WITH_SEASTAR