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
29typedef int (*MODULE_INIT_FN)(
30	int argc,
31	char *argv[]);
32typedef int (*MODULE_LOAD_FN)(
33	const void *module,
34	const char *filename);
35typedef int (*MODULE_TERM_FN)(void);
36
37
38struct 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
49typedef struct module_loaded_t {
50	struct module_loaded_t *next;
51	lt_dlhandle lib;
52	char name[1];
53} module_loaded_t;
54
55module_loaded_t *module_list = NULL;
56
57static int module_int_unload (module_loaded_t *module);
58
59#ifdef HAVE_EBCDIC
60static char ebuf[BUFSIZ];
61#endif
62
63int 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
80int 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
101void * 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
113int 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
125int 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
272int 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
282void *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
294static 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
330int load_null_module (const void *module, const char *file_name)
331{
332	return 0;
333}
334
335#ifdef SLAPD_EXTERNAL_EXTENSIONS
336int
337load_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