]> git.sur5r.net Git - openldap/blob - servers/slapd/tools/centipede.c
include portable.h
[openldap] / servers / slapd / tools / centipede.c
1 /* centipede.c - generate and install indexing information (view w/tabstop=4) */
2
3 #include "portable.h"
4
5 #include <stdio.h>
6 #include <string.h>
7 #include <ctype.h>
8 #include <sys/time.h>
9 #include <lber.h>
10 #include <ldap.h>
11 #include <ldapconfig.h>
12 #include <ldbm.h>
13
14 #define DEFAULT_LDAPFILTER      "(objectclass=*)"
15
16 #define CENTROID_VALUE  1
17 #define CENTROID_WORD   2
18
19 #define CENTROID_RELATIVE       1
20 #define CENTROID_FULL           2
21
22 #define WORD_BREAKS     " -',.()!;:&$%*\"/\\+_<>=?[]|^~"
23
24 char    *centdir;
25 int             ldbmcachesize;
26 int             centroidvalues;
27 int             centroidtype;
28 int             doweights;
29 char    *ldaphost;
30 char    *srcldapbinddn;
31 char    *srcldappasswd;
32 char    *destldapbinddn;
33 char    *destldappasswd;
34 char    *ldapbase;
35 int             srcldapauthmethod;
36 int             destldapauthmethod;
37 int             verbose;
38 int             not;
39
40 static LDAP             *start_ldap_search();
41 static LDAP             *bind_to_destination_ldap();
42 static int              create_tmp_files();
43 static int              generate_new_centroids();
44 static LDAPMod  **diff_centroids();
45 static LDAPMod  **full_centroid();
46 static char             **charray_add_dup();
47
48 static void usage( char *name )
49 {
50         fprintf( stderr, "usage: %s [options] -s url -d url attributes\n", name );
51         fprintf( stderr, "where:\n" );
52         fprintf( stderr, "\t-s url\t\t[[ldap://][host[:port]]/]searchbasedn\n");
53         fprintf( stderr, "\t-d url\t\t[[ldap://][host[:port]]/]centroidentrydn\n");
54         fprintf( stderr, "options:\n" );
55         fprintf( stderr, "\t-v \t\tturn on verbose mode\n" );
56         fprintf( stderr, "\t-n \t\tgenerate, but do not install index info\n" );
57         fprintf( stderr, "\t-f filter\tentry selection filter\n" );
58         fprintf( stderr, "\t-F \t\tgenerate a full centroid\n" );
59         fprintf( stderr, "\t-R \t\tgenerate a relative centroid\n" );
60         fprintf( stderr, "\t-w \t\tgenerate a word-based centroid\n" );
61         fprintf( stderr, "\t-t directory\tcentroid directory\n" );
62         fprintf( stderr, "\t-b binddn\tsource bind dn\n" );
63         fprintf( stderr, "\t-p passwd\tsource bind passwd (for simple auth)\n" );
64         fprintf( stderr, "\t-m authmethod\tsource authmethod \"simple\" or \"kerberos\"\n" );
65         fprintf( stderr, "\t-B binddn\tdestination bind dn\n" );
66         fprintf( stderr, "\t-P passwd\tdestination bind passwd (for simple auth)\n" );
67         fprintf( stderr, "\t-M authmethod\tdestination authmethod \"simple\" or \"kerberos\"\n" );
68         fprintf( stderr, "\t-c size\t\tldbm cache size\n" );
69 }
70
71 main( int argc, char **argv )
72 {
73         char            *ldapfilter, *ldapref;
74         char            *ldapsrcurl, *ldapdesturl;
75         LDAP            *ld;
76         LDAPMod         **mods;
77         char            **attrs;
78         char            **tmpfile;
79         LDBM            *ldbm;
80         LDBM            oldbm;
81         char            buf[BUFSIZ];
82         int                     i, j, k, count;
83         char            *s;
84         extern int      optind;
85         extern char     *optarg;
86
87         ldapsrcurl = NULL;
88         ldapdesturl = NULL;
89         ldaphost = LDAPHOST;
90         ldapbase = DEFAULT_BASE;
91         srcldapauthmethod = LDAP_AUTH_SIMPLE;
92         destldapauthmethod = LDAP_AUTH_SIMPLE;
93         srcldapbinddn = NULL;
94         srcldappasswd = NULL;
95         destldapbinddn = NULL;
96         destldappasswd = NULL;
97         ldapfilter = DEFAULT_LDAPFILTER;
98         centroidvalues = CENTROID_VALUE;
99         centroidtype = CENTROID_RELATIVE;
100         centdir = NULL;
101         tmpfile = NULL;
102         ldbmcachesize = 0;
103
104         while ( (i = getopt( argc, argv, "s:d:c:b:B:f:FRWp:P:m:M:t:vwn" ))
105             != EOF ) {
106                 switch ( i ) {
107                 case 's':       /* source url [[ldap://][host[:port]]/]basedn */
108                         ldapsrcurl = strdup( optarg );
109                         break;
110
111                 case 'd':       /* destination url [[ldap://][host[:port]]/]entrydn */
112                         ldapdesturl = strdup( optarg );
113                         break;
114
115                 case 'f':       /* specify a filter */
116                         ldapfilter = strdup( optarg );
117                         break;
118
119                 case 'F':       /* generate full centroid */
120                         centroidtype = CENTROID_FULL;
121                         break;
122
123                 case 'R':       /* generate relative centroid */
124                         centroidtype = CENTROID_RELATIVE;
125                         break;
126
127                 case 'w':       /* generate word centroid */
128                         centroidvalues = CENTROID_WORD;
129                         break;
130
131                 case 'W':       /* generate weights */
132                         doweights = 1;
133                         break;
134
135                 case 't':       /* temp file directory */
136                         centdir = strdup( optarg );
137                         break;
138
139                 case 'b':       /* src bind dn */
140                         srcldapbinddn = strdup( optarg );
141                         break;
142
143                 case 'p':       /* src bind password */
144                         srcldappasswd = strdup( optarg );
145                         while ( *optarg )
146                                 *optarg++ = 'x';
147                         break;
148
149                 case 'B':       /* dest bind dn */
150                         destldapbinddn = strdup( optarg );
151                         break;
152
153                 case 'P':       /* dest bind password */
154                         destldappasswd = strdup( optarg );
155                         while ( *optarg )
156                                 *optarg++ = 'x';
157                         break;
158
159                 case 'm':       /* src bind method */
160                         if ( strcasecmp( optarg, "simple" ) == 0 ) {
161                                 srcldapauthmethod = LDAP_AUTH_SIMPLE;
162                         } else if ( strcasecmp( optarg, "kerberos" ) == 0 ) {
163                                 srcldapauthmethod = LDAP_AUTH_KRBV4;
164                         } else {
165                                 fprintf( stderr, "%s: unknown auth method\n", optarg );
166                                 fprintf( stderr, "expecting \"simple\" or \"kerberos\"\n",
167                                     optarg );
168                                 exit( 1 );
169                         }
170                         break;
171
172                 case 'M':       /* dest bind method */
173                         if ( strcasecmp( optarg, "simple" ) == 0 ) {
174                                 destldapauthmethod = LDAP_AUTH_SIMPLE;
175                         } else if ( strcasecmp( optarg, "kerberos" ) == 0 ) {
176                                 destldapauthmethod = LDAP_AUTH_KRBV4;
177                         } else {
178                                 fprintf( stderr, "%s: unknown auth method\n", optarg );
179                                 fprintf( stderr, "expecting \"simple\" or \"kerberos\"\n",
180                                     optarg );
181                                 exit( 1 );
182                         }
183                         break;
184
185                 case 'c':       /* ldbm cache size */
186                         ldbmcachesize = atoi( optarg );
187                         break;
188
189                 case 'v':       /* turn on verbose mode */
190                         verbose++;
191                         break;
192
193                 case 'n':       /* don't actually install index info */
194                         not++;
195                         break;
196
197                 default:
198                         usage( argv[0] );
199                         exit( 1 );
200                 }
201         }
202         if ( optind == argc || ldapsrcurl == NULL || ldapdesturl == NULL ) {
203                 usage( argv[0] );
204                 exit( 1 );
205         }
206         attrs = &argv[optind];
207
208         /*
209          * open the ldap connection and start searching for the entries
210          * we will use to generate the centroids.
211          */
212
213         if ( (ld = start_ldap_search( ldapsrcurl, ldapfilter, attrs )) == NULL ) {
214                 fprintf( stderr, "could not initiate ldap search\n" );
215                 exit( 1 );
216         }
217
218         if ( create_tmp_files( attrs, &tmpfile, &ldbm ) != 0 ) {
219                 fprintf( stderr, "could not create temp files\n" );
220                 exit( 1 );
221         }
222
223         /*
224          * go through the entries returned, building a centroid for each
225          * attribute as we go.
226          */
227
228         if ( (count = generate_new_centroids( ld, attrs, ldbm )) < 1 ) {
229                 if ( count == 0 ) {
230                     fprintf( stderr, "no entries matched\n" );
231                     exit( 0 );
232                 } else {
233                     fprintf( stderr, "could not generate new centroid\n" );
234                     exit( 1 );
235                 }
236         }
237
238         /*
239          * for each centroid we generated above, compare to the existing
240          * centroid, if any, and produce adds and deletes, or produce
241          * an entirely new centroid. in either case, update the "current"
242          * centroid version with the new one we just generated.
243          */
244
245         if ( (ld = bind_to_destination_ldap( ldapsrcurl, ldapdesturl )) == NULL ) {
246                 fprintf( stderr,
247                   "could not bind to index server, or could not create index entry\n" );
248                 exit( 1 );
249         }
250
251         for ( i = 0; ldbm[i] != NULL; i++ ) {
252                 /* generate the name of the existing centroid, if any */
253                 s = strrchr( tmpfile[i], '/' );
254                 *s = '\0';
255                 sprintf( buf, "%s/cent.%s", tmpfile[i], attrs[i] );
256                 *s = '/';
257
258                 /* generate the full centroid changes */
259                 if ( centroidtype == CENTROID_FULL || (oldbm = ldbm_open( buf,
260                   LDBM_WRITER, 0, ldbmcachesize )) == NULL ) {
261                         if ( (mods = full_centroid( attrs[i], ldbm[i], count )) == NULL ) {
262                                 fprintf( stderr, "could not produce full centroid for %s\n",
263                                   attrs[i] );
264                                 continue;
265                         }
266
267                 /* generate the differential centroid changes */
268                 } else {
269                         if ( (mods = diff_centroids( attrs[i], oldbm, ldbm[i], count ))
270                           == NULL ) {
271                                 fprintf( stderr, "could not diff centroids\n" );
272                                 ldbm_close( oldbm );
273                                 continue;
274                         }
275                         ldbm_close( oldbm );
276                 }
277
278                 if ( verbose > 1 ) {
279                         printf("changes:\n");
280                         for ( j = 0; mods[j] != NULL; j++ ) {
281                                 switch( mods[j]->mod_op ) {
282                                 case LDAP_MOD_ADD:
283                                         printf( "\tadd: %s\n",mods[j]->mod_type );
284                                         break;
285                                 case LDAP_MOD_DELETE:
286                                         printf( "\tdelete: %s\n",mods[j]->mod_type );
287                                         break;
288                                 case LDAP_MOD_REPLACE:
289                                         printf( "\treplace: %s\n",mods[j]->mod_type );
290                                         break;
291                                 }
292                                 if ( mods[j]->mod_values != NULL ) {
293                                         for ( k = 0; mods[j]->mod_values[k] != NULL; k++ ) {
294                                                 printf( "\t\t%s\n", mods[j]->mod_values[k] );
295                                         }
296                                 }
297                         }
298                         printf("end changes:\n");
299                 }
300
301                 if ( verbose ) {
302                         printf( "%sModifying centroid...", not ? "Not " : "" );
303                         fflush( stdout );
304                 }
305
306                 /* attempt to make the changes to the index server entry */
307                 if ( !not && ldap_modify_s( ld, ldapbase, mods ) != LDAP_SUCCESS ) {
308                         fprintf( stderr, "could not apply centroid modification for %s\n",
309                           attrs[i] );
310                         ldap_perror( ld, ldapbase );
311                 }
312                 ldap_mods_free( mods, 1 );
313
314                 if ( verbose ) {
315                         printf( "\n" );
316                         fflush( stdout );
317                 }
318
319                 /* move the new centroid into the old one's place */
320                 if ( ! not ) {
321                         (void) unlink( buf );
322                         if ( link( tmpfile[i], buf ) != 0 ) {
323                                 perror( "link" );
324                                 fprintf( stderr, "could not rename %s to %s\n", buf,
325                                   tmpfile[i] );
326                                 continue;
327                         }
328                 }
329                 (void) unlink( tmpfile[i] );
330         }
331
332         /* clean up */
333         for ( i = 0; attrs[i] != NULL; i++ ) {
334                 ldbm_close( ldbm[i] );
335                 free( tmpfile[i] );
336         }
337         free( ldbm );
338         free( tmpfile );
339
340         exit( 0 );
341 }
342
343 /*
344  * open an ldap connection, bind, and initiate the search
345  */
346
347 static LDAP *
348 start_ldap_search(
349         char    *ldapsrcurl,
350         char    *ldapfilter,
351         char    **attrs
352 )
353 {
354         LDAP    *ld;
355         char    *s, *s2;
356         int             i;
357
358         if ( strncmp( ldapsrcurl, "ldap://", 7 ) == 0 ) {
359                 s = ldapsrcurl + 7;
360         }
361         if ( (s2 = strchr( s, '/' )) == NULL ) {
362                 ldapbase = strdup( s );
363         } else {
364                 if ( *s != '/' ) {
365                         *s2 = '\0';
366                         ldaphost = strdup( s );
367                         *s2 = '/';
368                 }
369                 ldapbase = strdup( s2 + 1 );
370         }
371
372         if ( verbose ) {
373                 printf( "Base: %s\n", ldapbase );
374                 printf( "Attributes:" );
375                 for ( i = 0; attrs[i] != NULL; i++ ) {
376                         printf( " %s", attrs[i] );
377                 }
378                 printf( "\n" );
379                 printf( "Binding to source LDAP server..." );
380                 fflush( stdout );
381         }
382
383         if ( (ld = ldap_open( ldaphost, LDAP_PORT )) == NULL ) {
384                 perror( "ldap_open" );
385                 return( NULL );
386         }
387
388         if ( ldap_bind_s( ld, srcldapbinddn, srcldappasswd, srcldapauthmethod )
389           != LDAP_SUCCESS) {
390                 ldap_perror( ld, "ldap_bind_s" );
391                 ldap_unbind( ld );
392                 return( NULL );
393         }
394
395         printf( "\nInitiating search..." );
396         if ( ldap_search( ld, ldapbase, LDAP_SCOPE_SUBTREE, ldapfilter, attrs, 0 )
397           == -1 ) {
398                 ldap_perror( ld, "ldap_search" );
399                 ldap_unbind( ld );
400                 return( NULL );
401         }
402
403         if ( verbose ) {
404                 printf( "\n" );
405         }
406
407         return( ld );
408 }
409
410 /*
411  * create the temporary ldbm files we will use to hold the new centroids
412  */
413
414 static int
415 create_tmp_files(
416         char    **attrs,
417         char    ***tmpfile,
418         LDBM    **ldbm
419 )
420 {
421         int     i;
422
423         for ( i = 0; attrs[i] != NULL; i++ )
424                 ;       /* NULL */
425         i++;
426
427         if ( (*tmpfile = (char **) malloc( i * sizeof(char *) )) == NULL ) {
428                 perror( "malloc" );
429                 return( -1 );
430         }
431         if ( (*ldbm = (LDBM *) malloc( i * sizeof(LDBM) )) == NULL ) {
432                 perror( "malloc" );
433                 return( -1 );
434         }
435         for ( i = 0; attrs[i] != NULL; i++ ) {
436                 if ( ((*tmpfile)[i] = tempnam( centdir, NULL )) == NULL ) {
437                         perror( "tmpnam" );
438                         return( -1 );
439                 }
440
441                 if ( ((*ldbm)[i] = ldbm_open( (*tmpfile)[i], LDBM_WRCREAT, 0600,
442                   ldbmcachesize )) == NULL ) {
443                         fprintf( stderr, "ldbm_open of \"%s\" failed\n", (*tmpfile)[i] );
444                         perror( "ldbm_open" );
445                         return( -1 );
446                 }
447         }
448         (*tmpfile)[i] = NULL;
449         (*ldbm)[i] = NULL;
450
451         return( 0 );
452 }
453
454 /*
455  * step through each entry returned from the search and generate
456  * the appropriate centroid values.
457  */
458
459 static int
460 generate_new_centroids(
461         LDAP    *ld,
462         char    **attrs,
463         LDBM    *ldbm
464 )
465 {
466         Datum           key, data;
467         int                     rc, i, j, count;
468         LDAPMessage     *res, *e;
469         char            *dn, *s, *w;
470         char            **val;
471         char            last;
472
473         if ( verbose ) {
474                 printf( "Generating new centroids for..." );
475                 fflush( stdout );
476         }
477
478         data.dptr = "";
479         data.dsize = 1;
480         count = 0;
481         while ( (rc = ldap_result( ld, LDAP_RES_ANY, 0, NULL, &res ))
482           == LDAP_RES_SEARCH_ENTRY ) {
483                 count++;
484                 e = ldap_first_entry( ld, res );
485                 dn = ldap_get_dn( ld, e );
486
487                 /* for each attr we want to generate a centroid for */
488                 for ( i = 0; attrs[i] != NULL; i++ ) {
489                         if ( (val = ldap_get_values( ld, e, attrs[i] )) == NULL ) {
490                                 continue;
491                         }
492
493                         /* for each value */
494                         for ( j = 0; val[j] != NULL; j++ ) {
495                                 /* normalize the value */
496                                 for ( s = val[j]; *s; s++ ) {
497                                         if ( isascii( *s ) ) {
498                                                 *s = tolower( *s );
499                                         }
500                                         last = *s;
501                                 }
502                                 if ( isascii( last ) && isdigit( last ) ) {
503                                         continue;
504                                 }
505
506                                 /* generate a value-based centroid */
507                                 if ( centroidvalues == CENTROID_VALUE ) {
508                                         key.dptr = val[j];
509                                         key.dsize = strlen( key.dptr ) + 1;
510                                         (void) ldbm_store( ldbm[i], key, data, LDBM_INSERT );
511
512                                 /* generate a word-based centroid */
513                                 } else {
514                                         for ( w = strtok( val[j], WORD_BREAKS ); w != NULL;
515                                           w = strtok( NULL, WORD_BREAKS ) ) {
516                                                 key.dptr = w;
517                                                 key.dsize = strlen( key.dptr ) + 1;
518                                                 (void) ldbm_store( ldbm[i], key, data, LDBM_INSERT );
519                                         }
520                                 }
521                         }
522                         ldap_value_free( val );
523                 }
524                 free( dn );
525                 ldap_msgfree( res );
526         }
527         ldap_msgfree( res );
528         ldap_unbind( ld );
529
530         if ( verbose ) {
531                 printf( "%d entries\n", count );
532         }
533
534         return( count );
535 }
536
537 /*
538  * compare the old and new centroids, generating the appropriate add
539  * and delete operations. if the underlying database is ordered, we
540  * can do this more efficiently.
541  */
542
543 static LDAPMod **
544 diff_centroids(
545         char    *attr,
546         LDBM    oldbm,
547         LDBM    nldbm,
548     int         nentries
549 )
550 {
551         Datum   okey, nkey;
552         Datum   olast, nlast;
553         Datum   lastkey, key;
554         Datum   data;
555         int             rc;
556         LDAPMod **mods;
557         char    **avals, **dvals;
558         int             amax, acur, dmax, dcur;
559         char    **vals;
560
561 #ifdef LDBM_USE_DB2
562         DBC     *ocursorp;
563         DBC     *ncursorp;
564 #endif /* LDBM_USE_DB2 */
565
566         if ( verbose ) {
567                 printf( "Generating mods for differential %s centroid...", attr );
568                 fflush( stdout );
569         }
570
571         if ( (mods = (LDAPMod **) malloc( sizeof(LDAPMod *) * 4 )) == NULL ||
572              (mods[0] = (LDAPMod *) malloc( sizeof(LDAPMod) )) == NULL ||
573              (mods[1] = (LDAPMod *) malloc( sizeof(LDAPMod) )) == NULL ||
574              (mods[2] = (LDAPMod *) malloc( sizeof(LDAPMod) )) == NULL ||
575              (vals = (char **) malloc( 2 * sizeof(char *) )) == NULL ||
576                  (vals[0] = (char *) malloc( 20 )) == NULL )
577         {
578                 perror( "malloc" );
579                 exit( -1 );
580         }
581         /* add values in mods[0] */
582         mods[0]->mod_op = LDAP_MOD_ADD;
583         mods[0]->mod_type = attr;
584         mods[0]->mod_values = NULL;
585         avals = NULL;
586         acur = amax = 0;
587         /* delete values in mods[1] */
588         mods[1]->mod_op = LDAP_MOD_DELETE;
589         mods[1]->mod_type = attr;
590         mods[1]->mod_values = NULL;
591         dvals = NULL;
592         dcur = dmax = 0;
593         /* number of entries in mods[2] */
594         sprintf( vals[0], "%d", nentries );
595         vals[1] = NULL;
596         mods[2]->mod_op = LDAP_MOD_REPLACE;
597         mods[2]->mod_type = "nentries";
598         mods[2]->mod_values = vals;
599         /* null terminate list of mods */
600         mods[3] = NULL;
601
602 #ifdef LDBM_ORDERED
603         /*
604          * if the underlying database is ordered, we can do a more efficient
605          * dual traversal, yielding O(N) performance.
606          */
607
608         olast.dptr = NULL;
609         nlast.dptr = NULL;
610 #ifdef LDBM_USE_DB2
611         for ( okey = ldbm_firstkey( oldbm, &ocursorp ),
612                         nkey = ldbm_firstkey( nldbm, &ncursorp );
613               okey.dptr != NULL && nkey.dptr != NULL; )
614 #else
615         for ( okey = ldbm_firstkey( oldbm ), nkey = ldbm_firstkey( nldbm );
616               okey.dptr != NULL && nkey.dptr != NULL; )
617 #endif
618         {
619                 rc = strcmp( okey.dptr, nkey.dptr );
620
621                 if ( rc == 0 ) {
622                         /* value is in both places - leave it */
623                         if ( olast.dptr != NULL ) {
624                                 ldbm_datum_free( oldbm, olast );
625                         }
626                         olast = okey;
627                         if ( nlast.dptr != NULL ) {
628                                 ldbm_datum_free( nldbm, nlast );
629                         }
630                         nlast = nkey;
631
632 #ifdef LDBM_USE_DB2
633                         okey = ldbm_nextkey( oldbm, olast, ocursorp );
634                         nkey = ldbm_nextkey( nldbm, nlast, ncursorp );
635 #else
636                         okey = ldbm_nextkey( oldbm, olast );
637                         nkey = ldbm_nextkey( nldbm, nlast );
638 #endif
639                 } else if ( rc > 0 ) {
640                         /* new value is not in old centroid - add it */
641                         if ( charray_add_dup( &avals, &acur, &amax, nkey.dptr ) == NULL ) {
642                                 ldap_mods_free( mods, 1 );
643                                 return( NULL );
644                         }
645
646                         if ( nlast.dptr != NULL ) {
647                                 ldbm_datum_free( nldbm, nlast );
648                         }
649                         nlast = nkey;
650
651 #ifdef LDBM_USE_DB2
652                         nkey = ldbm_nextkey( nldbm, nlast, ncursorp );
653 #else
654                         nkey = ldbm_nextkey( nldbm, nlast );
655 #endif
656                 } else {
657                         /* old value is not in new centroid - delete it */
658                         if ( charray_add_dup( &dvals, &dcur, &dmax, okey.dptr ) == NULL ) {
659                                 ldap_mods_free( mods, 1 );
660                                 return( NULL );
661                         }
662
663                         if ( olast.dptr != NULL ) {
664                                 ldbm_datum_free( oldbm, olast );
665                         }
666                         olast = okey;
667
668 #ifdef LDBM_USE_DB2
669                         okey = ldbm_nextkey( oldbm, olast, ocursorp );
670 #else
671                         okey = ldbm_nextkey( oldbm, olast );
672 #endif
673                 }
674         }
675
676         while ( okey.dptr != NULL ) {
677                 if ( charray_add_dup( &dvals, &dcur, &dmax, okey.dptr ) == NULL ) {
678                         ldap_mods_free( mods, 1 );
679                         return( NULL );
680                 }
681
682 #ifdef LDBM_USE_DB2
683                 okey = ldbm_nextkey( oldbm, olast, ocursorp );
684 #else
685                 okey = ldbm_nextkey( oldbm, olast );
686 #endif
687                 if ( olast.dptr != NULL ) {
688                         ldbm_datum_free( oldbm, olast );
689                 }
690                 olast = okey;
691         }
692         if ( olast.dptr != NULL ) {
693                 ldbm_datum_free( oldbm, olast );
694         }
695         while ( nkey.dptr != NULL ) {
696                 if ( charray_add_dup( &avals, &acur, &amax, nkey.dptr ) == NULL ) {
697                         ldap_mods_free( mods, 1 );
698                         return( NULL );
699                 }
700
701 #ifdef LDBM_USE_DB2
702                 nkey = ldbm_nextkey( nldbm, nlast, ncursorp );
703 #else
704                 nkey = ldbm_nextkey( nldbm, nlast );
705 #endif
706                 if ( nlast.dptr != NULL ) {
707                         ldbm_datum_free( nldbm, nlast );
708                 }
709                 nlast = nkey;
710         }
711         if ( nlast.dptr != NULL ) {
712                 ldbm_datum_free( nldbm, nlast );
713         }
714 #else
715         /*
716          * if the underlying database is not ordered, we have to
717          * generate list of values to add by stepping through all new
718          * values and looking them up in the old centroid (not there => add),
719          * then stepping through all old values and looking them up in the
720          * new centroid (not there => delete). this yields O(Nf(N)) performance,
721          * where f(N) is the order to retrieve a single item.
722          */
723
724         /* generate list of values to add */
725         lastkey.dptr = NULL;
726 #ifdef LDBM_USE_DB2
727         for ( key = ldbm_firstkey( nldbm, &ncursorp ); key.dptr != NULL;
728           key = ldbm_nextkey( nldbm, lastkey, ncursorp ) )
729 #else
730         for ( key = ldbm_firstkey( nldbm ); key.dptr != NULL;
731           key = ldbm_nextkey( nldbm, lastkey ) )
732 #endif
733         {
734                 /* see if it's in the old one */
735                 data = ldbm_fetch( oldbm, key );
736
737                 /* not there - add it */
738                 if ( data.dptr == NULL ) {
739                         if ( charray_add_dup( &avals, &acur, &amax, key.dptr ) == NULL ) {
740                                 ldap_mods_free( mods, 1 );
741                                 return( NULL );
742                         }
743                 } else {
744                         ldbm_datum_free( oldbm, data );
745                 }
746                 if ( lastkey.dptr != NULL ) {
747                         ldbm_datum_free( nldbm, lastkey );
748                 }
749                 lastkey = key;
750         }
751         if ( lastkey.dptr != NULL ) {
752                 ldbm_datum_free( nldbm, lastkey );
753         }
754
755         /* generate list of values to delete */
756         lastkey.dptr = NULL;
757 #ifdef LDBM_USE_DB2
758         for ( key = ldbm_firstkey( oldbm, &ocursorp ); key.dptr != NULL;
759           key = ldbm_nextkey( oldbm, lastkey, ocursorp ) )
760 #else
761         for ( key = ldbm_firstkey( oldbm ); key.dptr != NULL;
762           key = ldbm_nextkey( oldbm, lastkey ) )
763 #endif
764         {
765                 /* see if it's in the new one */
766                 data = ldbm_fetch( nldbm, key );
767
768                 /* not there - delete it */
769                 if ( data.dptr == NULL ) {
770                         if ( charray_add_dup( &dvals, &dcur, &dmax, key.dptr ) == NULL ) {
771                                 ldap_mods_free( mods, 1 );
772                                 return( NULL );
773                         }
774                 } else {
775                         ldbm_datum_free( nldbm, data );
776                 }
777                 if ( lastkey.dptr != NULL ) {
778                         ldbm_datum_free( oldbm, lastkey );
779                 }
780                 lastkey = key;
781         }
782         if ( lastkey.dptr != NULL ) {
783                 ldbm_datum_free( oldbm, lastkey );
784         }
785 #endif
786
787         mods[0]->mod_values = avals;
788         mods[1]->mod_values = dvals;
789
790         if ( verbose ) {
791                 printf( "\n" );
792                 fflush( stdout );
793         }
794
795         if ( mods[1]->mod_values == NULL ) {
796                 free( (char *) mods[1] );
797                 mods[1] = NULL;
798         }
799         if ( mods[0]->mod_values == NULL ) {
800                 free( (char *) mods[0] );
801                 mods[0] = mods[1];
802                 mods[1] = NULL;
803         }
804         if ( mods[0] == NULL ) {
805                 free( (char *) mods );
806                 return( NULL );
807         } else {
808                 return( mods );
809         }
810 }
811
812 static LDAPMod **
813 full_centroid(
814         char    *attr,
815         LDBM    ldbm,
816     int         nentries
817 )
818 {
819         Datum   key, lastkey;
820         LDAPMod **mods;
821         char    **vals;
822         int             vcur, vmax;
823
824 #ifdef LDBM_USE_DB2
825         DBC *cursorp;
826 #endif
827
828         if ( verbose ) {
829                 printf( "Generating mods for full %s centroid...", attr );
830                 fflush( stdout );
831         }
832
833         if ( (mods = (LDAPMod **) malloc( sizeof(LDAPMod *) * 3 )) == NULL ||
834              (mods[0] = (LDAPMod *) malloc( sizeof(LDAPMod) )) == NULL ||
835              (mods[1] = (LDAPMod *) malloc( sizeof(LDAPMod) )) == NULL ||
836              (vals = (char **) malloc( 2 * sizeof(char *) )) == NULL ||
837              (vals[0] = (char *) malloc( 20 )) == NULL )
838         {
839                 perror( "malloc" );
840                 exit( -1 );
841         }
842         mods[0]->mod_op = LDAP_MOD_REPLACE;
843         mods[0]->mod_type = attr;
844         mods[0]->mod_values = NULL;
845         sprintf( vals[0], "%d", nentries );
846         vals[1] = NULL;
847         mods[1]->mod_op = LDAP_MOD_REPLACE;
848         mods[1]->mod_type = "nentries";
849         mods[1]->mod_values = vals;
850         mods[2] = NULL;
851
852         lastkey.dptr = NULL;
853         vals = NULL;
854         vcur = vmax = 0;
855 #ifdef LDBM_USE_DB2
856         for ( key = ldbm_firstkey( ldbm, &cursorp ); key.dptr != NULL;
857           key = ldbm_nextkey( ldbm, lastkey, cursorp ) )
858 #else
859         for ( key = ldbm_firstkey( ldbm ); key.dptr != NULL;
860           key = ldbm_nextkey( ldbm, lastkey ) )
861 #endif
862         {
863                 if ( charray_add_dup( &vals, &vcur, &vmax, key.dptr ) == NULL ) {
864                         ldap_mods_free( mods, 1 );
865                         return( NULL );
866                 }
867
868                 if ( lastkey.dptr != NULL ) {
869                         ldbm_datum_free( ldbm, lastkey );
870                 }
871                 lastkey = key;
872         }
873         if ( lastkey.dptr != NULL ) {
874                 ldbm_datum_free( ldbm, lastkey );
875         }
876         mods[0]->mod_values = vals;
877
878         if ( verbose ) {
879                 printf( "\n" );
880                 fflush( stdout );
881         }
882
883         if ( mods[0]->mod_values == NULL ) {
884                 free( (char *) mods[0] );
885                 free( (char *) mods );
886                 return( NULL );
887         } else {
888                 return( mods );
889         }
890 }
891
892 /*
893  * extract the destination ldap host, port, and base object for the
894  * server to receive the index information. then, open a connection,
895  * bind, and see if the entry exists. if not, create it and set things
896  * up so the centroid full and diff routines can modify it to contain
897  * the new centroid information.
898  */
899
900 static LDAP *
901 bind_to_destination_ldap(
902         char    *ldapsrcurl,
903         char    *ldapdesturl
904 )
905 {
906         LDAP            *ld;
907         LDAPMessage     *res;
908         int                     rc;
909         char            *s, *s2, *d;
910         char            *attrs[2], *refvalues[2], *ocvalues[2];
911         LDAPMod         *mp[3];
912         LDAPMod         m[2];
913         char            buf[BUFSIZ];
914
915         if ( verbose ) {
916                 printf( "Binding to destination LDAP server..." );
917                 fflush( stdout );
918         }
919
920         /* first, pick out the destination ldap server info */
921         if ( ldapbase != NULL ) {
922                 free( ldapbase );
923         }
924         if ( strncmp( ldapdesturl, "ldap://", 7 ) == 0 ) {
925                 s = ldapdesturl + 7;
926         }
927         if ( (s2 = strchr( s, '/' )) == NULL ) {
928                 ldapbase = strdup( s );
929         } else {
930                 if ( *s != '/' ) {
931                         *s2 = '\0';
932                         if ( ldaphost != NULL )
933                                 free( ldaphost );
934                         ldaphost = strdup( s );
935                         *s2 = '/';
936                 }
937                 ldapbase = strdup( s2 + 1 );
938         }
939         strcpy( buf, "ref=" );
940         if ( strpbrk( ldapsrcurl, " ,;" ) != NULL ) {
941                 strcat( buf, "\"" );
942         }
943         for ( s = d = ldapsrcurl; *s; s++ ) {
944                 if ( *s != '"' ) {
945                         *d++ = *s;
946                 }
947         }
948         *d = '\0';
949         strcat( buf, ldapsrcurl );
950         if ( strpbrk( ldapsrcurl, " ,;" ) != NULL ) {
951                 strcat( buf, "\"" );
952         }
953         strcat( buf, ", " );
954         strcat( buf, ldapbase );
955         free( ldapbase );
956         ldapbase = strdup( buf );
957
958         if ( (ld = ldap_open( ldaphost, LDAP_PORT )) == NULL ) {
959                 perror( "ldap_open" );
960                 return( NULL );
961         }
962
963         if ( ldap_bind_s( ld, destldapbinddn, destldappasswd, destldapauthmethod )
964           != LDAP_SUCCESS) {
965                 ldap_perror( ld, "ldap_bind_s" );
966                 ldap_unbind( ld );
967                 return( NULL );
968         }
969         if ( verbose ) {
970                 printf( "\n" );
971         }
972
973         attrs[0] = "c";
974         attrs[1] = NULL;
975         rc = ldap_search_s( ld, ldapbase, LDAP_SCOPE_BASE, "(objectclass=*)",
976           attrs, 0, &res );
977         ldap_msgfree( res );
978
979         if ( rc == LDAP_NO_SUCH_OBJECT ) {
980                 if ( verbose ) {
981                         printf( "%sCreating centroid entry...", not ? "Not " : "" );
982                         fflush( stdout );
983                 }
984
985                 /* create the centroid index entry */
986                 m[0].mod_op = 0;
987                 m[0].mod_type = "ref";
988                 refvalues[0] = ldapsrcurl;
989                 refvalues[1] = NULL;
990                 m[0].mod_values = refvalues;
991                 m[1].mod_op = 0;
992                 m[1].mod_type = "objectclass";
993                 ocvalues[0] = "indexentry";
994                 ocvalues[1] = NULL;
995                 m[1].mod_values = ocvalues;
996                 mp[0] = &m[0];
997                 mp[1] = &m[1];
998                 mp[2] = NULL;
999
1000                 if ( !not && ldap_add_s( ld, ldapbase, mp ) != LDAP_SUCCESS ) {
1001                         ldap_perror( ld, ldapbase );
1002                         ldap_unbind( ld );
1003                         return( NULL );
1004                 }
1005
1006                 if ( verbose ) {
1007                         printf( "\n" );
1008                         fflush( stdout );
1009                 }
1010         } else if ( rc != LDAP_SUCCESS ) {
1011                 ldap_perror( ld, "ldap_search_s" );
1012                 ldap_unbind( ld );
1013                 return( NULL );
1014         }
1015
1016         return( ld );
1017 }
1018
1019 static char **
1020 charray_add_dup(
1021         char    ***a,
1022     int         *cur,
1023     int         *max,
1024         char    *s
1025 )
1026 {
1027         int n;
1028  
1029         if ( *a == NULL ) {
1030                 *a = (char **) malloc( (BUFSIZ + 1) * sizeof(char *) );
1031                 *cur = 0;
1032                 *max = BUFSIZ;
1033         } else if ( *cur >= *max ) {
1034                 *max += BUFSIZ;
1035                 *a = (char **) realloc( *a, (*max + 1) * sizeof(char *) );
1036         }
1037         if ( *a == NULL ) {
1038                 return( NULL );
1039         }
1040
1041         (*a)[(*cur)++] = strdup( s );
1042         (*a)[*cur] = NULL;
1043         return( *a );
1044 }