1 /* $NetBSD: module.c,v 1.3 2021/08/14 16:14:58 christos Exp $ */
2
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 1998-2021 The OpenLDAP Foundation.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
11 * Public License.
12 *
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
16 */
17
18 #include <sys/cdefs.h>
19 __RCSID("$NetBSD: module.c,v 1.3 2021/08/14 16:14:58 christos Exp $");
20
21 #include "portable.h"
22 #include <stdio.h>
23 #include "slap.h"
24
25 #ifdef SLAPD_MODULES
26
27 #include <ltdl.h>
28
29 typedef int (*MODULE_INIT_FN)(
30 int argc,
31 char *argv[]);
32 typedef int (*MODULE_LOAD_FN)(
33 const void *module,
34 const char *filename);
35 typedef int (*MODULE_TERM_FN)(void);
36
37
38 struct module_regtable_t {
39 char *type;
40 MODULE_LOAD_FN proc;
41 } module_regtable[] = {
42 { "null", load_null_module },
43 #ifdef SLAPD_EXTERNAL_EXTENSIONS
44 { "extension", load_extop_module },
45 #endif
46 { NULL, NULL }
47 };
48
49 typedef struct module_loaded_t {
50 struct module_loaded_t *next;
51 lt_dlhandle lib;
52 char name[1];
53 } module_loaded_t;
54
55 module_loaded_t *module_list = NULL;
56
57 static int module_int_unload (module_loaded_t *module);
58
59 #ifdef HAVE_EBCDIC
60 static char ebuf[BUFSIZ];
61 #endif
62
module_init(void)63 int module_init (void)
64 {
65 if (lt_dlinit()) {
66 const char *error = lt_dlerror();
67 #ifdef HAVE_EBCDIC
68 strcpy( ebuf, error );
69 __etoa( ebuf );
70 error = ebuf;
71 #endif
72 Debug(LDAP_DEBUG_ANY, "lt_dlinit failed: %s\n", error );
73
74 return -1;
75 }
76
77 return module_path( LDAP_MODULEDIR );
78 }
79
module_kill(void)80 int module_kill (void)
81 {
82 /* unload all modules before shutdown */
83 while (module_list != NULL) {
84 module_int_unload(module_list);
85 }
86
87 if (lt_dlexit()) {
88 const char *error = lt_dlerror();
89 #ifdef HAVE_EBCDIC
90 strcpy( ebuf, error );
91 __etoa( ebuf );
92 error = ebuf;
93 #endif
94 Debug(LDAP_DEBUG_ANY, "lt_dlexit failed: %s\n", error );
95
96 return -1;
97 }
98 return 0;
99 }
100
module_handle(const char * file_name)101 void * module_handle( const char *file_name )
102 {
103 module_loaded_t *module;
104
105 for ( module = module_list; module; module= module->next ) {
106 if ( !strcmp( module->name, file_name )) {
107 return module;
108 }
109 }
110 return NULL;
111 }
112
module_unload(const char * file_name)113 int module_unload( const char *file_name )
114 {
115 module_loaded_t *module;
116
117 module = module_handle( file_name );
118 if ( module ) {
119 module_int_unload( module );
120 return 0;
121 }
122 return -1; /* not found */
123 }
124
module_load(const char * file_name,int argc,char * argv[])125 int module_load(const char* file_name, int argc, char *argv[])
126 {
127 module_loaded_t *module;
128 const char *error;
129 int rc;
130 MODULE_INIT_FN initialize;
131 #ifdef HAVE_EBCDIC
132 #define file ebuf
133 #else
134 #define file file_name
135 #endif
136
137 module = module_handle( file_name );
138 if ( module ) {
139 Debug( LDAP_DEBUG_ANY, "module_load: (%s) already loaded\n",
140 file_name );
141 return -1;
142 }
143
144 /* If loading a backend, see if we already have it */
145 if ( !strncasecmp( file_name, "back_", 5 )) {
146 char *name = (char *)file_name + 5;
147 char *dot = strchr( name, '.');
148 if (dot) *dot = '\0';
149 rc = backend_info( name ) != NULL;
150 if (dot) *dot = '.';
151 if ( rc ) {
152 Debug( LDAP_DEBUG_CONFIG, "module_load: (%s) already present (static)\n",
153 file_name );
154 return 0;
155 }
156 } else {
157 /* check for overlays too */
158 char *dot = strchr( file_name, '.' );
159 if ( dot ) *dot = '\0';
160 rc = overlay_find( file_name ) != NULL;
161 if ( dot ) *dot = '.';
162 if ( rc ) {
163 Debug( LDAP_DEBUG_CONFIG, "module_load: (%s) already present (static)\n",
164 file_name );
165 return 0;
166 }
167 }
168
169 module = (module_loaded_t *)ch_calloc(1, sizeof(module_loaded_t) +
170 strlen(file_name));
171 if (module == NULL) {
172 Debug(LDAP_DEBUG_ANY, "module_load failed: (%s) out of memory\n", file_name );
173
174 return -1;
175 }
176 strcpy( module->name, file_name );
177
178 #ifdef HAVE_EBCDIC
179 strcpy( file, file_name );
180 __atoe( file );
181 #endif
182 /*
183 * The result of lt_dlerror(), when called, must be cached prior
184 * to calling Debug. This is because Debug is a macro that expands
185 * into multiple function calls.
186 */
187 if ((module->lib = lt_dlopenext(file)) == NULL) {
188 error = lt_dlerror();
189 #ifdef HAVE_EBCDIC
190 strcpy( ebuf, error );
191 __etoa( ebuf );
192 error = ebuf;
193 #endif
194 Debug(LDAP_DEBUG_ANY, "lt_dlopenext failed: (%s) %s\n", file_name,
195 error );
196
197 ch_free(module);
198 return -1;
199 }
200
201 Debug(LDAP_DEBUG_CONFIG, "loaded module %s\n", file_name );
202
203
204 #ifdef HAVE_EBCDIC
205 #pragma convlit(suspend)
206 #endif
207 if ((initialize = lt_dlsym(module->lib, "init_module")) == NULL) {
208 #ifdef HAVE_EBCDIC
209 #pragma convlit(resume)
210 #endif
211 Debug(LDAP_DEBUG_CONFIG, "module %s: no init_module() function found\n",
212 file_name );
213
214 lt_dlclose(module->lib);
215 ch_free(module);
216 return -1;
217 }
218
219 /* The imported init_module() routine passes back the type of
220 * module (i.e., which part of slapd it should be hooked into)
221 * or -1 for error. If it passes back 0, then you get the
222 * old behavior (i.e., the library is loaded and not hooked
223 * into anything).
224 *
225 * It might be better if the conf file could specify the type
226 * of module. That way, a single module could support multiple
227 * type of hooks. This could be done by using something like:
228 *
229 * moduleload extension /usr/local/openldap/whatever.so
230 *
231 * then we'd search through module_regtable for a matching
232 * module type, and hook in there.
233 */
234 rc = initialize(argc, argv);
235 if (rc == -1) {
236 Debug(LDAP_DEBUG_CONFIG, "module %s: init_module() failed\n",
237 file_name );
238
239 lt_dlclose(module->lib);
240 ch_free(module);
241 return rc;
242 }
243
244 if (rc >= (int)(sizeof(module_regtable) / sizeof(struct module_regtable_t))
245 || module_regtable[rc].proc == NULL)
246 {
247 Debug(LDAP_DEBUG_CONFIG, "module %s: unknown registration type (%d)\n",
248 file_name, rc );
249
250 module_int_unload(module);
251 return -1;
252 }
253
254 rc = (module_regtable[rc].proc)(module, file_name);
255 if (rc != 0) {
256 Debug(LDAP_DEBUG_CONFIG, "module %s: %s module could not be registered\n",
257 file_name, module_regtable[rc].type );
258
259 module_int_unload(module);
260 return rc;
261 }
262
263 module->next = module_list;
264 module_list = module;
265
266 Debug(LDAP_DEBUG_CONFIG, "module %s: %s module registered\n",
267 file_name, module_regtable[rc].type );
268
269 return 0;
270 }
271
module_path(const char * path)272 int module_path(const char *path)
273 {
274 #ifdef HAVE_EBCDIC
275 strcpy(ebuf, path);
276 __atoe(ebuf);
277 path = ebuf;
278 #endif
279 return lt_dlsetsearchpath( path );
280 }
281
module_resolve(const void * module,const char * name)282 void *module_resolve (const void *module, const char *name)
283 {
284 #ifdef HAVE_EBCDIC
285 strcpy(ebuf, name);
286 __atoe(ebuf);
287 name = ebuf;
288 #endif
289 if (module == NULL || name == NULL)
290 return(NULL);
291 return(lt_dlsym(((module_loaded_t *)module)->lib, name));
292 }
293
module_int_unload(module_loaded_t * module)294 static int module_int_unload (module_loaded_t *module)
295 {
296 module_loaded_t *mod;
297 MODULE_TERM_FN terminate;
298
299 if (module != NULL) {
300 /* remove module from tracking list */
301 if (module_list == module) {
302 module_list = module->next;
303 } else {
304 for (mod = module_list; mod; mod = mod->next) {
305 if (mod->next == module) {
306 mod->next = module->next;
307 break;
308 }
309 }
310 }
311
312 /* call module's terminate routine, if present */
313 #ifdef HAVE_EBCDIC
314 #pragma convlit(suspend)
315 #endif
316 if ((terminate = lt_dlsym(module->lib, "term_module"))) {
317 #ifdef HAVE_EBCDIC
318 #pragma convlit(resume)
319 #endif
320 terminate();
321 }
322
323 /* close the library and free the memory */
324 lt_dlclose(module->lib);
325 ch_free(module);
326 }
327 return 0;
328 }
329
load_null_module(const void * module,const char * file_name)330 int load_null_module (const void *module, const char *file_name)
331 {
332 return 0;
333 }
334
335 #ifdef SLAPD_EXTERNAL_EXTENSIONS
336 int
load_extop_module(const void * module,const char * file_name)337 load_extop_module (
338 const void *module,
339 const char *file_name
340 )
341 {
342 SLAP_EXTOP_MAIN_FN *ext_main;
343 SLAP_EXTOP_GETOID_FN *ext_getoid;
344 struct berval oid;
345 int rc;
346
347 ext_main = (SLAP_EXTOP_MAIN_FN *)module_resolve(module, "ext_main");
348 if (ext_main == NULL) {
349 return(-1);
350 }
351
352 ext_getoid = module_resolve(module, "ext_getoid");
353 if (ext_getoid == NULL) {
354 return(-1);
355 }
356
357 rc = (ext_getoid)(0, &oid, 256);
358 if (rc != 0) {
359 return(rc);
360 }
361 if (oid.bv_val == NULL || oid.bv_len == 0) {
362 return(-1);
363 }
364
365 /* FIXME: this is broken, and no longer needed,
366 * as a module can call load_extop() itself... */
367 rc = load_extop( &oid, ext_main );
368 return rc;
369 }
370 #endif /* SLAPD_EXTERNAL_EXTENSIONS */
371 #endif /* SLAPD_MODULES */
372
373