|
|
|
1 |
/* translog.c - log modifications for replication purposes */ |
2 |
|
3 |
#include "portable.h" |
4 |
|
5 |
#ifdef SLAPD_OVER_TRANSLOG |
6 |
|
7 |
#include <stdio.h> |
8 |
#include <unistd.h> |
9 |
#include <ac/string.h> |
10 |
#include <ac/ctype.h> |
11 |
#include "slap.h" |
12 |
|
13 |
//#define O_DEBUG /* enable debug messages */ |
14 |
|
15 |
typedef struct translog_data { |
16 |
ldap_pvt_thread_mutex_t ad_mutex; |
17 |
char *ad_logfile; |
18 |
} translog_data; |
19 |
|
20 |
static const char LAST_ID[] = "/var/lib/univention-ldap/last_id"; |
21 |
static const char LAST_ID_TMP[] = "/var/lib/univention-ldap/last_id.new"; |
22 |
static const char TRANSACTION[] = "/var/lib/univention-ldap/notify/transaction"; |
23 |
|
24 |
static FILE* fopen_lock(const char *name, const char *type, FILE **l_file) |
25 |
{ |
26 |
char buf[PATH_MAX]; |
27 |
FILE *file; |
28 |
int fd; |
29 |
|
30 |
if (snprintf(buf, sizeof(buf), "%s.lock", name) >= sizeof(buf)) |
31 |
return NULL; |
32 |
|
33 |
if ((*l_file = fopen(buf, type)) == NULL) |
34 |
return NULL; |
35 |
|
36 |
fd = fileno(*l_file); |
37 |
lockf(fd, F_LOCK, 0); |
38 |
|
39 |
if ((file = fopen(name, type)) == NULL) { |
40 |
fclose(*l_file); |
41 |
*l_file = NULL; |
42 |
} |
43 |
|
44 |
return file; |
45 |
} |
46 |
|
47 |
static int fclose_lock(FILE **file, FILE **l_file) |
48 |
{ |
49 |
int ret = 0; |
50 |
|
51 |
if (*file != NULL) { |
52 |
ret = fclose(*file); |
53 |
*file = NULL; |
54 |
} |
55 |
|
56 |
if (*l_file != NULL) { |
57 |
fclose(*l_file); |
58 |
*l_file = NULL; |
59 |
} |
60 |
|
61 |
return ret; |
62 |
} |
63 |
|
64 |
static long from_lastid() |
65 |
{ |
66 |
/* if the file /var/lib/univention-ldap/last_id exists, we read the last id |
67 |
* from this file |
68 |
*/ |
69 |
FILE *f; |
70 |
long id = -1; |
71 |
|
72 |
if ((f = fopen(LAST_ID, "r")) != NULL) { |
73 |
int n = fscanf(f, "%ld", &id); |
74 |
fclose(f); |
75 |
|
76 |
if (n != 1) |
77 |
Debug(LDAP_DEBUG_ANY, "OVER: Failed to parse %s\n", LAST_ID, 0, 0); |
78 |
} |
79 |
|
80 |
return id; |
81 |
} |
82 |
|
83 |
static long from_translog(const char *filename) |
84 |
{ |
85 |
FILE *f; |
86 |
long off = 2; |
87 |
int c; |
88 |
long id = -1; |
89 |
|
90 |
if ((f = fopen(filename, "r")) == NULL) { |
91 |
Debug( LDAP_DEBUG_ANY, "OVER: unable to open file %s\n", filename, 0, 0); |
92 |
return -1; |
93 |
} |
94 |
|
95 |
do { |
96 |
off++; |
97 |
if (!fseek(f, -off, SEEK_END)) |
98 |
Debug(LDAP_DEBUG_ANY, "OVER: seek failed on %s\n", filename, 0, 0); |
99 |
c = fgetc(f); |
100 |
} while (c != '\n' && c != EOF && ftell(f) != 1); |
101 |
|
102 |
if (c == EOF) { |
103 |
/* emty file */ |
104 |
id = 0; |
105 |
} else { |
106 |
if (ftell(f) == 1) { |
107 |
/* only one entry */ |
108 |
if (fseek(f, 0, SEEK_SET)) |
109 |
Debug(LDAP_DEBUG_ANY, "OVER: seek failed on %s\n", filename, 0, 0); |
110 |
} |
111 |
int n = fscanf(f, "%ld", &id); |
112 |
if (n != 1) |
113 |
Debug(LDAP_DEBUG_ANY, "OVER: Failed to parse %s at %ld\n", filename, off, 0); |
114 |
} |
115 |
|
116 |
fclose(f); |
117 |
return id; |
118 |
} |
119 |
|
120 |
static long get_last_id(Operation *op) |
121 |
{ |
122 |
/* Get the last used Transaction ID from Transaction-Log file |
123 |
* Returns: |
124 |
* -1 und errors |
125 |
* 0 if Transaction-Log file is empty |
126 |
* last Transaction ID if Transaction-Log file is nonempty |
127 |
*/ |
128 |
long id; |
129 |
|
130 |
Debug( LDAP_DEBUG_TRACE, "OVER: get_last_id\n", 0, 0, 0); |
131 |
|
132 |
id = from_lastid(); |
133 |
if (id >= 1) |
134 |
return id; |
135 |
|
136 |
/* File must be locked to prevent UDN from renaming it underneth */ |
137 |
slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; |
138 |
translog_data *ad = on->on_bi.bi_private; |
139 |
id = from_translog(ad->ad_logfile); |
140 |
if (id >= 1) |
141 |
return id; |
142 |
|
143 |
id = from_translog(TRANSACTION); |
144 |
return id; |
145 |
} |
146 |
|
147 |
static int translog_response(Operation *op, SlapReply *rs) { |
148 |
/* Handel all requests and responses from and to database backend |
149 |
* Returns: |
150 |
* SLAP_CB_CONTINUE in all cases |
151 |
*/ |
152 |
slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; |
153 |
translog_data *ad = on->on_bi.bi_private; |
154 |
FILE *file, *l_file; |
155 |
char what; |
156 |
long lastid = -1; |
157 |
int failure = 0; |
158 |
|
159 |
if ( rs->sr_err != LDAP_SUCCESS ) { |
160 |
Debug( LDAP_DEBUG_TRACE, "OVER: rs->sr_err != LDAP_SUCCESS on \"%s\" ERR: 0x%02x\n", op->o_req_dn.bv_val, rs->sr_err, 0 ); |
161 |
return SLAP_CB_CONTINUE; |
162 |
} |
163 |
|
164 |
if ( !op->o_bd || !ad->ad_logfile ) { |
165 |
Debug( LDAP_DEBUG_TRACE, "OVER: BackendDB or logfile not specified\n", 0, 0, 0 ); |
166 |
return SLAP_CB_CONTINUE; |
167 |
} |
168 |
|
169 |
switch(op->o_tag) { |
170 |
case LDAP_REQ_MODRDN: what = 'r'; break; /* 0x6cU == LDAP_REQ_MODDN == LDAP_REQ_RENAME */ |
171 |
case LDAP_REQ_DELETE: what = 'd'; break; /* 0x4aU */ |
172 |
case LDAP_REQ_ADD: what = 'a'; break; /* 0x68U */ |
173 |
case LDAP_REQ_MODIFY: what = 'm'; break; /* 0x66U */ |
174 |
case LDAP_REQ_SEARCH: return SLAP_CB_CONTINUE; /* 0x63U get rid of search debug messages */ |
175 |
default: |
176 |
Debug( LDAP_DEBUG_TRACE, "OVER: SWITCH(o_tag) default, case was: \"%lu\"\n", (unsigned long) op->o_tag, 0, 0 ); |
177 |
return SLAP_CB_CONTINUE; |
178 |
} |
179 |
|
180 |
ldap_pvt_thread_mutex_lock(&ad->ad_mutex); |
181 |
|
182 |
if ((file = fopen_lock(ad->ad_logfile, "a", &l_file)) == NULL) { |
183 |
Debug( LDAP_DEBUG_ANY, "OVER: Could not open translog file %s\n", ad->ad_logfile, 0, 0 ); |
184 |
|
185 |
ldap_pvt_thread_mutex_unlock(&ad->ad_mutex); |
186 |
return SLAP_CB_CONTINUE; |
187 |
} |
188 |
|
189 |
lastid = get_last_id(op); |
190 |
if ( lastid > -1 ) { |
191 |
Debug( LDAP_DEBUG_TRACE, "OVER: Found ID \"%ld\"\n", lastid, 0, 0 ); |
192 |
failure |= fprintf(file, "%ld %s %c\n", ++lastid, op->o_req_dn.bv_val, what) <= 0; |
193 |
if ( what == 'r' && op->orr_newSup ) { |
194 |
/* print newsuperior as add in translog file */ |
195 |
failure |= fprintf(file, "%ld %s,%s a\n", ++lastid, op->orr_newrdn.bv_val, op->orr_newSup->bv_val) <= 0; |
196 |
} else if ( what == 'r' ) { |
197 |
struct berval pdn; |
198 |
dnParent( &op->o_req_dn, &pdn ); |
199 |
failure |= fprintf(file, "%ld %s,%s a\n", ++lastid, op->orr_newrdn.bv_val, pdn.bv_val) <= 0; |
200 |
} |
201 |
} else { |
202 |
Debug( LDAP_DEBUG_ANY, "OVER: Could not find last ID, lastid seems to be: \"%ld\"\n", lastid, 0, 0 ); |
203 |
failure |= fprintf(file, "<TransID> %s %c\n", op->o_req_dn.bv_val, what) <= 0; |
204 |
if ( what == 'r' && op->orr_newSup ) { |
205 |
/* print newsuperior as add in translog file */ |
206 |
failure |= fprintf(file, "<TransID> %s,%s a\n", op->orr_newrdn.bv_val, op->orr_newSup->bv_val) <= 0; |
207 |
} else if ( what == 'r' ) { |
208 |
struct berval pdn; |
209 |
dnParent( &op->o_req_dn, &pdn ); |
210 |
failure |= fprintf(file, "<TransID> %s,%s a\n", op->orr_newrdn.bv_val, pdn.bv_val) <= 0; |
211 |
} |
212 |
} |
213 |
|
214 |
failure |= fclose_lock(&file, &l_file) != 0; |
215 |
if (failure) |
216 |
Debug(LDAP_DEBUG_ANY, "OVER: Could not append file %s\n", ad->ad_logfile, 0, 0); |
217 |
|
218 |
if ((file = fopen(LAST_ID_TMP, "w")) == NULL || |
219 |
fprintf(file, "%ld", lastid) <= 0 || |
220 |
!fclose(file)) { |
221 |
Debug(LDAP_DEBUG_ANY, "OVER: Could not write file %s\n", LAST_ID_TMP, 0, 0); |
222 |
} else { |
223 |
if (rename(LAST_ID_TMP, LAST_ID)) |
224 |
Debug(LDAP_DEBUG_ANY, "OVER: Could not rename file %s\n", LAST_ID_TMP, 0, 0); |
225 |
} |
226 |
|
227 |
#ifdef O_DEBUG |
228 |
/* Test if the entry is in backend allready */ |
229 |
int rc; |
230 |
Entry *e; |
231 |
/* set "real" backend as next backend */ |
232 |
op->o_bd->bd_info = on->on_info->oi_orig; |
233 |
rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); |
234 |
Debug( LDAP_DEBUG_TRACE, "OVER: Fetched Object, returncode was \"%d\"\n", rc, 0, 0); |
235 |
if ( e ) { |
236 |
Debug( LDAP_DEBUG_TRACE, "OVER: Fetched Object \"%s\"\n", e->e_ndn, 0, 0); |
237 |
} |
238 |
op->o_bd->bd_info = (BackendInfo *)on; |
239 |
#endif |
240 |
|
241 |
ldap_pvt_thread_mutex_unlock(&ad->ad_mutex); |
242 |
return SLAP_CB_CONTINUE; |
243 |
} |
244 |
|
245 |
static slap_overinst translog; |
246 |
|
247 |
static int translog_db_init( BackendDB *be ) { |
248 |
Debug( LDAP_DEBUG_TRACE, "OVER: db_init\n", 0, 0, 0 ); |
249 |
|
250 |
slap_overinst *on = (slap_overinst *)be->bd_info; |
251 |
translog_data *ad = ch_calloc(1, sizeof(translog_data)); |
252 |
|
253 |
on->on_bi.bi_private = ad; |
254 |
ldap_pvt_thread_mutex_init( &ad->ad_mutex ); |
255 |
return 0; |
256 |
} |
257 |
|
258 |
static int translog_db_close( BackendDB *be ) { |
259 |
Debug( LDAP_DEBUG_TRACE, "OVER: db_close\n", 0, 0, 0 ); |
260 |
|
261 |
slap_overinst *on = (slap_overinst *)be->bd_info; |
262 |
translog_data *ad = on->on_bi.bi_private; |
263 |
|
264 |
free( ad->ad_logfile ); |
265 |
ad->ad_logfile = NULL; |
266 |
return 0; |
267 |
} |
268 |
|
269 |
static int translog_db_destroy( BackendDB *be ) { |
270 |
Debug( LDAP_DEBUG_TRACE, "OVER: db_destroy\n", 0, 0, 0 ); |
271 |
|
272 |
slap_overinst *on = (slap_overinst *)be->bd_info; |
273 |
translog_data *ad = on->on_bi.bi_private; |
274 |
|
275 |
ldap_pvt_thread_mutex_destroy( &ad->ad_mutex ); |
276 |
free( ad ); |
277 |
return 0; |
278 |
} |
279 |
|
280 |
static int translog_config( BackendDB *be, const char *fname, int lineno, int argc, char **argv ) { |
281 |
slap_overinst *on = (slap_overinst *) be->bd_info; |
282 |
translog_data *ad = on->on_bi.bi_private; |
283 |
|
284 |
Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_TRACE, "OVER: Configuring Translog Overlay\n", 0, 0, 0 ); |
285 |
|
286 |
/* log file */ |
287 |
if ( strcasecmp( argv[0], "translog" ) == 0 ) { |
288 |
if ( argc < 2 ) { |
289 |
Debug( LDAP_DEBUG_ANY, "%s: line %d: missing filename in \"translog <filename>\" line\n", fname, lineno, 0 ); |
290 |
return(1); |
291 |
} |
292 |
Debug(LDAP_DEBUG_CONFIG, "OVER: Configured Translog Overlay to use file \"%s\"\n", argv[1], 0, 0); |
293 |
|
294 |
/* save filename in translog_data */ |
295 |
ad->ad_logfile = ch_strdup( argv[1] ); |
296 |
return 0; |
297 |
} |
298 |
return SLAP_CONF_UNKNOWN; |
299 |
} |
300 |
|
301 |
int translog_init() { |
302 |
Debug( LDAP_DEBUG_TRACE, "OVER: Loading Translog Overlay\n", 0, 0, 0 ); |
303 |
|
304 |
translog.on_bi.bi_type = "translog"; |
305 |
translog.on_bi.bi_db_init = translog_db_init; |
306 |
translog.on_bi.bi_db_config = translog_config; |
307 |
translog.on_bi.bi_db_close = translog_db_close; |
308 |
translog.on_bi.bi_db_destroy = translog_db_destroy; |
309 |
translog.on_response = translog_response; |
310 |
|
311 |
return overlay_register(&translog); |
312 |
} |
313 |
|
314 |
#if SLAPD_OVER_TRANSLOG == SLAPD_MOD_DYNAMIC |
315 |
int |
316 |
init_module( int argc, char *argv[] ) |
317 |
{ |
318 |
return translog_init(); |
319 |
} |
320 |
#endif |
321 |
|
322 |
#endif /* SLAPD_OVER_TRANSLOG */ |