xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/slapi/plugin.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: plugin.c,v 1.2 2020/08/11 13:15:42 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 2002-2020 The OpenLDAP Foundation.
7  * Portions Copyright 1997,2002-2003 IBM Corporation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was initially developed by IBM Corporation for use in
20  * IBM products and subsequently ported to OpenLDAP Software by
21  * Steve Omrani.  Additional significant contributors include:
22  *    Luke Howard
23  */
24 
25 #include <sys/cdefs.h>
26 __RCSID("$NetBSD: plugin.c,v 1.2 2020/08/11 13:15:42 christos Exp $");
27 
28 #include "portable.h"
29 #include "ldap_pvt_thread.h"
30 #include "slap.h"
31 #include "config.h"
32 #include "slapi.h"
33 #include "lutil.h"
34 
35 /*
36  * Note: if ltdl.h is not available, slapi should not be compiled
37  */
38 #include <ltdl.h>
39 
40 static int slapi_int_load_plugin( Slapi_PBlock *, const char *, const char *, int,
41 	SLAPI_FUNC *, lt_dlhandle * );
42 
43 /* pointer to link list of extended objects */
44 static ExtendedOp *pGExtendedOps = NULL;
45 
46 /*********************************************************************
47  * Function Name:      plugin_pblock_new
48  *
49  * Description:        This routine creates a new Slapi_PBlock structure,
50  *                     loads in the plugin module and executes the init
51  *                     function provided by the module.
52  *
53  * Input:              type - type of the plugin, such as SASL, database, etc.
54  *                     path - the loadpath to load the module in
55  *                     initfunc - name of the plugin function to execute first
56  *                     argc - number of arguements
57  *                     argv[] - an array of char pointers point to
58  *                              the arguments passed in via
59  *                              the configuration file.
60  *
61  * Output:
62  *
63  * Return Values:      a pointer to a newly created Slapi_PBlock structrue or
64  *                     NULL - function failed
65  *
66  * Messages:           None
67  *********************************************************************/
68 
69 static Slapi_PBlock *
70 plugin_pblock_new(
71 	int type,
72 	int argc,
73 	char *argv[] )
74 {
75 	Slapi_PBlock	*pPlugin = NULL;
76 	Slapi_PluginDesc *pPluginDesc = NULL;
77 	lt_dlhandle	hdLoadHandle;
78 	int		rc;
79 	char		**av2 = NULL, **ppPluginArgv;
80 	char		*path = argv[2];
81 	char		*initfunc = argv[3];
82 
83 	pPlugin = slapi_pblock_new();
84 	if ( pPlugin == NULL ) {
85 		rc = LDAP_NO_MEMORY;
86 		goto done;
87 	}
88 
89 	slapi_pblock_set( pPlugin, SLAPI_PLUGIN_TYPE, (void *)&type );
90 	slapi_pblock_set( pPlugin, SLAPI_PLUGIN_ARGC, (void *)&argc );
91 
92 	av2 = ldap_charray_dup( argv );
93 	if ( av2 == NULL ) {
94 		rc = LDAP_NO_MEMORY;
95 		goto done;
96 	}
97 
98 	if ( argc > 0 ) {
99 		ppPluginArgv = &av2[4];
100 	} else {
101 		ppPluginArgv = NULL;
102 	}
103 
104 	slapi_pblock_set( pPlugin, SLAPI_PLUGIN_ARGV, (void *)ppPluginArgv );
105 	slapi_pblock_set( pPlugin, SLAPI_X_CONFIG_ARGV, (void *)av2 );
106 
107 	rc = slapi_int_load_plugin( pPlugin, path, initfunc, 1, NULL, &hdLoadHandle );
108 	if ( rc != 0 ) {
109 		goto done;
110 	}
111 
112 	if ( slapi_pblock_get( pPlugin, SLAPI_PLUGIN_DESCRIPTION, (void **)&pPluginDesc ) == 0 &&
113 	     pPluginDesc != NULL ) {
114 		slapi_log_error(SLAPI_LOG_TRACE, "plugin_pblock_new",
115 				"Registered plugin %s %s [%s] (%s)\n",
116 				pPluginDesc->spd_id,
117 				pPluginDesc->spd_version,
118 				pPluginDesc->spd_vendor,
119 				pPluginDesc->spd_description);
120 	}
121 
122 done:
123 	if ( rc != 0 && pPlugin != NULL ) {
124 		slapi_pblock_destroy( pPlugin );
125 		pPlugin = NULL;
126 		if ( av2 != NULL ) {
127 			ldap_charray_free( av2 );
128 		}
129 	}
130 
131 	return pPlugin;
132 }
133 
134 /*********************************************************************
135  * Function Name:      slapi_int_register_plugin
136  *
137  * Description:        insert the slapi_pblock structure to the end of the plugin
138  *                     list
139  *
140  * Input:              a pointer to a plugin slapi_pblock structure to be added to
141  *                     the list
142  *
143  * Output:             none
144  *
145  * Return Values:      LDAP_SUCCESS - successfully inserted.
146  *                     LDAP_LOCAL_ERROR.
147  *
148  * Messages:           None
149  *********************************************************************/
150 int
151 slapi_int_register_plugin(
152 	Backend *be,
153 	Slapi_PBlock *pPB )
154 {
155 	Slapi_PBlock	*pTmpPB;
156 	Slapi_PBlock	*pSavePB;
157 	int   		 rc = LDAP_SUCCESS;
158 
159 	assert( be != NULL );
160 
161 	pTmpPB = SLAPI_BACKEND_PBLOCK( be );
162 	if ( pTmpPB == NULL ) {
163 		SLAPI_BACKEND_PBLOCK( be ) = pPB;
164 	} else {
165 		while ( pTmpPB != NULL && rc == LDAP_SUCCESS ) {
166 			pSavePB = pTmpPB;
167 			rc = slapi_pblock_get( pTmpPB, SLAPI_IBM_PBLOCK, &pTmpPB );
168 		}
169 
170 		if ( rc == LDAP_SUCCESS ) {
171 			rc = slapi_pblock_set( pSavePB, SLAPI_IBM_PBLOCK, (void *)pPB );
172 		}
173 	}
174 
175 	return ( rc != LDAP_SUCCESS ) ? LDAP_OTHER : LDAP_SUCCESS;
176 }
177 
178 /*********************************************************************
179  * Function Name:      slapi_int_get_plugins
180  *
181  * Description:        get the desired type of function pointers defined
182  *                     in all the plugins
183  *
184  * Input:              the type of the functions to get, such as pre-operation,etc.
185  *
186  * Output:             none
187  *
188  * Return Values:      this routine returns a pointer to an array of function
189  *                     pointers containing backend-specific plugin functions
190  *                     followed by global plugin functions
191  *
192  * Messages:           None
193  *********************************************************************/
194 int
195 slapi_int_get_plugins(
196 	Backend *be,
197 	int functype,
198 	SLAPI_FUNC **ppFuncPtrs )
199 {
200 
201 	Slapi_PBlock	*pCurrentPB;
202 	SLAPI_FUNC	FuncPtr;
203 	SLAPI_FUNC	*pTmpFuncPtr;
204 	int		numPB = 0;
205 	int		rc = LDAP_SUCCESS;
206 
207 	assert( ppFuncPtrs != NULL );
208 
209 	if ( be == NULL ) {
210 		goto done;
211 	}
212 
213 	pCurrentPB = SLAPI_BACKEND_PBLOCK( be );
214 
215 	while ( pCurrentPB != NULL && rc == LDAP_SUCCESS ) {
216 		rc = slapi_pblock_get( pCurrentPB, functype, &FuncPtr );
217 		if ( rc == LDAP_SUCCESS ) {
218 			if ( FuncPtr != NULL )  {
219 				numPB++;
220 			}
221 			rc = slapi_pblock_get( pCurrentPB,
222 				SLAPI_IBM_PBLOCK, &pCurrentPB );
223 		}
224 	}
225 
226 	if ( numPB == 0 ) {
227 		*ppFuncPtrs = NULL;
228 		rc = LDAP_SUCCESS;
229 		goto done;
230 	}
231 
232 	/*
233 	 * Now, build the function pointer array of backend-specific
234 	 * plugins followed by global plugins.
235 	 */
236 	*ppFuncPtrs = pTmpFuncPtr =
237 		(SLAPI_FUNC *)ch_malloc( ( numPB + 1 ) * sizeof(SLAPI_FUNC) );
238 	if ( ppFuncPtrs == NULL ) {
239 		rc = LDAP_NO_MEMORY;
240 		goto done;
241 	}
242 
243 	pCurrentPB = SLAPI_BACKEND_PBLOCK( be );
244 
245 	while ( pCurrentPB != NULL && rc == LDAP_SUCCESS )  {
246 		rc = slapi_pblock_get( pCurrentPB, functype, &FuncPtr );
247 		if ( rc == LDAP_SUCCESS ) {
248 			if ( FuncPtr != NULL )  {
249 				*pTmpFuncPtr = FuncPtr;
250 				pTmpFuncPtr++;
251 			}
252 			rc = slapi_pblock_get( pCurrentPB,
253 					SLAPI_IBM_PBLOCK, &pCurrentPB );
254 		}
255 	}
256 
257 	*pTmpFuncPtr = NULL;
258 
259 
260 done:
261 	if ( rc != LDAP_SUCCESS && *ppFuncPtrs != NULL ) {
262 		ch_free( *ppFuncPtrs );
263 		*ppFuncPtrs = NULL;
264 	}
265 
266 	return rc;
267 }
268 
269 /*********************************************************************
270  * Function Name:      createExtendedOp
271  *
272  * Description: Creates an extended operation structure and
273  *              initializes the fields
274  *
275  * Return value: A newly allocated structure or NULL
276  ********************************************************************/
277 ExtendedOp *
278 createExtendedOp()
279 {
280 	ExtendedOp *ret;
281 
282 	ret = (ExtendedOp *)slapi_ch_malloc(sizeof(ExtendedOp));
283 	ret->ext_oid.bv_val = NULL;
284 	ret->ext_oid.bv_len = 0;
285 	ret->ext_func = NULL;
286 	ret->ext_be = NULL;
287 	ret->ext_next = NULL;
288 
289 	return ret;
290 }
291 
292 
293 /*********************************************************************
294  * Function Name:      slapi_int_unregister_extop
295  *
296  * Description:        This routine removes the ExtendedOp structures
297  *					   asscoiated with a particular extended operation
298  *					   plugin.
299  *
300  * Input:              pBE - pointer to a backend structure
301  *                     opList - pointer to a linked list of extended
302  *                              operation structures
303  *                     pPB - pointer to a slapi parameter block
304  *
305  * Output:
306  *
307  * Return Value:       none
308  *
309  * Messages:           None
310  *********************************************************************/
311 void
312 slapi_int_unregister_extop(
313 	Backend *pBE,
314 	ExtendedOp **opList,
315 	Slapi_PBlock *pPB )
316 {
317 	ExtendedOp	*pTmpExtOp, *backExtOp;
318 	char		**pTmpOIDs;
319 	int		i;
320 
321 #if 0
322 	assert( pBE != NULL); /* unused */
323 #endif /* 0 */
324 	assert( opList != NULL );
325 	assert( pPB != NULL );
326 
327 	if ( *opList == NULL ) {
328 		return;
329 	}
330 
331 	slapi_pblock_get( pPB, SLAPI_PLUGIN_EXT_OP_OIDLIST, &pTmpOIDs );
332 	if ( pTmpOIDs == NULL ) {
333 		return;
334 	}
335 
336 	for ( i = 0; pTmpOIDs[i] != NULL; i++ ) {
337 		backExtOp = NULL;
338 		pTmpExtOp = *opList;
339 		for ( ; pTmpExtOp != NULL; pTmpExtOp = pTmpExtOp->ext_next) {
340 			int	rc;
341 			rc = strcasecmp( pTmpExtOp->ext_oid.bv_val,
342 					pTmpOIDs[ i ] );
343 			if ( rc == 0 ) {
344 				if ( backExtOp == NULL ) {
345 					*opList = pTmpExtOp->ext_next;
346 				} else {
347 					backExtOp->ext_next
348 						= pTmpExtOp->ext_next;
349 				}
350 
351 				ch_free( pTmpExtOp );
352 				break;
353 			}
354 			backExtOp = pTmpExtOp;
355 		}
356 	}
357 }
358 
359 
360 /*********************************************************************
361  * Function Name:      slapi_int_register_extop
362  *
363  * Description:        This routine creates a new ExtendedOp structure, loads
364  *                     in the extended op module and put the extended op function address
365  *                     in the structure. The function will not be executed in
366  *                     this routine.
367  *
368  * Input:              pBE - pointer to a backend structure
369  *                     opList - pointer to a linked list of extended
370  *                              operation structures
371  *                     pPB - pointer to a slapi parameter block
372  *
373  * Output:
374  *
375  * Return Value:       an LDAP return code
376  *
377  * Messages:           None
378  *********************************************************************/
379 int
380 slapi_int_register_extop(
381 	Backend *pBE,
382 	ExtendedOp **opList,
383 	Slapi_PBlock *pPB )
384 {
385 	ExtendedOp	*pTmpExtOp = NULL;
386 	SLAPI_FUNC	tmpFunc;
387 	char		**pTmpOIDs;
388 	int		rc = LDAP_OTHER;
389 	int		i;
390 
391 	if ( (*opList) == NULL ) {
392 		*opList = createExtendedOp();
393 		if ( (*opList) == NULL ) {
394 			rc = LDAP_NO_MEMORY;
395 			goto error_return;
396 		}
397 		pTmpExtOp = *opList;
398 
399 	} else {                        /* Find the end of the list */
400 		for ( pTmpExtOp = *opList; pTmpExtOp->ext_next != NULL;
401 				pTmpExtOp = pTmpExtOp->ext_next )
402 			; /* EMPTY */
403 		pTmpExtOp->ext_next = createExtendedOp();
404 		if ( pTmpExtOp->ext_next == NULL ) {
405 			rc = LDAP_NO_MEMORY;
406 			goto error_return;
407 		}
408 		pTmpExtOp = pTmpExtOp->ext_next;
409 	}
410 
411 	rc = slapi_pblock_get( pPB,SLAPI_PLUGIN_EXT_OP_OIDLIST, &pTmpOIDs );
412 	if ( rc != 0 ) {
413 		rc = LDAP_OTHER;
414 		goto error_return;
415 	}
416 
417 	rc = slapi_pblock_get(pPB,SLAPI_PLUGIN_EXT_OP_FN, &tmpFunc);
418 	if ( rc != 0 ) {
419 		rc = LDAP_OTHER;
420 		goto error_return;
421 	}
422 
423 	if ( (pTmpOIDs == NULL) || (tmpFunc == NULL) ) {
424 		rc = LDAP_OTHER;
425 		goto error_return;
426 	}
427 
428 	for ( i = 0; pTmpOIDs[i] != NULL; i++ ) {
429 		pTmpExtOp->ext_oid.bv_val = pTmpOIDs[i];
430 		pTmpExtOp->ext_oid.bv_len = strlen( pTmpOIDs[i] );
431 		pTmpExtOp->ext_func = tmpFunc;
432 		pTmpExtOp->ext_be = pBE;
433 		if ( pTmpOIDs[i + 1] != NULL ) {
434 			pTmpExtOp->ext_next = createExtendedOp();
435 			if ( pTmpExtOp->ext_next == NULL ) {
436 				rc = LDAP_NO_MEMORY;
437 				break;
438 			}
439 			pTmpExtOp = pTmpExtOp->ext_next;
440 		}
441 	}
442 
443 error_return:
444 	return rc;
445 }
446 
447 /*********************************************************************
448  * Function Name:      slapi_int_get_extop_plugin
449  *
450  * Description:        This routine gets the function address for a given function
451  *                     name.
452  *
453  * Input:
454  *                     funcName - name of the extended op function, ie. an OID.
455  *
456  * Output:             pFuncAddr - the function address of the requested function name.
457  *
458  * Return Values:      a pointer to a newly created ExtendOp structrue or
459  *                     NULL - function failed
460  *
461  * Messages:           None
462  *********************************************************************/
463 int
464 slapi_int_get_extop_plugin(
465 	struct berval *reqoid,
466 	SLAPI_FUNC *pFuncAddr )
467 {
468 	ExtendedOp	*pTmpExtOp;
469 
470 	assert( reqoid != NULL );
471 	assert( pFuncAddr != NULL );
472 
473 	*pFuncAddr = NULL;
474 
475 	if ( pGExtendedOps == NULL ) {
476 		return LDAP_OTHER;
477 	}
478 
479 	pTmpExtOp = pGExtendedOps;
480 	while ( pTmpExtOp != NULL ) {
481 		int	rc;
482 
483 		rc = strcasecmp( reqoid->bv_val, pTmpExtOp->ext_oid.bv_val );
484 		if ( rc == 0 ) {
485 			*pFuncAddr = pTmpExtOp->ext_func;
486 			break;
487 		}
488 		pTmpExtOp = pTmpExtOp->ext_next;
489 	}
490 
491 	return ( *pFuncAddr == NULL ? 1 : 0 );
492 }
493 
494 /***************************************************************************
495  * This function is similar to slapi_int_get_extop_plugin above. except it returns one OID
496  * per call. It is called from root_dse_info (root_dse.c).
497  * The function is a modified version of get_supported_extop (file extended.c).
498  ***************************************************************************/
499 struct berval *
500 slapi_int_get_supported_extop( int index )
501 {
502         ExtendedOp	*ext;
503 
504         for ( ext = pGExtendedOps ; ext != NULL && --index >= 0;
505 			ext = ext->ext_next) {
506                 ; /* empty */
507         }
508 
509         if ( ext == NULL ) {
510 		return NULL;
511 	}
512 
513         return &ext->ext_oid ;
514 }
515 
516 /*********************************************************************
517  * Function Name:      slapi_int_load_plugin
518  *
519  * Description:        This routine loads the specified DLL, gets and executes the init function
520  *                     if requested.
521  *
522  * Input:
523  *                     pPlugin - a pointer to a Slapi_PBlock struct which will be passed to
524  *                               the DLL init function.
525  *                     path - path name of the DLL to be load.
526  *                     initfunc - either the DLL initialization function or an OID of the
527  *                                loaded extended operation.
528  *                     doInit - if it is TRUE, execute the init function, otherwise, save the
529  *                              function address but not execute it.
530  *
531  * Output:             pInitFunc - the function address of the loaded function. This param
532  *                                 should be not be null if doInit is FALSE.
533  *                     pLdHandle - handle returned by lt_dlopen()
534  *
535  * Return Values:      LDAP_SUCCESS, LDAP_LOCAL_ERROR
536  *
537  * Messages:           None
538  *********************************************************************/
539 
540 static int
541 slapi_int_load_plugin(
542 	Slapi_PBlock	*pPlugin,
543 	const char	*path,
544 	const char	*initfunc,
545 	int		doInit,
546 	SLAPI_FUNC	*pInitFunc,
547 	lt_dlhandle	*pLdHandle )
548 {
549 	int		rc = LDAP_SUCCESS;
550 	SLAPI_FUNC	fpInitFunc = NULL;
551 
552 	assert( pLdHandle != NULL );
553 
554 	if ( lt_dlinit() ) {
555 		return LDAP_LOCAL_ERROR;
556 	}
557 
558 	/* load in the module */
559 	*pLdHandle = lt_dlopen( path );
560 	if ( *pLdHandle == NULL ) {
561 		fprintf( stderr, "failed to load plugin %s: %s\n",
562 			 path, lt_dlerror() );
563 		return LDAP_LOCAL_ERROR;
564 	}
565 
566 	fpInitFunc = (SLAPI_FUNC)lt_dlsym( *pLdHandle, initfunc );
567 	if ( fpInitFunc == NULL ) {
568 		fprintf( stderr, "failed to find symbol %s in plugin %s: %s\n",
569 			 initfunc, path, lt_dlerror() );
570 		lt_dlclose( *pLdHandle );
571 		return LDAP_LOCAL_ERROR;
572 	}
573 
574 	if ( doInit ) {
575 		rc = ( *fpInitFunc )( pPlugin );
576 		if ( rc != LDAP_SUCCESS ) {
577 			lt_dlclose( *pLdHandle );
578 		}
579 
580 	} else {
581 		*pInitFunc = fpInitFunc;
582 	}
583 
584 	return rc;
585 }
586 
587 /*
588  * Special support for computed attribute plugins
589  */
590 int
591 slapi_int_call_plugins(
592 	Backend		*be,
593 	int		funcType,
594 	Slapi_PBlock	*pPB )
595 {
596 
597 	int rc = 0;
598 	SLAPI_FUNC *pGetPlugin = NULL, *tmpPlugin = NULL;
599 
600 	if ( pPB == NULL ) {
601 		return 1;
602 	}
603 
604 	rc = slapi_int_get_plugins( be, funcType, &tmpPlugin );
605 	if ( rc != LDAP_SUCCESS || tmpPlugin == NULL ) {
606 		/* Nothing to do, front-end should ignore. */
607 		return rc;
608 	}
609 
610 	for ( pGetPlugin = tmpPlugin ; *pGetPlugin != NULL; pGetPlugin++ ) {
611 		rc = (*pGetPlugin)(pPB);
612 
613 		/*
614 		 * Only non-postoperation plugins abort processing on
615 		 * failure (confirmed with SLAPI specification).
616 		 */
617 		if ( !SLAPI_PLUGIN_IS_POST_FN( funcType ) && rc != 0 ) {
618 			/*
619 			 * Plugins generally return negative error codes
620 			 * to indicate failure, although in the case of
621 			 * bind plugins they may return SLAPI_BIND_xxx
622 			 */
623 			break;
624 		}
625 	}
626 
627 	slapi_ch_free( (void **)&tmpPlugin );
628 
629 	return rc;
630 }
631 
632 int
633 slapi_int_read_config(
634 	Backend		*be,
635 	const char	*fname,
636 	int		lineno,
637 	int		argc,
638 	char		**argv )
639 {
640 	int		iType = -1;
641 	int		numPluginArgc = 0;
642 
643 	if ( argc < 4 ) {
644 		fprintf( stderr,
645 			"%s: line %d: missing arguments "
646 			"in \"plugin <plugin_type> <lib_path> "
647 			"<init_function> [<arguments>]\" line\n",
648 			fname, lineno );
649 		return 1;
650 	}
651 
652 	/* automatically instantiate overlay if necessary */
653 	if ( !slapi_over_is_inst( be ) ) {
654 		ConfigReply cr = { 0 };
655 		if ( slapi_over_config( be, &cr ) != 0 ) {
656 			fprintf( stderr, "Failed to instantiate SLAPI overlay: "
657 				"err=%d msg=\"%s\"\n", cr.err, cr.msg );
658 			return -1;
659 		}
660 	}
661 
662 	if ( strcasecmp( argv[1], "preoperation" ) == 0 ) {
663 		iType = SLAPI_PLUGIN_PREOPERATION;
664 	} else if ( strcasecmp( argv[1], "postoperation" ) == 0 ) {
665 		iType = SLAPI_PLUGIN_POSTOPERATION;
666 	} else if ( strcasecmp( argv[1], "extendedop" ) == 0 ) {
667 		iType = SLAPI_PLUGIN_EXTENDEDOP;
668 	} else if ( strcasecmp( argv[1], "object" ) == 0 ) {
669 		iType = SLAPI_PLUGIN_OBJECT;
670 	} else {
671 		fprintf( stderr, "%s: line %d: invalid plugin type \"%s\".\n",
672 				fname, lineno, argv[1] );
673 		return 1;
674 	}
675 
676 	numPluginArgc = argc - 4;
677 
678 	if ( iType == SLAPI_PLUGIN_PREOPERATION ||
679 		  	iType == SLAPI_PLUGIN_EXTENDEDOP ||
680 			iType == SLAPI_PLUGIN_POSTOPERATION ||
681 			iType == SLAPI_PLUGIN_OBJECT ) {
682 		int rc;
683 		Slapi_PBlock *pPlugin;
684 
685 		pPlugin = plugin_pblock_new( iType, numPluginArgc, argv );
686 		if (pPlugin == NULL) {
687 			return 1;
688 		}
689 
690 		if (iType == SLAPI_PLUGIN_EXTENDEDOP) {
691 			rc = slapi_int_register_extop(be, &pGExtendedOps, pPlugin);
692 			if ( rc != LDAP_SUCCESS ) {
693 				slapi_pblock_destroy( pPlugin );
694 				return 1;
695 			}
696 		}
697 
698 		rc = slapi_int_register_plugin( be, pPlugin );
699 		if ( rc != LDAP_SUCCESS ) {
700 			if ( iType == SLAPI_PLUGIN_EXTENDEDOP ) {
701 				slapi_int_unregister_extop( be, &pGExtendedOps, pPlugin );
702 			}
703 			slapi_pblock_destroy( pPlugin );
704 			return 1;
705 		}
706 	}
707 
708 	return 0;
709 }
710 
711 void
712 slapi_int_plugin_unparse(
713 	Backend *be,
714 	BerVarray *out
715 )
716 {
717 	Slapi_PBlock *pp;
718 	int i, j;
719 	char **argv, ibuf[32], *ptr;
720 	struct berval idx, bv;
721 
722 	*out = NULL;
723 	idx.bv_val = ibuf;
724 	i = 0;
725 
726 	for ( pp = SLAPI_BACKEND_PBLOCK( be );
727 	      pp != NULL;
728 	      slapi_pblock_get( pp, SLAPI_IBM_PBLOCK, &pp ) )
729 	{
730 		slapi_pblock_get( pp, SLAPI_X_CONFIG_ARGV, &argv );
731 		if ( argv == NULL ) /* could be dynamic plugin */
732 			continue;
733 		idx.bv_len = snprintf( idx.bv_val, sizeof( ibuf ), "{%d}", i );
734 		if ( idx.bv_len >= sizeof( ibuf ) ) {
735 			/* FIXME: just truncating by now */
736 			idx.bv_len = sizeof( ibuf ) - 1;
737 		}
738 		bv.bv_len = idx.bv_len;
739 		for (j=1; argv[j]; j++) {
740 			bv.bv_len += strlen(argv[j]);
741 			if ( j ) bv.bv_len++;
742 		}
743 		bv.bv_val = ch_malloc( bv.bv_len + 1 );
744 		ptr = lutil_strcopy( bv.bv_val, ibuf );
745 		for (j=1; argv[j]; j++) {
746 			if ( j ) *ptr++ = ' ';
747 			ptr = lutil_strcopy( ptr, argv[j] );
748 		}
749 		ber_bvarray_add( out, &bv );
750 	}
751 }
752 
753