plugin.c revision 1.1.1.4 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