]> git.sur5r.net Git - openldap/blob - servers/slapd/repl.c
Additional changes suggested by Hallvard
[openldap] / servers / slapd / repl.c
1 /* repl.c - log modifications for replication purposes */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2006 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
17  * All rights reserved.
18  *
19  * Redistribution and use in source and binary forms are permitted
20  * provided that this notice is preserved and that due credit is given
21  * to the University of Michigan at Ann Arbor. The name of the University
22  * may not be used to endorse or promote products derived from this
23  * software without specific prior written permission. This software
24  * is provided ``as is'' without express or implied warranty.
25  */
26
27 #include "portable.h"
28
29 #include <stdio.h>
30
31 #include <ac/string.h>
32 #include <ac/ctype.h>
33 #include <ac/socket.h>
34
35 #ifdef HAVE_SYS_FILE_H
36 #include <sys/file.h>
37 #endif
38
39 #include "slap.h"
40 #include "ldif.h"
41
42 int
43 add_replica_info(
44         Backend         *be,
45         const char      *uri, 
46         const char      *host )
47 {
48         int i = 0;
49
50         assert( be != NULL );
51         assert( host != NULL );
52
53         if ( be->be_replica != NULL ) {
54                 for ( ; be->be_replica[ i ] != NULL; i++ );
55         }
56                 
57         be->be_replica = ch_realloc( be->be_replica, 
58                 sizeof( struct slap_replica_info * )*( i + 2 ) );
59
60         be->be_replica[ i ] 
61                 = ch_calloc( sizeof( struct slap_replica_info ), 1 );
62         ber_str2bv( uri, 0, 0, &be->be_replica[ i ]->ri_bindconf.sb_uri );
63         be->be_replica[ i ]->ri_host = host;
64         be->be_replica[ i ]->ri_nsuffix = NULL;
65         be->be_replica[ i ]->ri_attrs = NULL;
66         be->be_replica[ i + 1 ] = NULL;
67
68         return( i );
69 }
70
71 int
72 destroy_replica_info(
73         Backend         *be )
74 {
75         int i = 0;
76
77         assert( be != NULL );
78
79         if ( be->be_replica == NULL ) {
80                 return 0;
81         }
82
83         for ( ; be->be_replica[ i ] != NULL; i++ ) {
84                 ber_bvarray_free( be->be_replica[ i ]->ri_nsuffix );
85
86                 if ( be->be_replica[ i ]->ri_attrs ) {
87                         AttributeName   *an = be->be_replica[ i ]->ri_attrs;
88                         int             j;
89
90                         for ( j = 0; !BER_BVISNULL( &an[ j ].an_name ); j++ )
91                         {
92                                 ch_free( an[ j ].an_name.bv_val );
93                         }
94                         ch_free( an );
95                 }
96
97                 bindconf_free( &be->be_replica[ i ]->ri_bindconf );
98
99                 ch_free( be->be_replica[ i ] );
100         }
101
102         ch_free( be->be_replica );
103
104         return 0;
105 }
106
107 int
108 add_replica_suffix(
109     Backend     *be,
110     int         nr,
111     const char  *suffix
112 )
113 {
114         struct berval dn, ndn;
115         int rc;
116
117         dn.bv_val = (char *) suffix;
118         dn.bv_len = strlen( dn.bv_val );
119
120         rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
121         if( rc != LDAP_SUCCESS ) {
122                 return 2;
123         }
124
125         if ( select_backend( &ndn, 0, 0 ) != be ) {
126                 free( ndn.bv_val );
127                 return 1;
128         }
129
130         ber_bvarray_add( &be->be_replica[nr]->ri_nsuffix, &ndn );
131         return 0;
132 }
133
134 int
135 add_replica_attrs(
136         Backend *be,
137         int     nr,
138         char    *attrs,
139         int     exclude
140 )
141 {
142         if ( be->be_replica[nr]->ri_attrs != NULL ) {
143                 if ( be->be_replica[nr]->ri_exclude != exclude ) {
144                         fprintf( stderr, "attr selective replication directive '%s' conflicts with previous one (discarded)\n", attrs );
145                         ch_free( be->be_replica[nr]->ri_attrs );
146                         be->be_replica[nr]->ri_attrs = NULL;
147                 }
148         }
149
150         be->be_replica[nr]->ri_exclude = exclude;
151         be->be_replica[nr]->ri_attrs = str2anlist( be->be_replica[nr]->ri_attrs,
152                 attrs, "," );
153         return ( be->be_replica[nr]->ri_attrs == NULL );
154 }
155    
156 static void
157 print_vals( FILE *fp, struct berval *type, struct berval *bv );
158 static void
159 replog1( struct slap_replica_info *ri, Operation *op, FILE *fp, long now);
160
161 void
162 replog( Operation *op )
163 {
164         FILE    *fp, *lfp;
165         int     i;
166 /* undef NO_LOG_WHEN_NO_REPLICAS */
167 #ifdef NO_LOG_WHEN_NO_REPLICAS
168         int     count = 0;
169 #endif
170         int     subsets = 0;
171         long    now = slap_get_time();
172         char    *replogfile;
173
174         replogfile = op->o_bd->be_replogfile ? op->o_bd->be_replogfile :
175                 frontendDB->be_replogfile;
176         if ( !replogfile ) {
177                 return;
178         }
179
180         ldap_pvt_thread_mutex_lock( &replog_mutex );
181         if ( (fp = lock_fopen( replogfile, "a", &lfp )) == NULL ) {
182                 ldap_pvt_thread_mutex_unlock( &replog_mutex );
183                 return;
184         }
185
186         for ( i = 0; op->o_bd->be_replica != NULL && op->o_bd->be_replica[i] != NULL; i++ ) {
187                 /* check if dn's suffix matches legal suffixes, if any */
188                 if ( op->o_bd->be_replica[i]->ri_nsuffix != NULL ) {
189                         int j;
190
191                         for ( j = 0; op->o_bd->be_replica[i]->ri_nsuffix[j].bv_val; j++ ) {
192                                 if ( dnIsSuffix( &op->o_req_ndn, &op->o_bd->be_replica[i]->ri_nsuffix[j] ) ) {
193                                         break;
194                                 }
195                         }
196
197                         if ( !op->o_bd->be_replica[i]->ri_nsuffix[j].bv_val ) {
198                                 /* do not add "replica:" line */
199                                 continue;
200                         }
201                 }
202                 /* See if we only want a subset of attributes */
203                 if ( op->o_bd->be_replica[i]->ri_attrs != NULL &&
204                         ( op->o_tag == LDAP_REQ_MODIFY || op->o_tag == LDAP_REQ_ADD || op->o_tag == LDAP_REQ_EXTENDED ) ) {
205                         if ( !subsets ) {
206                                 subsets = i + 1;
207                         }
208                         /* Do attribute subsets by themselves in a second pass */
209                         continue;
210                 }
211
212                 fprintf( fp, "replica: %s\n", op->o_bd->be_replica[i]->ri_host );
213 #ifdef NO_LOG_WHEN_NO_REPLICAS
214                 ++count;
215 #endif
216         }
217
218 #ifdef NO_LOG_WHEN_NO_REPLICAS
219         if ( count == 0 && subsets == 0 ) {
220                 /* if no replicas matched, drop the log 
221                  * (should we log it anyway?) */
222                 lock_fclose( fp, lfp );
223                 ldap_pvt_thread_mutex_unlock( &replog_mutex );
224
225                 return;
226         }
227 #endif
228
229         replog1( NULL, op, fp, now );
230
231         if ( subsets > 0 ) {
232                 for ( i = subsets - 1; op->o_bd->be_replica[i] != NULL; i++ ) {
233
234                         /* If no attrs, we already did this above */
235                         if ( op->o_bd->be_replica[i]->ri_attrs == NULL ) {
236                                 continue;
237                         }
238
239                         /* check if dn's suffix matches legal suffixes, if any */
240                         if ( op->o_bd->be_replica[i]->ri_nsuffix != NULL ) {
241                                 int j;
242
243                                 for ( j = 0; op->o_bd->be_replica[i]->ri_nsuffix[j].bv_val; j++ ) {
244                                         if ( dnIsSuffix( &op->o_req_ndn, &op->o_bd->be_replica[i]->ri_nsuffix[j] ) ) {
245                                                 break;
246                                         }
247                                 }
248
249                                 if ( !op->o_bd->be_replica[i]->ri_nsuffix[j].bv_val ) {
250                                         /* no matching suffix found, skip it */
251                                         continue;
252                                 }
253                         }
254                         switch( op->o_tag ) {
255                         case LDAP_REQ_EXTENDED:
256                                 /* quick hack for extended operations */
257                                 /* assume change parameter is a Modifications* */
258                                 /* fall thru */
259                         case LDAP_REQ_MODIFY:
260                         case LDAP_REQ_ADD:
261                                 break;
262                         default:
263                                 /* Other operations were logged in the first pass */
264                                 continue;
265                         }
266                         replog1( op->o_bd->be_replica[i], op, fp, now );
267                 }
268         }
269
270         lock_fclose( fp, lfp );
271         ldap_pvt_thread_mutex_unlock( &replog_mutex );
272 }
273
274 static void
275 rephdr(
276         struct slap_replica_info *ri,
277         Operation *op,
278         FILE *fp,
279         long now
280 )
281 {
282         if ( ri ) {
283                 fprintf( fp, "replica: %s\n", ri->ri_host );
284         }
285         fprintf( fp, "time: %ld\n", now );
286         fprintf( fp, "dn: %s\n", op->o_req_dn.bv_val );
287 }
288
289 static void
290 replog1(
291         struct slap_replica_info *ri,
292         Operation *op,
293         FILE    *fp,
294         long    now
295 )
296 {
297         Modifications   *ml;
298         Attribute       *a;
299         AttributeName   *an;
300         int             dohdr = 1, ocs = -1;
301         struct berval vals[2];
302
303         vals[1].bv_val = NULL;
304         vals[1].bv_len = 0;
305
306         switch ( op->o_tag ) {
307         case LDAP_REQ_EXTENDED:
308                 /* quick hack for extended operations */
309                 /* assume change parameter is a Modifications* */
310                 /* fall thru */
311
312         case LDAP_REQ_MODIFY:
313                 for ( ml = op->orm_modlist; ml != NULL; ml = ml->sml_next ) {
314                         char *did = NULL, *type = ml->sml_desc->ad_cname.bv_val;
315                         switch ( ml->sml_op ) {
316                         case LDAP_MOD_ADD:
317                                 did = "add"; break;
318
319                         case LDAP_MOD_DELETE:
320                                 did = "delete"; break;
321
322                         case LDAP_MOD_REPLACE:
323                                 did = "replace"; break;
324
325                         case LDAP_MOD_INCREMENT:
326                                 did = "increment"; break;
327                         }
328                         if ( ri && ri->ri_attrs ) {
329                                 int is_in = ad_inlist( ml->sml_desc, ri->ri_attrs );
330
331                                 /* skip if:
332                                  *   1) the attribute is not in the list,
333                                  *      and it's not an exclusion list
334                                  *   2) the attribute is in the list
335                                  *      and it's an exclusion list,
336                                  *      and either the objectClass attribute
337                                  *      has already been dealt with or
338                                  *      this is not the objectClass attr
339                                  */
340                                 if ( ( !is_in && !ri->ri_exclude )
341                                         || ( ( is_in && ri->ri_exclude )
342                                                 && ( !ocs || ml->sml_desc != slap_schema.si_ad_objectClass ) ) )
343                                 {
344                                         continue;
345                                 }
346
347                                 /* If this is objectClass, see if the value is included
348                                  * in any subset, otherwise drop it.
349                                  */
350                                 if ( ocs && ml->sml_desc == slap_schema.si_ad_objectClass
351                                         && ml->sml_values )
352                                 {
353                                         int i, first = 1;
354
355                                         if ( ocs == -1 ) ocs = 0;
356
357                                         for ( i=0; ml->sml_values[i].bv_val; i++ ) {
358                                                 int match = 0;
359                                                 for ( an = ri->ri_attrs; an->an_name.bv_val; an++ ) {
360                                                         if ( an->an_oc ) {
361                                                                 struct berval   bv = an->an_name;
362
363                                                                 ocs = 1;
364                                                                 match |= an->an_oc_exclude;
365
366                                                                 switch ( bv.bv_val[ 0 ] ) {
367                                                                 case '@':
368                                                                 case '+':
369                                                                 case '!':
370                                                                         bv.bv_val++;
371                                                                         bv.bv_len--;
372                                                                         break;
373                                                                 }
374
375                                                                 if ( ml->sml_values[i].bv_len == bv.bv_len
376                                                                         && !strcasecmp(ml->sml_values[i].bv_val,
377                                                                                 bv.bv_val ) )
378                                                                 {
379                                                                         match = !an->an_oc_exclude;
380                                                                         break;
381                                                                 }
382                                                         }
383                                                 }
384                                                 /* Objectclasses need no special treatment, drop into
385                                                  * regular processing
386                                                  */
387                                                 if ( !ocs ) break;
388
389                                                 match ^= ri->ri_exclude;
390                                                 /* Found a match, log it */
391                                                 if ( match ) {
392                                                         if ( dohdr ) {
393                                                                 rephdr( ri, op, fp, now );
394                                                                 fprintf( fp, "changetype: modify\n" );
395                                                                 dohdr = 0;
396                                                         }
397                                                         if ( first ) {
398                                                                 fprintf( fp, "%s: %s\n", did, type );
399                                                                 first = 0;
400                                                         }
401                                                         vals[0] = ml->sml_values[i];
402                                                         print_vals( fp, &ml->sml_desc->ad_cname, vals );
403                                                         ocs = 2;
404                                                 }
405
406                                         }
407                                         /* Explicit objectclasses have been handled already */
408                                         if ( ocs ) {
409                                                 if ( ocs == 2 ) {
410                                                         fprintf( fp, "-\n" );
411                                                 }
412                                                 continue;
413                                         }
414                                 }
415                         }
416                         if ( dohdr ) {
417                                 rephdr( ri, op, fp, now );
418                                 fprintf( fp, "changetype: modify\n" );
419                                 dohdr = 0;
420                         }
421                         fprintf( fp, "%s: %s\n", did, type );
422                         if ( ml->sml_values ) {
423                                 print_vals( fp, &ml->sml_desc->ad_cname, ml->sml_values );
424                         }
425                         fprintf( fp, "-\n" );
426                 }
427                 break;
428
429         case LDAP_REQ_ADD:
430                 for ( a = op->ora_e->e_attrs ; a != NULL; a=a->a_next ) {
431                         if ( ri && ri->ri_attrs ) {
432                                 int is_in = ad_inlist( a->a_desc, ri->ri_attrs );
433
434                                 /* skip if:
435                                  *   1) the attribute is not in the list,
436                                  *      and it's not an exclusion list
437                                  *   2) the attribute is in the list
438                                  *      and it's an exclusion list,
439                                  *      and either the objectClass attribute
440                                  *      has already been dealt with or
441                                  *      this is not the objectClass attr
442                                  */
443                                 if ( ( !is_in && !ri->ri_exclude )
444                                         || ( ( is_in && ri->ri_exclude )
445                                                 && ( !ocs || a->a_desc != slap_schema.si_ad_objectClass ) ) )
446                                 {
447                                         continue;
448                                 }
449
450                                 /* If the list includes objectClass names,
451                                  * only include those classes in the
452                                  * objectClass attribute
453                                  */
454                                 if ( ocs && a->a_desc == slap_schema.si_ad_objectClass ) {
455                                         int i;
456
457                                         if ( ocs == -1 ) ocs = 0;
458
459                                         for ( i=0; a->a_vals[i].bv_val; i++ ) {
460                                                 int match = 0;
461                                                 for ( an = ri->ri_attrs; an->an_name.bv_val; an++ ) {
462                                                         if ( an->an_oc ) {
463                                                                 struct berval   bv = an->an_name;
464
465                                                                 ocs = 1;
466                                                                 match |= an->an_oc_exclude;
467
468                                                                 switch ( bv.bv_val[ 0 ] ) {
469                                                                 case '@':
470                                                                 case '+':
471                                                                 case '!':
472                                                                         bv.bv_val++;
473                                                                         bv.bv_len--;
474                                                                         break;
475                                                                 }
476
477                                                                 if ( a->a_vals[i].bv_len == bv.bv_len
478                                                                         && !strcasecmp(a->a_vals[i].bv_val,
479                                                                                 bv.bv_val ) )
480                                                                 {
481                                                                         match = !an->an_oc_exclude;
482                                                                         break;
483                                                                 }
484                                                         }
485                                                 }
486                                                 if ( !ocs ) break;
487
488                                                 match ^= ri->ri_exclude;
489                                                 if ( match ) {
490                                                         if ( dohdr ) {
491                                                                 rephdr( ri, op, fp, now );
492                                                                 fprintf( fp, "changetype: add\n" );
493                                                                 dohdr = 0;
494                                                         }
495                                                         vals[0] = a->a_nvals[i];
496                                                         print_vals( fp, &a->a_desc->ad_cname, vals );
497                                                 }
498                                         }
499                                         if ( ocs ) continue;
500                                 }
501                         }
502                         if ( dohdr ) {
503                                 rephdr( ri, op, fp, now );
504                                 fprintf( fp, "changetype: add\n" );
505                                 dohdr = 0;
506                         }
507                         print_vals( fp, &a->a_desc->ad_cname, a->a_vals );
508                 }
509                 break;
510
511         case LDAP_REQ_DELETE:
512                 rephdr( ri, op, fp, now );
513                 fprintf( fp, "changetype: delete\n" );
514                 break;
515
516         case LDAP_REQ_MODRDN:
517                 rephdr( ri, op, fp, now );
518                 fprintf( fp, "changetype: modrdn\n" );
519                 fprintf( fp, "newrdn: %s\n", op->orr_newrdn.bv_val );
520                 fprintf( fp, "deleteoldrdn: %d\n", op->orr_deleteoldrdn ? 1 : 0 );
521                 if( op->orr_newSup != NULL ) {
522                         fprintf( fp, "newsuperior: %s\n", op->orr_newSup->bv_val );
523                 }
524         }
525         fprintf( fp, "\n" );
526 }
527
528 static void
529 print_vals(
530         FILE *fp,
531         struct berval *type,
532         struct berval *bv )
533 {
534         ber_len_t i, len;
535         char    *buf, *bufp;
536
537         for ( i = 0, len = 0; bv && bv[i].bv_val; i++ ) {
538                 if ( bv[i].bv_len > len )
539                         len = bv[i].bv_len;
540         }
541
542         len = LDIF_SIZE_NEEDED( type->bv_len, len ) + 1;
543         buf = (char *) ch_malloc( len );
544
545         for ( ; bv && bv->bv_val; bv++ ) {
546                 bufp = buf;
547                 ldif_sput( &bufp, LDIF_PUT_VALUE, type->bv_val,
548                                     bv->bv_val, bv->bv_len );
549                 *bufp = '\0';
550
551                 fputs( buf, fp );
552
553         }
554         free( buf );
555 }