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 37static 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 */ 41static 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 66static Slapi_PBlock * 67plugin_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 119done: 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 *********************************************************************/ 147int 148slapi_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 *********************************************************************/ 191int 192slapi_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 257done: 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 ********************************************************************/ 274ExtendedOp * 275createExtendedOp() 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 *********************************************************************/ 308void 309slapi_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 *********************************************************************/ 376int 377slapi_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 440error_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 *********************************************************************/ 460int 461slapi_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 ***************************************************************************/ 496struct berval * 497slapi_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 537static int 538slapi_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 */ 587int 588slapi_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 629int 630slapi_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 708void 709slapi_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