r12434: implement database scavenging, the only missing part is the verifying of...
[samba.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 "dlinklist.h"
25 #include "lib/events/events.h"
26 #include "lib/socket/socket.h"
27 #include "smbd/service_task.h"
28 #include "smbd/service_stream.h"
29 #include "lib/messaging/irpc.h"
30 #include "librpc/gen_ndr/ndr_winsrepl.h"
31 #include "wrepl_server/wrepl_server.h"
32 #include "nbt_server/wins/winsdb.h"
33 #include "ldb/include/ldb.h"
34 #include "ldb/include/ldb_errors.h"
35 #include "libcli/composite/composite.h"
36 #include "libcli/wrepl/winsrepl.h"
37 #include "wrepl_server/wrepl_out_helpers.h"
38 #include "system/time.h"
39
40 static NTSTATUS wreplsrv_scavenging_owned_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
41 {
42         NTSTATUS status;
43         struct winsdb_record *rec = NULL;
44         struct ldb_result *res = NULL;
45         const char *filter;
46         uint32_t i;
47         int ret;
48         time_t now = time(NULL);
49         const char *now_timestr;
50         const char *action;
51         const char *old_state;
52         uint32_t modify_flags;
53         BOOL modify_record;
54         BOOL delete_record;
55         BOOL delete_tombstones;
56         struct timeval tombstone_extra_time;
57
58         now_timestr = ldb_timestring(tmp_mem, now);
59         NT_STATUS_HAVE_NO_MEMORY(now_timestr);
60         filter = talloc_asprintf(tmp_mem,
61                                  "(&(winsOwner=%s)(objectClass=winsRecord)"
62                                  "(expireTime<=%s)(!(isStatic=1)))",
63                                  WINSDB_OWNER_LOCAL, now_timestr);
64         NT_STATUS_HAVE_NO_MEMORY(filter);
65         ret = ldb_search(service->wins_db, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
66         if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
67         talloc_steal(tmp_mem, res);
68
69         tombstone_extra_time = timeval_add(&service->startup_time,
70                                            service->config.tombstone_extra_timeout,
71                                            0);
72         delete_tombstones = timeval_expired(&tombstone_extra_time);
73
74         for (i=0; i < res->count; i++) {
75                 status = winsdb_record(res->msgs[i], tmp_mem, &rec);
76                 NT_STATUS_NOT_OK_RETURN(status);
77
78                 if (rec->is_static) {
79                         DEBUG(0,("%s: corrupted record: %s\n",
80                                 __location__, nbt_name_string(rec, rec->name)));
81                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
82                 }
83
84                 if (rec->expire_time > now) {
85                         DEBUG(0,("%s: corrupted record: %s\n",
86                                 __location__, nbt_name_string(rec, rec->name)));
87                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
88                 }
89
90                 modify_flags    = 0;
91                 modify_record   = False;
92                 delete_record   = False;
93
94                 switch (rec->state) {
95                 case WREPL_STATE_ACTIVE:
96                         old_state       = "active";
97                         rec->state      = WREPL_STATE_RELEASED;
98                         rec->expire_time= service->config.tombstone_interval + now;
99                         modify_flags    = 0;
100                         modify_record   = True;
101                         break;
102
103                 case WREPL_STATE_RELEASED:
104                         old_state       = "released";
105                         rec->state      = WREPL_STATE_TOMBSTONE;
106                         rec->expire_time= service->config.tombstone_timeout + now;
107                         modify_flags    = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
108                         modify_record   = True;
109                         break;
110
111                 case WREPL_STATE_TOMBSTONE:
112                         old_state       = "tombstone";
113                         if (!delete_tombstones) break;
114                         delete_record = True;
115                         break;
116
117                 case WREPL_STATE_RESERVED:
118                         DEBUG(0,("%s: corrupted record: %s\n",
119                                 __location__, nbt_name_string(rec, rec->name)));
120                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
121                 }
122
123                 if (modify_record) {
124                         action = "modify";
125                         ret = winsdb_modify(service->wins_db, rec, modify_flags);
126                 } else if (delete_record) {
127                         action = "delete";
128                         ret = winsdb_delete(service->wins_db, rec);
129                 } else {
130                         action = "skip";
131                         ret = NBT_RCODE_OK;
132                 }
133
134                 if (ret != NBT_RCODE_OK) {
135                         DEBUG(1,("WINS scavenging: failed to %s name %s (owned:%s): error:%u\n",
136                                 action, nbt_name_string(rec, rec->name), old_state, ret));
137                 } else {
138                         DEBUG(4,("WINS scavenging: %s name: %s (owned:%s)\n",
139                                 action, nbt_name_string(rec, rec->name), old_state));
140                 }
141
142                 talloc_free(rec);
143         }
144
145         return NT_STATUS_OK;
146 }
147
148 static NTSTATUS wreplsrv_scavenging_replica_non_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
149 {
150         NTSTATUS status;
151         struct winsdb_record *rec = NULL;
152         struct ldb_result *res = NULL;
153         const char *filter;
154         uint32_t i;
155         int ret;
156         time_t now = time(NULL);
157         const char *now_timestr;
158         const char *action;
159         const char *old_state;
160         uint32_t modify_flags;
161         BOOL modify_record;
162         BOOL delete_record;
163         BOOL delete_tombstones;
164         struct timeval tombstone_extra_time;
165
166         now_timestr = ldb_timestring(tmp_mem, now);
167         NT_STATUS_HAVE_NO_MEMORY(now_timestr);
168         filter = talloc_asprintf(tmp_mem,
169                                  "(&(!(winsOwner=%s))(objectClass=winsRecord)"
170                                  "(!(recordState=%u))(expireTime<=%s)(!(isStatic=1)))",
171                                  WINSDB_OWNER_LOCAL, WREPL_STATE_ACTIVE, now_timestr);
172         NT_STATUS_HAVE_NO_MEMORY(filter);
173         ret = ldb_search(service->wins_db, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
174         if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
175         talloc_steal(tmp_mem, res);
176
177         tombstone_extra_time = timeval_add(&service->startup_time,
178                                            service->config.tombstone_extra_timeout,
179                                            0);
180         delete_tombstones = timeval_expired(&tombstone_extra_time);
181
182         for (i=0; i < res->count; i++) {
183                 status = winsdb_record(res->msgs[i], tmp_mem, &rec);
184                 NT_STATUS_NOT_OK_RETURN(status);
185
186                 if (rec->is_static) {
187                         DEBUG(0,("%s: corrupted record: %s\n",
188                                 __location__, nbt_name_string(rec, rec->name)));
189                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
190                 }
191
192                 if (rec->expire_time > now) {
193                         DEBUG(0,("%s: corrupted record: %s\n",
194                                 __location__, nbt_name_string(rec, rec->name)));
195                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
196                 }
197
198                 modify_flags    = 0;
199                 modify_record   = False;
200                 delete_record   = False;
201
202                 switch (rec->state) {
203                 case WREPL_STATE_ACTIVE:
204                         DEBUG(0,("%s: corrupted record: %s\n",
205                                 __location__, nbt_name_string(rec, rec->name)));
206                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
207
208                 case WREPL_STATE_RELEASED:
209                         old_state       = "released";
210                         rec->state      = WREPL_STATE_TOMBSTONE;
211                         rec->expire_time= service->config.tombstone_timeout + now;
212                         modify_flags    = 0;
213                         modify_record   = True;
214                         break;
215
216                 case WREPL_STATE_TOMBSTONE:
217                         old_state       = "tombstone";
218                         if (!delete_tombstones) break;
219                         delete_record = True;
220                         break;
221
222                 case WREPL_STATE_RESERVED:
223                         DEBUG(0,("%s: corrupted record: %s\n",
224                                 __location__, nbt_name_string(rec, rec->name)));
225                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
226                 }
227
228                 if (modify_record) {
229                         action = "modify";
230                         ret = winsdb_modify(service->wins_db, rec, modify_flags);
231                 } else if (delete_record) {
232                         action = "delete";
233                         ret = winsdb_delete(service->wins_db, rec);
234                 } else {
235                         action = "skip";
236                         ret = NBT_RCODE_OK;
237                 }
238
239                 if (ret != NBT_RCODE_OK) {
240                         DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s): error:%u\n",
241                                 action, nbt_name_string(rec, rec->name), old_state, ret));
242                 } else {
243                         DEBUG(4,("WINS scavenging: %s name: %s (replica:%s)\n",
244                                 action, nbt_name_string(rec, rec->name), old_state));
245                 }
246
247                 talloc_free(rec);
248         }
249
250         return NT_STATUS_OK;
251 }
252
253 static NTSTATUS wreplsrv_scavenging_replica_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
254 {
255         NTSTATUS status;
256         struct winsdb_record *rec = NULL;
257         struct ldb_result *res = NULL;
258         const char *filter;
259         uint32_t i;
260         int ret;
261         time_t now = time(NULL);
262         const char *now_timestr;
263         const char *action;
264         const char *old_state;
265         BOOL modify_flags;
266         BOOL modify_record;
267         BOOL delete_record;
268
269         now_timestr = ldb_timestring(tmp_mem, now);
270         NT_STATUS_HAVE_NO_MEMORY(now_timestr);
271         filter = talloc_asprintf(tmp_mem,
272                                  "(&(!(winsOwner=%s))(objectClass=winsRecord)"
273                                  "(recordState=%u)(expireTime<=%s)(!(isStatic=1)))",
274                                  WINSDB_OWNER_LOCAL, WREPL_STATE_ACTIVE, now_timestr);
275         NT_STATUS_HAVE_NO_MEMORY(filter);
276         ret = ldb_search(service->wins_db, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
277         if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
278         talloc_steal(tmp_mem, res);
279
280         for (i=0; i < res->count; i++) {
281                 status = winsdb_record(res->msgs[i], tmp_mem, &rec);
282                 NT_STATUS_NOT_OK_RETURN(status);
283
284                 if (rec->is_static) {
285                         DEBUG(0,("%s: corrupted record: %s\n",
286                                 __location__, nbt_name_string(rec, rec->name)));
287                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
288                 }
289
290                 if (rec->expire_time > now) {
291                         DEBUG(0,("%s: corrupted record: %s\n",
292                                 __location__, nbt_name_string(rec, rec->name)));
293                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
294                 }
295
296                 if (rec->state != WREPL_STATE_ACTIVE) {
297                         DEBUG(0,("%s: corrupted record: %s\n",
298                                 __location__, nbt_name_string(rec, rec->name)));
299                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
300                 }
301
302                 old_state = "active";
303
304                 modify_flags    = 0;
305                 modify_record   = False;
306                 delete_record   = False;
307
308                 /* 
309                  * TODO: ask the owning wins server if the record still exists,
310                  *       if not delete the record
311                  */
312                 DEBUG(0,("TODO: ask wins server '%s' if '%s' with version_id:%llu still exists\n",
313                         rec->wins_owner, nbt_name_string(rec, rec->name), rec->version));
314
315                 if (modify_record) {
316                         action = "modify";
317                         ret = winsdb_modify(service->wins_db, rec, modify_flags);
318                 } else if (delete_record) {
319                         action = "delete";
320                         ret = winsdb_delete(service->wins_db, rec);
321                 } else {
322                         action = "skip";
323                         ret = NBT_RCODE_OK;
324                 }
325
326                 if (ret != NBT_RCODE_OK) {
327                         DEBUG(1,("WINS scavenging: failed to %s name %s (replica:%s): error:%u\n",
328                                 action, nbt_name_string(rec, rec->name), old_state, ret));
329                 } else {
330                         DEBUG(4,("WINS scavenging: %s name: %s (replica:%s)\n",
331                                 action, nbt_name_string(rec, rec->name), old_state));
332                 }
333
334                 talloc_free(rec);
335         }
336
337         return NT_STATUS_OK;
338 }
339
340 NTSTATUS wreplsrv_scavenging_run(struct wreplsrv_service *service)
341 {
342         NTSTATUS status;
343         TALLOC_CTX *tmp_mem;
344
345         if (!timeval_expired(&service->scavenging.next_run)) {
346                 return NT_STATUS_OK;
347         }
348
349         service->scavenging.next_run = timeval_current_ofs(service->config.scavenging_interval, 0);
350         status = wreplsrv_periodic_schedule(service, service->config.scavenging_interval);
351         NT_STATUS_NOT_OK_RETURN(status);
352
353         if (service->scavenging.processing) {
354                 return NT_STATUS_OK;
355         }
356
357         DEBUG(4,("wreplsrv_scavenging_run(): start\n"));
358
359         tmp_mem = talloc_new(service);
360         service->scavenging.processing = True;
361         status = wreplsrv_scavenging_owned_records(service,tmp_mem);
362         service->scavenging.processing = False;
363         talloc_free(tmp_mem);
364         NT_STATUS_NOT_OK_RETURN(status);
365
366         tmp_mem = talloc_new(service);  
367         service->scavenging.processing = True;
368         status = wreplsrv_scavenging_replica_non_active_records(service, tmp_mem);
369         service->scavenging.processing = False;
370         talloc_free(tmp_mem);
371         NT_STATUS_NOT_OK_RETURN(status);
372
373         tmp_mem = talloc_new(service);
374         service->scavenging.processing = True;
375         status = wreplsrv_scavenging_replica_active_records(service, tmp_mem);
376         service->scavenging.processing = False;
377         talloc_free(tmp_mem);
378         NT_STATUS_NOT_OK_RETURN(status);
379
380         DEBUG(4,("wreplsrv_scavenging_run(): end\n"));
381
382         return NT_STATUS_OK;
383 }