]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/usn/usn.c
bbf09c5cbb845170cb8b89c4d1819140e3ddb96b
[openldap] / contrib / slapd-modules / usn / usn.c
1 /* usn.c - Maintain Microsoft-style Update Sequence Numbers */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2007-2010 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 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Howard Chu for inclusion in
18  * OpenLDAP Software.
19  */
20
21 #include "portable.h"
22
23 #ifdef SLAPD_OVER_USN
24
25 #include <stdio.h>
26
27 #include <ac/string.h>
28 #include <ac/socket.h>
29
30 #include "slap.h"
31 #include "config.h"
32
33 /* This overlay intercepts write operations and adds a Microsoft-style
34  * USN to the target entry.
35  */
36
37 typedef struct usn_info {
38         int ui_current;
39         ldap_pvt_thread_mutex_t ui_mutex;
40 } usn_info_t;
41
42 static AttributeDescription *ad_usnCreated, *ad_usnChanged;
43
44 static struct {
45         char *desc;
46         AttributeDescription **adp;
47 } as[] = {
48         { "( 1.2.840.113556.1.2.19 "
49             "NAME 'uSNCreated' "
50             "SYNTAX '1.2.840.113556.1.4.906' "
51                 "SINGLE-VALUE "
52                 "NO-USER-MODIFICATION )",
53                 &ad_usnCreated },
54         { "( 1.2.840.113556.1.2.120 "
55                 "NAME 'uSNChanged' "
56                 "SYNTAX '1.2.840.113556.1.4.906' "
57                 "SINGLE-VALUE "
58                 "NO-USER-MODIFICATION )",
59                 &ad_usnChanged },
60         { NULL }
61 };
62
63 static int
64 usn_func( Operation *op, SlapReply *rs )
65 {
66         slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
67         usn_info_t              *ui = on->on_bi.bi_private;
68         int my_usn;
69         char intbuf[64];
70         struct berval bv[2];
71
72         ldap_pvt_thread_mutex_lock( &ui->ui_mutex );
73         ui->ui_current++;
74         my_usn = ui->ui_current;
75         ldap_pvt_thread_mutex_unlock( &ui->ui_mutex );
76
77         BER_BVZERO(&bv[1]);
78         bv[0].bv_val = intbuf;
79         bv[0].bv_len = snprintf( intbuf, sizeof(intbuf), "%d", my_usn );
80         switch(op->o_tag) {
81         case LDAP_REQ_ADD:
82                 attr_merge( op->ora_e, ad_usnCreated, bv, NULL );
83                 attr_merge( op->ora_e, ad_usnChanged, bv, NULL );
84                 break;
85         case LDAP_REQ_DELETE:
86                 /* Probably need to update root usnLastObjRem */
87                 break;
88         default: {
89                 /* Modify, ModDN */
90                 Modifications *ml, *mod = ch_calloc( sizeof( Modifications ), 1 );
91                 for ( ml = op->orm_modlist; ml && ml->sml_next; ml = ml->sml_next );
92                 ml->sml_next = mod;
93                 mod->sml_desc = ad_usnChanged;
94                 mod->sml_numvals = 1;
95                 value_add_one( &mod->sml_values, &bv[0] );
96                 mod->sml_nvalues = NULL;
97                 mod->sml_op = LDAP_MOD_REPLACE;
98                 mod->sml_flags = 0;
99                 mod->sml_next = NULL;
100                 break;
101                 }
102         }
103         return SLAP_CB_CONTINUE;
104 }
105
106 static int
107 usn_operational(
108         Operation *op,
109         SlapReply *rs )
110 {
111         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
112         usn_info_t              *ui = (usn_info_t *)on->on_bi.bi_private;
113
114         if ( rs->sr_entry &&
115                 dn_match( &rs->sr_entry->e_nname, op->o_bd->be_nsuffix )) {
116
117                 if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
118                         ad_inlist( ad_usnChanged, rs->sr_attrs )) {
119                         Attribute *a, **ap = NULL;
120                         char intbuf[64];
121                         struct berval bv;
122                         int my_usn;
123
124                         for ( a=rs->sr_entry->e_attrs; a; a=a->a_next ) {
125                                 if ( a->a_desc == ad_usnChanged )
126                                         break;
127                         }
128
129                         if ( !a ) {
130                                 for ( ap = &rs->sr_operational_attrs; *ap;
131                                         ap=&(*ap)->a_next );
132
133                                         a = attr_alloc( ad_usnChanged );
134                                         *ap = a;
135                                 }
136
137                         if ( !ap ) {
138                                 if ( !rs->sr_flags & REP_ENTRY_MODIFIABLE ) {
139                                         rs->sr_entry = entry_dup( rs->sr_entry );
140                                         rs->sr_flags |=
141                                                 REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTBEFREED;
142                                         a = attr_find( rs->sr_entry->e_attrs,
143                                                 ad_usnChanged );
144                                 }
145                                 ber_bvarray_free( a->a_vals );
146                                 a->a_vals = NULL;
147                                 a->a_numvals = 0;
148                         }
149                         ldap_pvt_thread_mutex_lock( &ui->ui_mutex );
150                         my_usn = ui->ui_current;
151                         ldap_pvt_thread_mutex_unlock( &ui->ui_mutex );
152                         bv.bv_len = snprintf( intbuf, sizeof(intbuf), "%d", my_usn );
153                         bv.bv_val = intbuf;
154                         attr_valadd( a, &bv, NULL, 1 );
155                 }
156         }
157         return SLAP_CB_CONTINUE;
158 }
159
160 /* Read the old USN from the underlying DB. This code is
161  * stolen from the syncprov overlay.
162  */
163 static int
164 usn_db_open(
165         BackendDB *be,
166         ConfigReply *cr)
167 {
168         slap_overinst   *on = (slap_overinst *) be->bd_info;
169         usn_info_t *ui = (usn_info_t *)on->on_bi.bi_private;
170
171         Connection conn = { 0 };
172         OperationBuffer opbuf;
173         Operation *op;
174         Entry *e = NULL;
175         Attribute *a;
176         int rc;
177         void *thrctx = NULL;
178
179         thrctx = ldap_pvt_thread_pool_context();
180         connection_fake_init( &conn, &opbuf, thrctx );
181         op = &opbuf.ob_op;
182         op->o_bd = be;
183         op->o_dn = be->be_rootdn;
184         op->o_ndn = be->be_rootndn;
185
186         rc = overlay_entry_get_ov( op, be->be_nsuffix, NULL,
187                 slap_schema.si_ad_contextCSN, 0, &e, on );
188
189         if ( e ) {
190                 a = attr_find( e->e_attrs, ad_usnChanged );
191                 if ( a ) {
192                         ui->ui_current = atoi( a->a_vals[0].bv_val );
193                 }
194                 overlay_entry_release_ov( op, e, 0, on );
195         }
196         return 0;
197 }
198
199 static int
200 usn_db_init(
201         BackendDB *be,
202         ConfigReply *cr
203 )
204 {
205         slap_overinst   *on = (slap_overinst *)be->bd_info;
206         usn_info_t      *ui;
207
208         if ( SLAP_ISGLOBALOVERLAY( be ) ) {
209                 Debug( LDAP_DEBUG_ANY,
210                         "usn must be instantiated within a database.\n",
211                         0, 0, 0 );
212                 return 1;
213         }
214
215         ui = ch_calloc(1, sizeof(usn_info_t));
216         ldap_pvt_thread_mutex_init( &ui->ui_mutex );
217         on->on_bi.bi_private = ui;
218         return 0;
219 }
220
221 static int
222 usn_db_close(
223         BackendDB *be,
224         ConfigReply *cr
225 )
226 {
227         slap_overinst   *on = (slap_overinst *)be->bd_info;
228         usn_info_t      *ui = on->on_bi.bi_private;
229         Connection conn = {0};
230         OperationBuffer opbuf;
231         Operation *op;
232         SlapReply rs = {REP_RESULT};
233         void *thrctx;
234
235         Modifications mod;
236         slap_callback cb = {0};
237         char intbuf[64];
238         struct berval bv[2];
239
240         thrctx = ldap_pvt_thread_pool_context();
241         connection_fake_init( &conn, &opbuf, thrctx );
242         op = &opbuf.ob_op;
243         op->o_bd = be;
244         BER_BVZERO( &bv[1] );
245         bv[0].bv_len = snprintf( intbuf, sizeof(intbuf), "%d", ui->ui_current );
246         bv[0].bv_val = intbuf;
247         mod.sml_numvals = 1;
248         mod.sml_values = bv;
249         mod.sml_nvalues = NULL;
250         mod.sml_desc = ad_usnChanged;
251         mod.sml_op = LDAP_MOD_REPLACE;
252         mod.sml_flags = 0;
253         mod.sml_next = NULL;
254
255         cb.sc_response = slap_null_cb;
256         op->o_tag = LDAP_REQ_MODIFY;
257         op->o_callback = &cb;
258         op->orm_modlist = &mod;
259         op->orm_no_opattrs = 1;
260         op->o_dn = be->be_rootdn;
261         op->o_ndn = be->be_rootndn;
262         op->o_req_dn = op->o_bd->be_suffix[0];
263         op->o_req_ndn = op->o_bd->be_nsuffix[0];
264         op->o_bd->bd_info = on->on_info->oi_orig;
265         op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
266         op->o_no_schema_check = 1;
267         op->o_bd->be_modify( op, &rs );
268         if ( mod.sml_next != NULL ) {
269                 slap_mods_free( mod.sml_next, 1 );
270         }
271         return 0;
272 }
273
274 static int
275 usn_db_destroy(
276         BackendDB *be,
277         ConfigReply *cr
278 )
279 {
280         slap_overinst   *on = (slap_overinst *)be->bd_info;
281         usn_info_t      *ui = on->on_bi.bi_private;
282
283         ldap_pvt_thread_mutex_destroy( &ui->ui_mutex );
284         ch_free( ui );
285         on->on_bi.bi_private = NULL;
286         return 0;
287 }
288
289 /* This overlay is set up for dynamic loading via moduleload. For static
290  * configuration, you'll need to arrange for the slap_overinst to be
291  * initialized and registered by some other function inside slapd.
292  */
293
294 static slap_overinst usn;
295
296 int
297 usn_init( void )
298 {
299         int i, code;
300
301         memset( &usn, 0, sizeof( slap_overinst ) );
302         usn.on_bi.bi_type = "usn";
303         usn.on_bi.bi_db_init = usn_db_init;
304         usn.on_bi.bi_db_destroy = usn_db_destroy;
305         usn.on_bi.bi_db_open = usn_db_open;
306         usn.on_bi.bi_db_close = usn_db_close;
307
308         usn.on_bi.bi_op_modify = usn_func;
309         usn.on_bi.bi_op_modrdn = usn_func;
310         usn.on_bi.bi_op_add = usn_func;
311         usn.on_bi.bi_op_delete = usn_func;
312         usn.on_bi.bi_operational = usn_operational;
313
314         for ( i = 0; as[i].desc; i++ ) {
315                 code = register_at( as[i].desc, as[i].adp, 0 );
316                 if ( code ) {
317                         Debug( LDAP_DEBUG_ANY,
318                                 "usn_init: register_at #%d failed\n", i, 0, 0 );
319                         return code;
320                 }
321         }
322         return overlay_register( &usn );
323 }
324
325 #if SLAPD_OVER_USN == SLAPD_MOD_DYNAMIC
326 int
327 init_module( int argc, char *argv[] )
328 {
329         return usn_init();
330 }
331 #endif /* SLAPD_OVER_USN == SLAPD_MOD_DYNAMIC */
332
333 #endif /* defined(SLAPD_OVER_USN) */