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