8f705d7aa13acb899dbe6222a8ab7a1b7e0f8414
[obnox/samba/samba-obnox.git] / source4 / dsdb / kcc / kcc_periodic.c
1 /* 
2    Unix SMB/CIFS mplementation.
3    KCC service periodic handling
4    
5    Copyright (C) Andrew Tridgell 2009
6    based on repl service code
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 3 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, see <http://www.gnu.org/licenses/>.
20    
21 */
22
23 #include "includes.h"
24 #include "lib/events/events.h"
25 #include "dsdb/samdb/samdb.h"
26 #include "auth/auth.h"
27 #include "smbd/service.h"
28 #include "lib/messaging/irpc.h"
29 #include "dsdb/kcc/kcc_connection.h"
30 #include "dsdb/kcc/kcc_service.h"
31 #include <ldb_errors.h>
32 #include "../lib/util/dlinklist.h"
33 #include "librpc/gen_ndr/ndr_misc.h"
34 #include "librpc/gen_ndr/ndr_drsuapi.h"
35 #include "librpc/gen_ndr/ndr_drsblobs.h"
36 #include "librpc/gen_ndr/ndr_irpc_c.h"
37 #include "param/param.h"
38 #include "dsdb/common/util.h"
39
40 /*
41  * see if two repsFromToBlob blobs are for the same source DSA
42  */
43 static bool kccsrv_same_source_dsa(struct repsFromToBlob *r1, struct repsFromToBlob *r2)
44 {
45         return GUID_compare(&r1->ctr.ctr1.source_dsa_obj_guid,
46                             &r2->ctr.ctr1.source_dsa_obj_guid) == 0;
47 }
48
49 /*
50  * see if a repsFromToBlob is in a list
51  */
52 static bool reps_in_list(struct repsFromToBlob *r, struct repsFromToBlob *reps, uint32_t count)
53 {
54         uint32_t i;
55         for (i=0; i<count; i++) {
56                 if (kccsrv_same_source_dsa(r, &reps[i])) {
57                         return true;
58                 }
59         }
60         return false;
61 }
62
63 /*
64   make sure we only add repsFrom entries for DCs who are masters for
65   the partition
66  */
67 static bool check_MasterNC(struct kccsrv_partition *p, struct repsFromToBlob *r,
68                            struct ldb_result *res)
69 {
70         struct repsFromTo1 *r1 = &r->ctr.ctr1;
71         struct GUID invocation_id = r1->source_dsa_invocation_id;
72         unsigned int i, j;
73         TALLOC_CTX *tmp_ctx;
74
75         /* we are expecting only version 1 */
76         SMB_ASSERT(r->version == 1);
77
78         tmp_ctx = talloc_new(p);
79         if (!tmp_ctx) {
80                 return false;
81         }
82
83         for (i=0; i<res->count; i++) {
84                 struct ldb_message *msg = res->msgs[i];
85                 struct ldb_message_element *el;
86                 struct ldb_dn *dn;
87
88                 struct GUID id2 = samdb_result_guid(msg, "invocationID");
89                 if (GUID_all_zero(&id2) ||
90                     !GUID_equal(&invocation_id, &id2)) {
91                         continue;
92                 }
93
94                 el = ldb_msg_find_element(msg, "msDS-hasMasterNCs");
95                 if (!el || el->num_values == 0) {
96                         el = ldb_msg_find_element(msg, "hasMasterNCs");
97                         if (!el || el->num_values == 0) {
98                                 continue;
99                         }
100                 }
101                 for (j=0; j<el->num_values; j++) {
102                         dn = ldb_dn_from_ldb_val(tmp_ctx, p->service->samdb, &el->values[j]);
103                         if (!ldb_dn_validate(dn)) {
104                                 talloc_free(dn);
105                                 continue;
106                         }
107                         if (ldb_dn_compare(dn, p->dn) == 0) {
108                                 DEBUG(5,("%s %s match on %s in %s\n",
109                                          r1->other_info->dns_name,
110                                          el->name,
111                                          ldb_dn_get_linearized(dn),
112                                          ldb_dn_get_linearized(msg->dn)));
113                                 talloc_free(tmp_ctx);
114                                 return true;
115                         }
116                         talloc_free(dn);
117                 }
118         }
119         talloc_free(tmp_ctx);
120         return false;
121 }
122
123 struct kccsrv_notify_drepl_server_state {
124         struct dreplsrv_refresh r;
125 };
126
127 static void kccsrv_notify_drepl_server_done(struct tevent_req *subreq);
128
129 /**
130  * Force dreplsrv to update its state as topology is changed
131  */
132 static void kccsrv_notify_drepl_server(struct kccsrv_service *s,
133                                        TALLOC_CTX *mem_ctx)
134 {
135         struct kccsrv_notify_drepl_server_state *state;
136         struct dcerpc_binding_handle *irpc_handle;
137         struct tevent_req *subreq;
138
139         state = talloc_zero(s, struct kccsrv_notify_drepl_server_state);
140         if (state == NULL) {
141                 return;
142         }
143
144         irpc_handle = irpc_binding_handle_by_name(state, s->task->msg_ctx,
145                                                   "dreplsrv", &ndr_table_irpc);
146         if (irpc_handle == NULL) {
147                 /* dreplsrv is not running yet */
148                 TALLOC_FREE(state);
149                 return;
150         }
151
152         subreq = dcerpc_dreplsrv_refresh_r_send(state, s->task->event_ctx,
153                                                 irpc_handle, &state->r);
154         if (subreq == NULL) {
155                 TALLOC_FREE(state);
156                 return;
157         }
158         tevent_req_set_callback(subreq, kccsrv_notify_drepl_server_done, state);
159 }
160
161 static void kccsrv_notify_drepl_server_done(struct tevent_req *subreq)
162 {
163         struct kccsrv_notify_drepl_server_state *state =
164                 tevent_req_callback_data(subreq,
165                 struct kccsrv_notify_drepl_server_state);
166         NTSTATUS status;
167
168         status = dcerpc_dreplsrv_refresh_r_recv(subreq, state);
169         TALLOC_FREE(subreq);
170
171         /* we don't care about errors */
172         TALLOC_FREE(state);
173 }
174
175 uint32_t kccsrv_replica_flags(struct kccsrv_service *s)
176 {
177         if (s->am_rodc) {
178                 return DRSUAPI_DRS_INIT_SYNC |
179                         DRSUAPI_DRS_PER_SYNC |
180                         DRSUAPI_DRS_ADD_REF |
181                         DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING |
182                         DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP |
183                         DRSUAPI_DRS_NONGC_RO_REP;
184         }
185         return DRSUAPI_DRS_INIT_SYNC |
186                 DRSUAPI_DRS_PER_SYNC |
187                 DRSUAPI_DRS_ADD_REF |
188                 DRSUAPI_DRS_WRIT_REP;
189 }
190
191 /*
192  * add any missing repsFrom structures to our partitions
193  */
194 NTSTATUS kccsrv_add_repsFrom(struct kccsrv_service *s, TALLOC_CTX *mem_ctx,
195                             struct repsFromToBlob *reps, uint32_t count,
196                             struct ldb_result *res)
197 {
198         struct kccsrv_partition *p;
199         bool notify_dreplsrv = false;
200         uint32_t replica_flags = kccsrv_replica_flags(s);
201
202         /* update the repsFrom on all partitions */
203         for (p=s->partitions; p; p=p->next) {
204                 struct repsFromToBlob *our_reps;
205                 uint32_t our_count;
206                 WERROR werr;
207                 uint32_t i, j;
208                 bool modified = false;
209
210                 werr = dsdb_loadreps(s->samdb, mem_ctx, p->dn, "repsFrom", &our_reps, &our_count);
211                 if (!W_ERROR_IS_OK(werr)) {
212                         DEBUG(0,(__location__ ": Failed to load repsFrom from %s - %s\n", 
213                                  ldb_dn_get_linearized(p->dn), ldb_errstring(s->samdb)));
214                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
215                 }
216
217                 /* see if the entry already exists */
218                 for (i=0; i<count; i++) {
219                         for (j=0; j<our_count; j++) {
220                                 if (kccsrv_same_source_dsa(&reps[i], &our_reps[j])) {
221                                         /* we already have this one -
222                                            check the replica_flags are right */
223                                         if (replica_flags != our_reps[j].ctr.ctr1.replica_flags) {
224                                                 /* we need to update the old one with
225                                                  * the new flags
226                                                  */
227                                                 our_reps[j].ctr.ctr1.replica_flags = replica_flags;
228                                                 modified = true;
229                                         }
230                                         break;
231                                 }
232                         }
233                         if (j == our_count) {
234                                 /* we don't have the new one - add it
235                                  * if it is a master
236                                  */
237                                 if (res && !check_MasterNC(p, &reps[i], res)) {
238                                         /* its not a master, we don't
239                                            want to pull from it */
240                                         continue;
241                                 }
242                                 /* we need to add it to our repsFrom */
243                                 our_reps = talloc_realloc(mem_ctx, our_reps, struct repsFromToBlob, our_count+1);
244                                 NT_STATUS_HAVE_NO_MEMORY(our_reps);
245                                 our_reps[our_count] = reps[i];
246                                 our_reps[our_count].ctr.ctr1.replica_flags = replica_flags;
247                                 our_count++;
248                                 modified = true;
249                                 DEBUG(4,(__location__ ": Added repsFrom for %s\n",
250                                          reps[i].ctr.ctr1.other_info->dns_name));
251                         }
252                 }
253
254                 /* remove any stale ones */
255                 for (i=0; i<our_count; i++) {
256                         if (!reps_in_list(&our_reps[i], reps, count) ||
257                             (res && !check_MasterNC(p, &our_reps[i], res))) {
258                                 DEBUG(4,(__location__ ": Removed repsFrom for %s\n",
259                                          our_reps[i].ctr.ctr1.other_info->dns_name));
260                                 memmove(&our_reps[i], &our_reps[i+1], (our_count-(i+1))*sizeof(our_reps[0]));
261                                 our_count--;
262                                 i--;
263                                 modified = true;
264                         }
265                 }
266
267                 if (modified) {
268                         werr = dsdb_savereps(s->samdb, mem_ctx, p->dn, "repsFrom", our_reps, our_count);
269                         if (!W_ERROR_IS_OK(werr)) {
270                                 DEBUG(0,(__location__ ": Failed to save repsFrom to %s - %s\n", 
271                                          ldb_dn_get_linearized(p->dn), ldb_errstring(s->samdb)));
272                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
273                         }
274                         /* dreplsrv should refresh its state */
275                         notify_dreplsrv = true;
276                 }
277
278                 /* remove stale repsTo entries */
279                 modified = false;
280                 werr = dsdb_loadreps(s->samdb, mem_ctx, p->dn, "repsTo", &our_reps, &our_count);
281                 if (!W_ERROR_IS_OK(werr)) {
282                         DEBUG(0,(__location__ ": Failed to load repsTo from %s - %s\n", 
283                                  ldb_dn_get_linearized(p->dn), ldb_errstring(s->samdb)));
284                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
285                 }
286
287                 /* remove any stale ones */
288                 for (i=0; i<our_count; i++) {
289                         if (!reps_in_list(&our_reps[i], reps, count)) {
290                                 DEBUG(4,(__location__ ": Removed repsTo for %s\n",
291                                          our_reps[i].ctr.ctr1.other_info->dns_name));
292                                 memmove(&our_reps[i], &our_reps[i+1], (our_count-(i+1))*sizeof(our_reps[0]));
293                                 our_count--;
294                                 i--;
295                                 modified = true;
296                         }
297                 }
298
299                 if (modified) {
300                         werr = dsdb_savereps(s->samdb, mem_ctx, p->dn, "repsTo", our_reps, our_count);
301                         if (!W_ERROR_IS_OK(werr)) {
302                                 DEBUG(0,(__location__ ": Failed to save repsTo to %s - %s\n", 
303                                          ldb_dn_get_linearized(p->dn), ldb_errstring(s->samdb)));
304                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
305                         }
306                         /* dreplsrv should refresh its state */
307                         notify_dreplsrv = true;
308                 }
309         }
310
311         /* notify dreplsrv toplogy has changed */
312         if (notify_dreplsrv) {
313                 kccsrv_notify_drepl_server(s, mem_ctx);
314         }
315
316         return NT_STATUS_OK;
317
318 }
319
320
321 /*
322   form a unique list of DNs from a search result and a given set of attributes
323  */
324 static int kccsrv_dn_list(struct ldb_context *ldb, struct ldb_result *res,
325                           TALLOC_CTX *mem_ctx,
326                           const char **attrs,
327                           struct ldb_dn ***dn_list, int *dn_count)
328 {
329         int i;
330         struct ldb_dn **nc_list = NULL;
331         int nc_count = 0;
332
333         nc_list = talloc_array(mem_ctx, struct ldb_dn *, 0);
334         if (nc_list == NULL) {
335                 return LDB_ERR_OPERATIONS_ERROR;
336         }
337
338         /* gather up a list of all NCs in this forest */
339         for (i=0; i<res->count; i++) {
340                 struct ldb_message *msg = res->msgs[i];
341                 int j;
342                 for (j=0; attrs[j]; j++) {
343                         struct ldb_message_element *el;
344                         int k;
345
346                         el = ldb_msg_find_element(msg, attrs[j]);
347                         if (el == NULL) continue;
348                         for (k=0; k<el->num_values; k++) {
349                                 struct ldb_dn *dn;
350                                 dn = ldb_dn_from_ldb_val(nc_list, ldb, &el->values[k]);
351                                 if (dn != NULL) {
352                                         int l;
353                                         for (l=0; l<nc_count; l++) {
354                                                 if (ldb_dn_compare(nc_list[l], dn) == 0) break;
355                                         }
356                                         if (l < nc_count) continue;
357                                         nc_list = talloc_realloc(mem_ctx, nc_list, struct ldb_dn *, nc_count+1);
358                                         if (nc_list == NULL) {
359                                                 return LDB_ERR_OPERATIONS_ERROR;
360                                         }
361                                         nc_list[nc_count] = dn;
362                                         nc_count++;
363                                 }
364                         }
365                 }
366         }
367
368         (*dn_list) = nc_list;
369         (*dn_count) = nc_count;
370         return LDB_SUCCESS;
371 }
372
373
374 /*
375   look for any additional global catalog partitions that we should be
376   replicating (by looking for msDS-HasDomainNCs), and add them to our
377   hasPartialReplicaNCs NTDS attribute
378  */
379 static int kccsrv_gc_update(struct kccsrv_service *s, struct ldb_result *res)
380 {
381         int i;
382         struct ldb_dn **nc_list = NULL;
383         int nc_count = 0;
384         struct ldb_dn **our_nc_list = NULL;
385         int our_nc_count = 0;
386         const char *attrs1[] = { "msDS-hasMasterNCs", "hasMasterNCs", "msDS-HasDomainNCs", NULL };
387         const char *attrs2[] = { "msDS-hasMasterNCs", "hasMasterNCs", "msDS-HasDomainNCs", "hasPartialReplicaNCs", NULL };
388         int ret;
389         TALLOC_CTX *tmp_ctx = talloc_new(res);
390         struct ldb_result *res2;
391         struct ldb_message *msg;
392
393         /* get a complete list of NCs for the forest */
394         ret = kccsrv_dn_list(s->samdb, res, tmp_ctx, attrs1, &nc_list, &nc_count);
395         if (ret != LDB_SUCCESS) {
396                 DEBUG(1,("Failed to get NC list for GC update - %s\n", ldb_errstring(s->samdb)));
397                 talloc_free(tmp_ctx);
398                 return ret;
399         }
400
401         /* get a list of what NCs we are already replicating */
402         ret = dsdb_search_dn(s->samdb, tmp_ctx, &res2, samdb_ntds_settings_dn(s->samdb, tmp_ctx), attrs2, 0);
403         if (ret != LDB_SUCCESS) {
404                 DEBUG(1,("Failed to get our NC list attributes for GC update - %s\n", ldb_errstring(s->samdb)));
405                 talloc_free(tmp_ctx);
406                 return ret;
407         }
408
409         ret = kccsrv_dn_list(s->samdb, res2, tmp_ctx, attrs2, &our_nc_list, &our_nc_count);
410         if (ret != LDB_SUCCESS) {
411                 DEBUG(1,("Failed to get our NC list for GC update - %s\n", ldb_errstring(s->samdb)));
412                 talloc_free(tmp_ctx);
413                 return ret;
414         }
415
416         msg = ldb_msg_new(tmp_ctx);
417         if (msg == NULL) {
418                 talloc_free(tmp_ctx);
419                 return LDB_ERR_OPERATIONS_ERROR;
420         }
421         msg->dn = res2->msgs[0]->dn;
422
423         /* see if we are missing any */
424         for (i=0; i<nc_count; i++) {
425                 int j;
426                 for (j=0; j<our_nc_count; j++) {
427                         if (ldb_dn_compare(nc_list[i], our_nc_list[j]) == 0) break;
428                 }
429                 if (j == our_nc_count) {
430                         /* its a new one */
431                         ret = ldb_msg_add_string(msg, "hasPartialReplicaNCs",
432                                                  ldb_dn_get_extended_linearized(msg, nc_list[i], 1));
433                         if (ret != LDB_SUCCESS) {
434                                 talloc_free(tmp_ctx);
435                                 return ret;
436                         }
437
438                 }
439         }
440
441         if (msg->num_elements == 0) {
442                 /* none to add */
443                 talloc_free(tmp_ctx);
444                 return LDB_SUCCESS;
445         }
446
447         if (s->am_rodc) {
448                 DEBUG(5, ("%d partial replica should be added but we are RODC so we skip\n", msg->num_elements));
449                 talloc_free(tmp_ctx);
450                 return LDB_SUCCESS;
451         }
452
453         msg->elements[0].flags = LDB_FLAG_MOD_ADD;
454
455         ret = dsdb_modify(s->samdb, msg, 0);
456         if (ret != LDB_SUCCESS) {
457                 DEBUG(0,("Failed to add hasPartialReplicaNCs - %s\n",
458                          ldb_errstring(s->samdb)));
459         }
460
461         talloc_free(tmp_ctx);
462         return ret;
463 }
464
465
466 /*
467   this is the core of our initial simple KCC
468   We just add a repsFrom entry for all DCs we find that have nTDSDSA
469   objects, except for ourselves
470  */
471 NTSTATUS kccsrv_simple_update(struct kccsrv_service *s, TALLOC_CTX *mem_ctx)
472 {
473         struct ldb_result *res;
474         unsigned int i;
475         int ret;
476         const char *attrs[] = { "objectGUID", "invocationID", "msDS-hasMasterNCs", "hasMasterNCs", "msDS-HasDomainNCs", NULL };
477         struct repsFromToBlob *reps = NULL;
478         uint32_t count = 0;
479         struct kcc_connection_list *ntds_conn, *dsa_conn;
480
481         ret = dsdb_search(s->samdb, mem_ctx, &res, s->config_dn, LDB_SCOPE_SUBTREE,
482                           attrs, DSDB_SEARCH_SHOW_EXTENDED_DN, "objectClass=nTDSDSA");
483         if (ret != LDB_SUCCESS) {
484                 DEBUG(0,(__location__ ": Failed nTDSDSA search - %s\n", ldb_errstring(s->samdb)));
485                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
486         }
487
488         if (samdb_is_gc(s->samdb)) {
489                 kccsrv_gc_update(s, res);
490         }
491
492         /* get the current list of connections */
493         ntds_conn = kccsrv_find_connections(s, mem_ctx);
494
495         dsa_conn = talloc_zero(mem_ctx, struct kcc_connection_list);
496
497         for (i=0; i<res->count; i++) {
498                 struct repsFromTo1 *r1;
499                 struct GUID ntds_guid, invocation_id;
500
501                 ntds_guid = samdb_result_guid(res->msgs[i], "objectGUID");
502                 if (GUID_compare(&ntds_guid, &s->ntds_guid) == 0) {
503                         /* don't replicate with ourselves */
504                         continue;
505                 }
506
507                 invocation_id = samdb_result_guid(res->msgs[i], "invocationID");
508
509                 reps = talloc_realloc(mem_ctx, reps, struct repsFromToBlob, count+1);
510                 NT_STATUS_HAVE_NO_MEMORY(reps);
511
512                 ZERO_STRUCT(reps[count]);
513                 reps[count].version = 1;
514                 r1 = &reps[count].ctr.ctr1;
515
516                 r1->other_info               = talloc_zero(reps, struct repsFromTo1OtherInfo);
517                 r1->other_info->dns_name     = samdb_ntds_msdcs_dns_name(s->samdb, reps, &ntds_guid);
518                 r1->source_dsa_obj_guid      = ntds_guid;
519                 r1->source_dsa_invocation_id = invocation_id;
520                 r1->replica_flags = kccsrv_replica_flags(s);
521                 memset(r1->schedule, 0x11, sizeof(r1->schedule));
522
523                 dsa_conn->servers = talloc_realloc(dsa_conn, dsa_conn->servers,
524                                                   struct kcc_connection,
525                                                   dsa_conn->count + 1);
526                 NT_STATUS_HAVE_NO_MEMORY(dsa_conn->servers);
527                 dsa_conn->servers[dsa_conn->count].dsa_guid = r1->source_dsa_obj_guid;
528                 dsa_conn->count++;
529
530                 count++;
531         }
532
533         kccsrv_apply_connections(s, ntds_conn, dsa_conn);
534
535         return kccsrv_add_repsFrom(s, mem_ctx, reps, count, res);
536 }
537
538
539 static void kccsrv_periodic_run(struct kccsrv_service *service);
540
541 static void kccsrv_periodic_handler_te(struct tevent_context *ev, struct tevent_timer *te,
542                                          struct timeval t, void *ptr)
543 {
544         struct kccsrv_service *service = talloc_get_type(ptr, struct kccsrv_service);
545         WERROR status;
546
547         service->periodic.te = NULL;
548
549         kccsrv_periodic_run(service);
550
551         status = kccsrv_periodic_schedule(service, service->periodic.interval);
552         if (!W_ERROR_IS_OK(status)) {
553                 task_server_terminate(service->task, win_errstr(status), true);
554                 return;
555         }
556 }
557
558 WERROR kccsrv_periodic_schedule(struct kccsrv_service *service, uint32_t next_interval)
559 {
560         TALLOC_CTX *tmp_mem;
561         struct tevent_timer *new_te;
562         struct timeval next_time;
563
564         /* prevent looping */
565         if (next_interval == 0) next_interval = 1;
566
567         next_time = timeval_current_ofs(next_interval, 50);
568
569         if (service->periodic.te) {
570                 /*
571                  * if the timestamp of the new event is higher,
572                  * as current next we don't need to reschedule
573                  */
574                 if (timeval_compare(&next_time, &service->periodic.next_event) > 0) {
575                         return WERR_OK;
576                 }
577         }
578
579         /* reset the next scheduled timestamp */
580         service->periodic.next_event = next_time;
581
582         new_te = tevent_add_timer(service->task->event_ctx, service,
583                                  service->periodic.next_event,
584                                  kccsrv_periodic_handler_te, service);
585         W_ERROR_HAVE_NO_MEMORY(new_te);
586
587         tmp_mem = talloc_new(service);
588         DEBUG(4,("kccsrv_periodic_schedule(%u) %sscheduled for: %s\n",
589                 next_interval,
590                 (service->periodic.te?"re":""),
591                 nt_time_string(tmp_mem, timeval_to_nttime(&next_time))));
592         talloc_free(tmp_mem);
593
594         talloc_free(service->periodic.te);
595         service->periodic.te = new_te;
596
597         return WERR_OK;
598 }
599
600 static void kccsrv_periodic_run(struct kccsrv_service *service)
601 {
602         TALLOC_CTX *mem_ctx;
603         NTSTATUS status;
604
605         DEBUG(4,("kccsrv_periodic_run(): update\n"));
606
607         mem_ctx = talloc_new(service);
608
609         if (service->samba_kcc_code)
610                 status = kccsrv_samba_kcc(service, mem_ctx);
611         else {
612                 status = kccsrv_simple_update(service, mem_ctx);
613                 if (!NT_STATUS_IS_OK(status))
614                         DEBUG(0,("kccsrv_simple_update failed - %s\n",
615                                 nt_errstr(status)));
616         }
617
618         status = kccsrv_check_deleted(service, mem_ctx);
619         if (!NT_STATUS_IS_OK(status)) {
620                 DEBUG(0,("kccsrv_check_deleted failed - %s\n", nt_errstr(status)));
621         }
622         talloc_free(mem_ctx);
623 }
624
625 /* Called when samba_kcc script has finished
626  */
627 static void samba_kcc_done(struct tevent_req *subreq)
628 {
629         struct kccsrv_service *service =
630                 tevent_req_callback_data(subreq, struct kccsrv_service);
631         int rc;
632         int sys_errno;
633
634         service->periodic.subreq = NULL;
635
636         rc = samba_runcmd_recv(subreq, &sys_errno);
637         TALLOC_FREE(subreq);
638
639         if (rc != 0)
640                 service->periodic.status =
641                         map_nt_error_from_unix_common(sys_errno);
642         else
643                 service->periodic.status = NT_STATUS_OK;
644
645         if (!NT_STATUS_IS_OK(service->periodic.status))
646                 DEBUG(0,(__location__ ": Failed samba_kcc - %s\n",
647                         nt_errstr(service->periodic.status)));
648         else
649                 DEBUG(3,("Completed samba_kcc OK\n"));
650 }
651
652 /* Invocation of the samba_kcc python script for replication
653  * topology generation.
654  */
655 NTSTATUS kccsrv_samba_kcc(struct kccsrv_service *service,
656                         TALLOC_CTX *ctxp)
657 {
658         NTSTATUS status = NT_STATUS_OK;
659         const char * const *samba_kcc_command =
660                 lpcfg_samba_kcc_command(service->task->lp_ctx);
661
662         /* kill any existing child */
663         TALLOC_FREE(service->periodic.subreq);
664
665         DEBUG(0,("Calling samba_kcc script\n"));
666         service->periodic.subreq = samba_runcmd_send(service,
667                                         service->task->event_ctx,
668                                         timeval_current_ofs(40, 0),
669                                         2, 0, samba_kcc_command, NULL);
670
671         if (service->periodic.subreq == NULL) {
672                 status = NT_STATUS_NO_MEMORY;
673                 goto xerror;
674         }
675         tevent_req_set_callback(service->periodic.subreq,
676                                 samba_kcc_done, service);
677
678 xerror:
679         if (!NT_STATUS_IS_OK(status))
680                 DEBUG(0,(__location__ ": failed - %s\n", nt_errstr(status)));
681         return status;
682 }