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