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