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