]> git.sur5r.net Git - openldap/blob - servers/slapd/repl.c
ITS#5728 release entries before send_ldap_ber()
[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-2008 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;
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         now = slap_get_time();
187         for ( i = 0; op->o_bd->be_replica != NULL && op->o_bd->be_replica[i] != NULL; i++ ) {
188                 /* check if dn's suffix matches legal suffixes, if any */
189                 if ( op->o_bd->be_replica[i]->ri_nsuffix != NULL ) {
190                         int j;
191
192                         for ( j = 0; op->o_bd->be_replica[i]->ri_nsuffix[j].bv_val; j++ ) {
193                                 if ( dnIsSuffix( &op->o_req_ndn, &op->o_bd->be_replica[i]->ri_nsuffix[j] ) ) {
194                                         break;
195                                 }
196                         }
197
198                         if ( !op->o_bd->be_replica[i]->ri_nsuffix[j].bv_val ) {
199                                 /* do not add "replica:" line */
200                                 continue;
201                         }
202                 }
203                 /* See if we only want a subset of attributes */
204                 if ( op->o_bd->be_replica[i]->ri_attrs != NULL &&
205                         ( op->o_tag == LDAP_REQ_MODIFY || op->o_tag == LDAP_REQ_ADD || op->o_tag == LDAP_REQ_EXTENDED ) ) {
206                         if ( !subsets ) {
207                                 subsets = i + 1;
208                         }
209                         /* Do attribute subsets by themselves in a second pass */
210                         continue;
211                 }
212
213                 fprintf( fp, "replica: %s\n", op->o_bd->be_replica[i]->ri_host );
214 #ifdef NO_LOG_WHEN_NO_REPLICAS
215                 ++count;
216 #endif
217         }
218
219 #ifdef NO_LOG_WHEN_NO_REPLICAS
220         if ( count == 0 && subsets == 0 ) {
221                 /* if no replicas matched, drop the log 
222                  * (should we log it anyway?) */
223                 lock_fclose( fp, lfp );
224                 ldap_pvt_thread_mutex_unlock( &replog_mutex );
225
226                 return;
227         }
228 #endif
229
230         replog1( NULL, op, fp, now );
231
232         if ( subsets > 0 ) {
233                 for ( i = subsets - 1; op->o_bd->be_replica[i] != NULL; i++ ) {
234
235                         /* If no attrs, we already did this above */
236                         if ( op->o_bd->be_replica[i]->ri_attrs == NULL ) {
237                                 continue;
238                         }
239
240                         /* check if dn's suffix matches legal suffixes, if any */
241                         if ( op->o_bd->be_replica[i]->ri_nsuffix != NULL ) {
242                                 int j;
243
244                                 for ( j = 0; op->o_bd->be_replica[i]->ri_nsuffix[j].bv_val; j++ ) {
245                                         if ( dnIsSuffix( &op->o_req_ndn, &op->o_bd->be_replica[i]->ri_nsuffix[j] ) ) {
246                                                 break;
247                                         }
248                                 }
249
250                                 if ( !op->o_bd->be_replica[i]->ri_nsuffix[j].bv_val ) {
251                                         /* no matching suffix found, skip it */
252                                         continue;
253                                 }
254                         }
255                         switch( op->o_tag ) {
256                         case LDAP_REQ_EXTENDED:
257                                 /* quick hack for extended operations */
258                                 /* assume change parameter is a Modifications* */
259                                 /* fall thru */
260                         case LDAP_REQ_MODIFY:
261                         case LDAP_REQ_ADD:
262                                 break;
263                         default:
264                                 /* Other operations were logged in the first pass */
265                                 continue;
266                         }
267                         replog1( op->o_bd->be_replica[i], op, fp, now );
268                 }
269         }
270
271         lock_fclose( fp, lfp );
272         ldap_pvt_thread_mutex_unlock( &replog_mutex );
273 }
274
275 static void
276 rephdr(
277         struct slap_replica_info *ri,
278         Operation *op,
279         FILE *fp,
280         long now
281 )
282 {
283         if ( ri ) {
284                 fprintf( fp, "replica: %s\n", ri->ri_host );
285         }
286         fprintf( fp, "time: %ld\n", now );
287         fprintf( fp, "dn: %s\n", op->o_req_dn.bv_val );
288 }
289
290 static void
291 replog1(
292         struct slap_replica_info *ri,
293         Operation *op,
294         FILE    *fp,
295         long    now
296 )
297 {
298         Modifications   *ml;
299         Attribute       *a;
300         AttributeName   *an;
301         int             dohdr = 1, ocs = -1;
302         struct berval vals[2];
303
304         vals[1].bv_val = NULL;
305         vals[1].bv_len = 0;
306
307         switch ( op->o_tag ) {
308         case LDAP_REQ_EXTENDED:
309                 /* quick hack for extended operations */
310                 /* assume change parameter is a Modifications* */
311                 /* fall thru */
312
313         case LDAP_REQ_MODIFY:
314                 for ( ml = op->orm_modlist; ml != NULL; ml = ml->sml_next ) {
315                         char *did = NULL, *type = ml->sml_desc->ad_cname.bv_val;
316                         switch ( ml->sml_op ) {
317                         case LDAP_MOD_ADD:
318                                 did = "add"; break;
319
320                         case LDAP_MOD_DELETE:
321                                 did = "delete"; break;
322
323                         case LDAP_MOD_REPLACE:
324                                 did = "replace"; break;
325
326                         case LDAP_MOD_INCREMENT:
327                                 did = "increment"; break;
328                         }
329                         if ( ri && ri->ri_attrs ) {
330                                 int is_in = ad_inlist( ml->sml_desc, ri->ri_attrs );
331
332                                 /* skip if:
333                                  *   1) the attribute is not in the list,
334                                  *      and it's not an exclusion list
335                                  *   2) the attribute is in the list
336                                  *      and it's an exclusion list,
337                                  *      and either the objectClass attribute
338                                  *      has already been dealt with or
339                                  *      this is not the objectClass attr
340                                  */
341                                 if ( ( !is_in && !ri->ri_exclude )
342                                         || ( ( is_in && ri->ri_exclude )
343                                                 && ( !ocs || ml->sml_desc != slap_schema.si_ad_objectClass ) ) )
344                                 {
345                                         continue;
346                                 }
347
348                                 /* If this is objectClass, see if the value is included
349                                  * in any subset, otherwise drop it.
350                                  */
351                                 if ( ocs && ml->sml_desc == slap_schema.si_ad_objectClass
352                                         && ml->sml_values )
353                                 {
354                                         int i, first = 1;
355
356                                         if ( ocs == -1 ) ocs = 0;
357
358                                         for ( i=0; ml->sml_values[i].bv_val; i++ ) {
359                                                 int match = 0;
360                                                 for ( an = ri->ri_attrs; an->an_name.bv_val; an++ ) {
361                                                         if ( an->an_oc ) {
362                                                                 struct berval   bv = an->an_name;
363
364                                                                 ocs = 1;
365                                                                 match |= an->an_oc_exclude;
366
367                                                                 switch ( bv.bv_val[ 0 ] ) {
368                                                                 case '@':
369                                                                 case '+':
370                                                                 case '!':
371                                                                         bv.bv_val++;
372                                                                         bv.bv_len--;
373                                                                         break;
374                                                                 }
375
376                                                                 if ( ml->sml_values[i].bv_len == bv.bv_len
377                                                                         && !strcasecmp(ml->sml_values[i].bv_val,
378                                                                                 bv.bv_val ) )
379                                                                 {
380                                                                         match = !an->an_oc_exclude;
381                                                                         break;
382                                                                 }
383                                                         }
384                                                 }
385                                                 /* Objectclasses need no special treatment, drop into
386                                                  * regular processing
387                                                  */
388                                                 if ( !ocs ) break;
389
390                                                 match ^= ri->ri_exclude;
391                                                 /* Found a match, log it */
392                                                 if ( match ) {
393                                                         if ( dohdr ) {
394                                                                 rephdr( ri, op, fp, now );
395                                                                 fprintf( fp, "changetype: modify\n" );
396                                                                 dohdr = 0;
397                                                         }
398                                                         if ( first ) {
399                                                                 fprintf( fp, "%s: %s\n", did, type );
400                                                                 first = 0;
401                                                         }
402                                                         vals[0] = ml->sml_values[i];
403                                                         print_vals( fp, &ml->sml_desc->ad_cname, vals );
404                                                         ocs = 2;
405                                                 }
406
407                                         }
408                                         /* Explicit objectclasses have been handled already */
409                                         if ( ocs ) {
410                                                 if ( ocs == 2 ) {
411                                                         fprintf( fp, "-\n" );
412                                                 }
413                                                 continue;
414                                         }
415                                 }
416                         }
417                         if ( dohdr ) {
418                                 rephdr( ri, op, fp, now );
419                                 fprintf( fp, "changetype: modify\n" );
420                                 dohdr = 0;
421                         }
422                         fprintf( fp, "%s: %s\n", did, type );
423                         if ( ml->sml_values ) {
424                                 print_vals( fp, &ml->sml_desc->ad_cname, ml->sml_values );
425                         }
426                         fprintf( fp, "-\n" );
427                 }
428                 break;
429
430         case LDAP_REQ_ADD:
431                 for ( a = op->ora_e->e_attrs ; a != NULL; a=a->a_next ) {
432                         if ( ri && ri->ri_attrs ) {
433                                 int is_in = ad_inlist( a->a_desc, ri->ri_attrs );
434
435                                 /* skip if:
436                                  *   1) the attribute is not in the list,
437                                  *      and it's not an exclusion list
438                                  *   2) the attribute is in the list
439                                  *      and it's an exclusion list,
440                                  *      and either the objectClass attribute
441                                  *      has already been dealt with or
442                                  *      this is not the objectClass attr
443                                  */
444                                 if ( ( !is_in && !ri->ri_exclude )
445                                         || ( ( is_in && ri->ri_exclude )
446                                                 && ( !ocs || a->a_desc != slap_schema.si_ad_objectClass ) ) )
447                                 {
448                                         continue;
449                                 }
450
451                                 /* If the list includes objectClass names,
452                                  * only include those classes in the
453                                  * objectClass attribute
454                                  */
455                                 if ( ocs && a->a_desc == slap_schema.si_ad_objectClass ) {
456                                         int i;
457
458                                         if ( ocs == -1 ) ocs = 0;
459
460                                         for ( i=0; a->a_vals[i].bv_val; i++ ) {
461                                                 int match = 0;
462                                                 for ( an = ri->ri_attrs; an->an_name.bv_val; an++ ) {
463                                                         if ( an->an_oc ) {
464                                                                 struct berval   bv = an->an_name;
465
466                                                                 ocs = 1;
467                                                                 match |= an->an_oc_exclude;
468
469                                                                 switch ( bv.bv_val[ 0 ] ) {
470                                                                 case '@':
471                                                                 case '+':
472                                                                 case '!':
473                                                                         bv.bv_val++;
474                                                                         bv.bv_len--;
475                                                                         break;
476                                                                 }
477
478                                                                 if ( a->a_vals[i].bv_len == bv.bv_len
479                                                                         && !strcasecmp(a->a_vals[i].bv_val,
480                                                                                 bv.bv_val ) )
481                                                                 {
482                                                                         match = !an->an_oc_exclude;
483                                                                         break;
484                                                                 }
485                                                         }
486                                                 }
487                                                 if ( !ocs ) break;
488
489                                                 match ^= ri->ri_exclude;
490                                                 if ( match ) {
491                                                         if ( dohdr ) {
492                                                                 rephdr( ri, op, fp, now );
493                                                                 fprintf( fp, "changetype: add\n" );
494                                                                 dohdr = 0;
495                                                         }
496                                                         vals[0] = a->a_nvals[i];
497                                                         print_vals( fp, &a->a_desc->ad_cname, vals );
498                                                 }
499                                         }
500                                         if ( ocs ) continue;
501                                 }
502                         }
503                         if ( dohdr ) {
504                                 rephdr( ri, op, fp, now );
505                                 fprintf( fp, "changetype: add\n" );
506                                 dohdr = 0;
507                         }
508                         print_vals( fp, &a->a_desc->ad_cname, a->a_vals );
509                 }
510                 break;
511
512         case LDAP_REQ_DELETE:
513                 rephdr( ri, op, fp, now );
514                 fprintf( fp, "changetype: delete\n" );
515                 break;
516
517         case LDAP_REQ_MODRDN:
518                 rephdr( ri, op, fp, now );
519                 fprintf( fp, "changetype: modrdn\n" );
520                 fprintf( fp, "newrdn: %s\n", op->orr_newrdn.bv_val );
521                 fprintf( fp, "deleteoldrdn: %d\n", op->orr_deleteoldrdn ? 1 : 0 );
522                 if( op->orr_newSup != NULL ) {
523                         fprintf( fp, "newsuperior: %s\n", op->orr_newSup->bv_val );
524                 }
525         }
526         fprintf( fp, "\n" );
527 }
528
529 static void
530 print_vals(
531         FILE *fp,
532         struct berval *type,
533         struct berval *bv )
534 {
535         ber_len_t i, len;
536         char    *buf, *bufp;
537
538         for ( i = 0, len = 0; bv && bv[i].bv_val; i++ ) {
539                 if ( bv[i].bv_len > len )
540                         len = bv[i].bv_len;
541         }
542
543         len = LDIF_SIZE_NEEDED( type->bv_len, len ) + 1;
544         buf = (char *) ch_malloc( len );
545
546         for ( ; bv && bv->bv_val; bv++ ) {
547                 bufp = buf;
548                 ldif_sput( &bufp, LDIF_PUT_VALUE, type->bv_val,
549                                     bv->bv_val, bv->bv_len );
550                 *bufp = '\0';
551
552                 fputs( buf, fp );
553
554         }
555         free( buf );
556 }