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