xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/module.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: module.c,v 1.2 2020/08/11 13:15:39 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1998-2020 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.2 2020/08/11 13:15:39 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 
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, 0, 0);
73 
74 		return -1;
75 	}
76 
77 	return module_path( LDAP_MODULEDIR );
78 }
79 
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, 0, 0);
95 
96 		return -1;
97 	}
98 	return 0;
99 }
100 
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 
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 
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, 0, 0 );
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, 0, 0 );
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, 0, 0 );
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 			0, 0);
174 
175 		return -1;
176 	}
177 	strcpy( module->name, file_name );
178 
179 #ifdef HAVE_EBCDIC
180 	strcpy( file, file_name );
181 	__atoe( file );
182 #endif
183 	/*
184 	 * The result of lt_dlerror(), when called, must be cached prior
185 	 * to calling Debug. This is because Debug is a macro that expands
186 	 * into multiple function calls.
187 	 */
188 	if ((module->lib = lt_dlopenext(file)) == NULL) {
189 		error = lt_dlerror();
190 #ifdef HAVE_EBCDIC
191 		strcpy( ebuf, error );
192 		__etoa( ebuf );
193 		error = ebuf;
194 #endif
195 		Debug(LDAP_DEBUG_ANY, "lt_dlopenext failed: (%s) %s\n", file_name,
196 			error, 0);
197 
198 		ch_free(module);
199 		return -1;
200 	}
201 
202 	Debug(LDAP_DEBUG_CONFIG, "loaded module %s\n", file_name, 0, 0);
203 
204 
205 #ifdef HAVE_EBCDIC
206 #pragma convlit(suspend)
207 #endif
208 	if ((initialize = lt_dlsym(module->lib, "init_module")) == NULL) {
209 #ifdef HAVE_EBCDIC
210 #pragma convlit(resume)
211 #endif
212 		Debug(LDAP_DEBUG_CONFIG, "module %s: no init_module() function found\n",
213 			file_name, 0, 0);
214 
215 		lt_dlclose(module->lib);
216 		ch_free(module);
217 		return -1;
218 	}
219 
220 	/* The imported init_module() routine passes back the type of
221 	 * module (i.e., which part of slapd it should be hooked into)
222 	 * or -1 for error.  If it passes back 0, then you get the
223 	 * old behavior (i.e., the library is loaded and not hooked
224 	 * into anything).
225 	 *
226 	 * It might be better if the conf file could specify the type
227 	 * of module.  That way, a single module could support multiple
228 	 * type of hooks. This could be done by using something like:
229 	 *
230 	 *    moduleload extension /usr/local/openldap/whatever.so
231 	 *
232 	 * then we'd search through module_regtable for a matching
233 	 * module type, and hook in there.
234 	 */
235 	rc = initialize(argc, argv);
236 	if (rc == -1) {
237 		Debug(LDAP_DEBUG_CONFIG, "module %s: init_module() failed\n",
238 			file_name, 0, 0);
239 
240 		lt_dlclose(module->lib);
241 		ch_free(module);
242 		return rc;
243 	}
244 
245 	if (rc >= (int)(sizeof(module_regtable) / sizeof(struct module_regtable_t))
246 		|| module_regtable[rc].proc == NULL)
247 	{
248 		Debug(LDAP_DEBUG_CONFIG, "module %s: unknown registration type (%d)\n",
249 			file_name, rc, 0);
250 
251 		module_int_unload(module);
252 		return -1;
253 	}
254 
255 	rc = (module_regtable[rc].proc)(module, file_name);
256 	if (rc != 0) {
257 		Debug(LDAP_DEBUG_CONFIG, "module %s: %s module could not be registered\n",
258 			file_name, module_regtable[rc].type, 0);
259 
260 		module_int_unload(module);
261 		return rc;
262 	}
263 
264 	module->next = module_list;
265 	module_list = module;
266 
267 	Debug(LDAP_DEBUG_CONFIG, "module %s: %s module registered\n",
268 		file_name, module_regtable[rc].type, 0);
269 
270 	return 0;
271 }
272 
273 int module_path(const char *path)
274 {
275 #ifdef HAVE_EBCDIC
276 	strcpy(ebuf, path);
277 	__atoe(ebuf);
278 	path = ebuf;
279 #endif
280 	return lt_dlsetsearchpath( path );
281 }
282 
283 void *module_resolve (const void *module, const char *name)
284 {
285 #ifdef HAVE_EBCDIC
286 	strcpy(ebuf, name);
287 	__atoe(ebuf);
288 	name = ebuf;
289 #endif
290 	if (module == NULL || name == NULL)
291 		return(NULL);
292 	return(lt_dlsym(((module_loaded_t *)module)->lib, name));
293 }
294 
295 static int module_int_unload (module_loaded_t *module)
296 {
297 	module_loaded_t *mod;
298 	MODULE_TERM_FN terminate;
299 
300 	if (module != NULL) {
301 		/* remove module from tracking list */
302 		if (module_list == module) {
303 			module_list = module->next;
304 		} else {
305 			for (mod = module_list; mod; mod = mod->next) {
306 				if (mod->next == module) {
307 					mod->next = module->next;
308 					break;
309 				}
310 			}
311 		}
312 
313 		/* call module's terminate routine, if present */
314 #ifdef HAVE_EBCDIC
315 #pragma convlit(suspend)
316 #endif
317 		if ((terminate = lt_dlsym(module->lib, "term_module"))) {
318 #ifdef HAVE_EBCDIC
319 #pragma convlit(resume)
320 #endif
321 			terminate();
322 		}
323 
324 		/* close the library and free the memory */
325 		lt_dlclose(module->lib);
326 		ch_free(module);
327 	}
328 	return 0;
329 }
330 
331 int load_null_module (const void *module, const char *file_name)
332 {
333 	return 0;
334 }
335 
336 #ifdef SLAPD_EXTERNAL_EXTENSIONS
337 int
338 load_extop_module (
339 	const void *module,
340 	const char *file_name
341 )
342 {
343 	SLAP_EXTOP_MAIN_FN *ext_main;
344 	SLAP_EXTOP_GETOID_FN *ext_getoid;
345 	struct berval oid;
346 	int rc;
347 
348 	ext_main = (SLAP_EXTOP_MAIN_FN *)module_resolve(module, "ext_main");
349 	if (ext_main == NULL) {
350 		return(-1);
351 	}
352 
353 	ext_getoid = module_resolve(module, "ext_getoid");
354 	if (ext_getoid == NULL) {
355 		return(-1);
356 	}
357 
358 	rc = (ext_getoid)(0, &oid, 256);
359 	if (rc != 0) {
360 		return(rc);
361 	}
362 	if (oid.bv_val == NULL || oid.bv_len == 0) {
363 		return(-1);
364 	}
365 
366 	/* FIXME: this is broken, and no longer needed,
367 	 * as a module can call load_extop() itself... */
368 	rc = load_extop( &oid, ext_main );
369 	return rc;
370 }
371 #endif /* SLAPD_EXTERNAL_EXTENSIONS */
372 #endif /* SLAPD_MODULES */
373 
374