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