r12701: the first scavenging run should happen after half of the renew_interval,
[jelmer/samba4-debian.git] / source / wrepl_server / wrepl_scavenging.c
1 /* 
2    Unix SMB/CIFS implementation.
3    
4    WINS Replication server
5    
6    Copyright (C) Stefan Metzmacher      2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "librpc/gen_ndr/ndr_winsrepl.h"
25 #include "wrepl_server/wrepl_server.h"
26 #include "nbt_server/wins/winsdb.h"
27 #include "ldb/include/ldb.h"
28 #include "ldb/include/ldb_errors.h"
29 #include "system/time.h"
30
31 const char *wreplsrv_owner_filter(struct wreplsrv_service *service,
32                                   TALLOC_CTX *mem_ctx,
33                                   const char *wins_owner)
34 {
35         if (strcmp(wins_owner, service->wins_db->local_owner) == 0) {
36                 return talloc_asprintf(mem_ctx, "(|(winsOwner=%s)(winsOwner=0.0.0.0))",
37                                        wins_owner);
38         }
39
40         return talloc_asprintf(mem_ctx, "(&(winsOwner=%s)(!(winsOwner=0.0.0.0)))",
41                                wins_owner);
42 }
43
44 static NTSTATUS wreplsrv_scavenging_owned_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
45 {
46         NTSTATUS status;
47         struct winsdb_record *rec = NULL;
48         struct ldb_result *res = NULL;
49         const char *owner_filter;
50         const char *filter;
51         uint32_t i;
52         int ret;
53         time_t now = time(NULL);
54         const char *now_timestr;
55         const char *action;
56         const char *old_state;
57         uint32_t modify_flags;
58         BOOL modify_record;
59         BOOL delete_record;
60         BOOL delete_tombstones;
61         struct timeval tombstone_extra_time;
62
63         now_timestr = ldb_timestring(tmp_mem, now);
64         NT_STATUS_HAVE_NO_MEMORY(now_timestr);
65         owner_filter = wreplsrv_owner_filter(service, tmp_mem,
66                                              service->wins_db->local_owner);
67         NT_STATUS_HAVE_NO_MEMORY(owner_filter);
68         filter = talloc_asprintf(tmp_mem,
69                                  "(&%s(objectClass=winsRecord)"
70                                  "(expireTime<=%s)(!(isStatic=1)))",
71                                  owner_filter, now_timestr);
72         NT_STATUS_HAVE_NO_MEMORY(filter);
73         ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
74         if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
75         talloc_steal(tmp_mem, res);
76         DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
77
78         tombstone_extra_time = timeval_add(&service->startup_time,
79                                            service->config.tombstone_extra_timeout,
80                                            0);
81         delete_tombstones = timeval_expired(&tombstone_extra_time);
82
83         for (i=0; i < res->count; i++) {
84                 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, &rec);
85                 NT_STATUS_NOT_OK_RETURN(status);
86
87                 if (rec->is_static) {
88                         DEBUG(0,("%s: corrupted record: %s\n",
89                                 __location__, nbt_name_string(rec, rec->name)));
90                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
91                 }
92
93                 if (rec->expire_time > now) {
94                         DEBUG(0,("%s: corrupted record: %s\n",
95                                 __location__, nbt_name_string(rec, rec->name)));
96                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
97                 }
98
99                 modify_flags    = 0;
100                 modify_record   = False;
101                 delete_record   = False;
102
103                 switch (rec->state) {
104                 case WREPL_STATE_ACTIVE:
105                         old_state       = "active";
106                         rec->state      = WREPL_STATE_RELEASED;
107                         rec->expire_time= service->config.tombstone_interval + now;
108                         modify_flags    = 0;
109                         modify_record   = True;
110                         break;
111
112                 case WREPL_STATE_RELEASED:
113                         old_state       = "released";
114                         rec->state      = WREPL_STATE_TOMBSTONE;
115                         rec->expire_time= service->config.tombstone_timeout + now;
116                         modify_flags    = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
117                         modify_record   = True;
118                         break;
119
120                 case WREPL_STATE_TOMBSTONE:
121                         old_state       = "tombstone";
122                         if (!delete_tombstones) break;
123                         delete_record = True;
124                         break;
125
126                 case WREPL_STATE_RESERVED:
127                         DEBUG(0,("%s: corrupted record: %s\n",
128                                 __location__, nbt_name_string(rec, rec->name)));
129                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
130                 }
131
132                 if (modify_record) {
133                         action = "modify";
134                         ret = winsdb_modify(service->wins_db, rec, modify_flags);
135                 } else if (delete_record) {
136                         action = "delete";
137                         ret = winsdb_delete(service->wins_db, rec);
138                 } else {
139                         action = "skip";
140                         ret = NBT_RCODE_OK;
141                 }
142
143                 if (ret != NBT_RCODE_OK) {
144                         DEBUG(1,("WINS scavenging: failed to %s name %s (owned:%s): error:%u\n",
145                                 action, nbt_name_string(rec, rec->name), old_state, ret));
146                 } else {
147                         DEBUG(4,("WINS scavenging: %s name: %s (owned:%s)\n",
148                                 action, nbt_name_string(rec, rec->name), old_state));
149                 }
150
151                 talloc_free(rec);
152         }
153
154         return NT_STATUS_OK;
155 }
156
157 static NTSTATUS wreplsrv_scavenging_replica_non_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
158 {
159         NTSTATUS status;
160         struct winsdb_record *rec = NULL;
161         struct ldb_result *res = NULL;
162         const char *owner_filter;
163         const char *filter;
164         uint32_t i;
165         int ret;
166         time_t now = time(NULL);
167         const char *now_timestr;
168         const char *action;
169         const char *old_state;
170         uint32_t modify_flags;
171         BOOL modify_record;
172         BOOL delete_record;
173         BOOL delete_tombstones;
174         struct timeval tombstone_extra_time;
175
176         now_timestr = ldb_timestring(tmp_mem, now);
177         NT_STATUS_HAVE_NO_MEMORY(now_timestr);
178         owner_filter = wreplsrv_owner_filter(service, tmp_mem,
179                                              service->wins_db->local_owner);
180         NT_STATUS_HAVE_NO_MEMORY(owner_filter);
181         filter = talloc_asprintf(tmp_mem,
182                                  "(&(!%s)(objectClass=winsRecord)"
183                                  "(!(recordState=%u))(expireTime<=%s)(!(isStatic=1)))",
184                                  owner_filter, WREPL_STATE_ACTIVE, now_timestr);
185         NT_STATUS_HAVE_NO_MEMORY(filter);
186         ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
187         if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
188         talloc_steal(tmp_mem, res);
189         DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
190
191         tombstone_extra_time = timeval_add(&service->startup_time,
192                                            service->config.tombstone_extra_timeout,
193                                            0);
194         delete_tombstones = timeval_expired(&tombstone_extra_time);
195
196         for (i=0; i < res->count; i++) {
197                 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, &rec);
198                 NT_STATUS_NOT_OK_RETURN(status);
199
200                 if (rec->is_static) {
201                         DEBUG(0,("%s: corrupted record: %s\n",
202                                 __location__, nbt_name_string(rec, rec->name)));
203                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
204                 }
205
206                 if (rec->expire_time > now) {
207                         DEBUG(0,("%s: corrupted record: %s\n",
208                                 __location__, nbt_name_string(rec, rec->name)));
209                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
210                 }
211
212                 modify_flags    = 0;
213                 modify_record   = False;
214                 delete_record   = False;
215
216                 switch (rec->state) {
217                 case WREPL_STATE_ACTIVE:
218                         DEBUG(0,("%s: corrupted record: %s\n",
219                                 __location__, nbt_name_string(rec, rec->name)));
220                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
221
222                 case WREPL_STATE_RELEASED:
223                         old_state       = "released";
224                         rec->state      = WREPL_STATE_TOMBSTONE;
225                         rec->expire_time= service->config.tombstone_timeout + now;
226                         modify_flags    = 0;
227                         modify_record   = True;
228                         break;
229
230                 case WREPL_STATE_TOMBSTONE:
231                         old_state       = "tombstone";
232                         if (!delete_tombstones) break;
233                         delete_record = True;
234                         break;
235
236                 case WREPL_STATE_RESERVED:
237                         DEBUG(0,("%s: corrupted record: %s\n",
238                                 __location__, nbt_name_string(rec, rec->name)));
239                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
240                 }
241
242                 if (modify_record) {
243                         action = "modify";
244                         ret = winsdb_modify(service->wins_db, rec, modify_flags);
245                 } else if (delete_record) {
246                         action = "delete";
247                         ret = winsdb_delete(service->wins_db, rec);
248                 } else {
249                         action = "skip";
250                         ret = NBT_RCODE_OK;
251                 }
252
253                 if (ret != NBT_RCODE_OK) {
254                         DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s): error:%u\n",
255                                 action, nbt_name_string(rec, rec->name), old_state, ret));
256                 } else {
257                         DEBUG(4,("WINS scavenging: %s name: %s (replica:%s)\n",
258                                 action, nbt_name_string(rec, rec->name), old_state));
259                 }
260
261                 talloc_free(rec);
262         }
263
264         return NT_STATUS_OK;
265 }
266
267 static NTSTATUS wreplsrv_scavenging_replica_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
268 {
269         NTSTATUS status;
270         struct winsdb_record *rec = NULL;
271         struct ldb_result *res = NULL;
272         const char *owner_filter;
273         const char *filter;
274         uint32_t i;
275         int ret;
276         time_t now = time(NULL);
277         const char *now_timestr;
278         const char *action;
279         const char *old_state;
280         BOOL modify_flags;
281         BOOL modify_record;
282         BOOL delete_record;
283
284         now_timestr = ldb_timestring(tmp_mem, now);
285         NT_STATUS_HAVE_NO_MEMORY(now_timestr);
286         owner_filter = wreplsrv_owner_filter(service, tmp_mem,
287                                              service->wins_db->local_owner);
288         NT_STATUS_HAVE_NO_MEMORY(owner_filter);
289         filter = talloc_asprintf(tmp_mem,
290                                  "(&(!%s)(objectClass=winsRecord)"
291                                  "(recordState=%u)(expireTime<=%s)(!(isStatic=1)))",
292                                  owner_filter, WREPL_STATE_ACTIVE, now_timestr);
293         NT_STATUS_HAVE_NO_MEMORY(filter);
294         ret = ldb_search(service->wins_db->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
295         if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
296         talloc_steal(tmp_mem, res);
297         DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
298
299         for (i=0; i < res->count; i++) {
300                 status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, &rec);
301                 NT_STATUS_NOT_OK_RETURN(status);
302
303                 if (rec->is_static) {
304                         DEBUG(0,("%s: corrupted record: %s\n",
305                                 __location__, nbt_name_string(rec, rec->name)));
306                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
307                 }
308
309                 if (rec->expire_time > now) {
310                         DEBUG(0,("%s: corrupted record: %s\n",
311                                 __location__, nbt_name_string(rec, rec->name)));
312                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
313                 }
314
315                 if (rec->state != WREPL_STATE_ACTIVE) {
316                         DEBUG(0,("%s: corrupted record: %s\n",
317                                 __location__, nbt_name_string(rec, rec->name)));
318                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
319                 }
320
321                 old_state = "active";
322
323                 modify_flags    = 0;
324                 modify_record   = False;
325                 delete_record   = False;
326
327                 /* 
328                  * TODO: ask the owning wins server if the record still exists,
329                  *       if not delete the record
330                  */
331                 DEBUG(0,("TODO: ask wins server '%s' if '%s' with version_id:%llu still exists\n",
332                         rec->wins_owner, nbt_name_string(rec, rec->name), rec->version));
333
334                 if (modify_record) {
335                         action = "modify";
336                         ret = winsdb_modify(service->wins_db, rec, modify_flags);
337                 } else if (delete_record) {
338                         action = "delete";
339                         ret = winsdb_delete(service->wins_db, rec);
340                 } else {
341                         action = "skip";
342                         ret = NBT_RCODE_OK;
343                 }
344
345                 if (ret != NBT_RCODE_OK) {
346                         DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s): error:%u\n",
347                                 action, nbt_name_string(rec, rec->name), old_state, ret));
348                 } else {
349                         DEBUG(4,("WINS scavenging: %s name: %s (replica:%s)\n",
350                                 action, nbt_name_string(rec, rec->name), old_state));
351                 }
352
353                 talloc_free(rec);
354         }
355
356         return NT_STATUS_OK;
357 }
358
359 NTSTATUS wreplsrv_scavenging_run(struct wreplsrv_service *service)
360 {
361         NTSTATUS status;
362         TALLOC_CTX *tmp_mem;
363         BOOL skip_first_run = False;
364
365         if (!timeval_expired(&service->scavenging.next_run)) {
366                 return NT_STATUS_OK;
367         }
368
369         if (timeval_is_zero(&service->scavenging.next_run)) {
370                 skip_first_run = True;
371         }
372
373         service->scavenging.next_run = timeval_current_ofs(service->config.scavenging_interval, 0);
374         status = wreplsrv_periodic_schedule(service, service->config.scavenging_interval);
375         NT_STATUS_NOT_OK_RETURN(status);
376
377         /*
378          * if it's the first time this functions is called (startup)
379          * the next_run is zero, in this case we should not do scavenging
380          */
381         if (skip_first_run) {
382                 return NT_STATUS_OK;
383         }
384
385         if (service->scavenging.processing) {
386                 return NT_STATUS_OK;
387         }
388
389         DEBUG(4,("wreplsrv_scavenging_run(): start\n"));
390
391         tmp_mem = talloc_new(service);
392         service->scavenging.processing = True;
393         status = wreplsrv_scavenging_owned_records(service,tmp_mem);
394         service->scavenging.processing = False;
395         talloc_free(tmp_mem);
396         NT_STATUS_NOT_OK_RETURN(status);
397
398         tmp_mem = talloc_new(service);  
399         service->scavenging.processing = True;
400         status = wreplsrv_scavenging_replica_non_active_records(service, tmp_mem);
401         service->scavenging.processing = False;
402         talloc_free(tmp_mem);
403         NT_STATUS_NOT_OK_RETURN(status);
404
405         tmp_mem = talloc_new(service);
406         service->scavenging.processing = True;
407         status = wreplsrv_scavenging_replica_active_records(service, tmp_mem);
408         service->scavenging.processing = False;
409         talloc_free(tmp_mem);
410         NT_STATUS_NOT_OK_RETURN(status);
411
412         DEBUG(4,("wreplsrv_scavenging_run(): end\n"));
413
414         return NT_STATUS_OK;
415 }