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