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