1 /* $NetBSD: plugin.c,v 1.2 2020/08/11 13:15:42 christos Exp $ */ 2 3 /* $OpenLDAP$ */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 2002-2020 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.2 2020/08/11 13:15:42 christos 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