Home | History | Annotate | Line # | Download | only in slapd
      1 /*	$NetBSD: module.c,v 1.4 2025/09/05 21:16:25 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 1998-2024 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.4 2025/09/05 21:16:25 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 );
     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 );
     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 );
    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 
    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 
    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 
    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 
    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
    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