X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source%2Fwrepl_server%2Fwrepl_server.c;h=f4b48ae916edab1024e806df4cdb342fa8a457aa;hb=3468952e771ab31f90b6c374ade01c5550810f42;hp=866090f40051825a4ee1f871f52cee193ec394ae;hpb=c2b2b582203d9fe2596ebc9cf2600d52e6f61831;p=jelmer%2Fsamba4-debian.git diff --git a/source/wrepl_server/wrepl_server.c b/source/wrepl_server/wrepl_server.c index 866090f40..f4b48ae91 100644 --- a/source/wrepl_server/wrepl_server.c +++ b/source/wrepl_server/wrepl_server.c @@ -7,7 +7,7 @@ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -16,32 +16,95 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program. If not, see . */ #include "includes.h" -#include "dlinklist.h" -#include "lib/events/events.h" -#include "lib/socket/socket.h" +#include "lib/util/dlinklist.h" #include "smbd/service_task.h" -#include "smbd/service_stream.h" +#include "smbd/service.h" #include "lib/messaging/irpc.h" -#include "librpc/gen_ndr/ndr_winsrepl.h" +#include "librpc/gen_ndr/winsrepl.h" #include "wrepl_server/wrepl_server.h" #include "nbt_server/wins/winsdb.h" #include "ldb/include/ldb.h" +#include "ldb/include/ldb_errors.h" +#include "auth/auth.h" +#include "db_wrap.h" +#include "param/param.h" + +static struct ldb_context *wins_config_db_connect(TALLOC_CTX *mem_ctx) +{ + return ldb_wrap_connect(mem_ctx, global_loadparm, private_path(mem_ctx, + global_loadparm, + lp_wins_config_url(global_loadparm)), + system_session(mem_ctx), NULL, 0, NULL); +} + +static uint64_t wins_config_db_get_seqnumber(struct ldb_context *ldb) +{ + int ret; + struct ldb_dn *dn; + struct ldb_result *res = NULL; + TALLOC_CTX *tmp_ctx = talloc_new(ldb); + uint64_t seqnumber = 0; + + dn = ldb_dn_new(tmp_ctx, ldb, "@BASEINFO"); + if (!dn) goto failed; + + /* find the record in the WINS database */ + ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, + NULL, NULL, &res); + if (ret != LDB_SUCCESS) goto failed; + talloc_steal(tmp_ctx, res); + if (res->count > 1) goto failed; + + if (res->count == 1) { + seqnumber = ldb_msg_find_attr_as_uint64(res->msgs[0], "sequenceNumber", 0); + } + +failed: + talloc_free(tmp_ctx); + return seqnumber; +} /* open winsdb */ static NTSTATUS wreplsrv_open_winsdb(struct wreplsrv_service *service) { - service->wins_db = winsdb_connect(service); + service->wins_db = winsdb_connect(service, WINSDB_HANDLE_CALLER_WREPL); if (!service->wins_db) { return NT_STATUS_INTERNAL_DB_ERROR; } + service->config.ldb = wins_config_db_connect(service); + if (!service->config.ldb) { + return NT_STATUS_INTERNAL_DB_ERROR; + } + + /* the default renew interval is 6 days */ + service->config.renew_interval = lp_parm_int(global_loadparm, NULL,"wreplsrv","renew_interval", 6*24*60*60); + + /* the default tombstone (extinction) interval is 6 days */ + service->config.tombstone_interval= lp_parm_int(global_loadparm, NULL,"wreplsrv","tombstone_interval", 6*24*60*60); + + /* the default tombstone (extinction) timeout is 1 day */ + service->config.tombstone_timeout = lp_parm_int(global_loadparm, NULL,"wreplsrv","tombstone_timeout", 1*24*60*60); + + /* the default tombstone extra timeout is 3 days */ + service->config.tombstone_extra_timeout = lp_parm_int(global_loadparm, NULL,"wreplsrv","tombstone_extra_timeout", 3*24*60*60); + + /* the default verify interval is 24 days */ + service->config.verify_interval = lp_parm_int(global_loadparm, NULL,"wreplsrv","verify_interval", 24*24*60*60); + + /* the default scavenging interval is 'renew_interval/2' */ + service->config.scavenging_interval=lp_parm_int(global_loadparm, NULL,"wreplsrv","scavenging_interval", + service->config.renew_interval/2); + + /* the maximun interval to the next periodic processing event */ + service->config.periodic_interval = lp_parm_int(global_loadparm, NULL,"wreplsrv","periodic_interval", 15); + return NT_STATUS_OK; } @@ -61,42 +124,89 @@ struct wreplsrv_partner *wreplsrv_find_partner(struct wreplsrv_service *service, /* load our replication partners */ -static NTSTATUS wreplsrv_load_partners(struct wreplsrv_service *service) +NTSTATUS wreplsrv_load_partners(struct wreplsrv_service *service) { - struct ldb_message **res = NULL; + struct wreplsrv_partner *partner; + struct ldb_result *res = NULL; int ret; - TALLOC_CTX *tmp_ctx = talloc_new(service); + TALLOC_CTX *tmp_ctx; int i; + uint64_t new_seqnumber; + + new_seqnumber = wins_config_db_get_seqnumber(service->config.ldb); + + /* if it's not the first run and nothing changed we're done */ + if (service->config.seqnumber != 0 && service->config.seqnumber == new_seqnumber) { + return NT_STATUS_OK; + } + + tmp_ctx = talloc_new(service); + NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); + + service->config.seqnumber = new_seqnumber; /* find the record in the WINS database */ - ret = ldb_search(service->wins_db, ldb_dn_explode(tmp_ctx, "CN=PARTNERS"), LDB_SCOPE_ONELEVEL, + ret = ldb_search(service->config.ldb, ldb_dn_new(tmp_ctx, service->config.ldb, "CN=PARTNERS"), LDB_SCOPE_SUBTREE, "(objectClass=wreplPartner)", NULL, &res); - if (res != NULL) { - talloc_steal(tmp_ctx, res); + if (ret != LDB_SUCCESS) goto failed; + talloc_steal(tmp_ctx, res); + + /* first disable all existing partners */ + for (partner=service->partners; partner; partner = partner->next) { + partner->type = WINSREPL_PARTNER_NONE; } - if (ret < 0) goto failed; - if (ret == 0) goto done; - for (i=0; i < ret; i++) { - struct wreplsrv_partner *partner; + for (i=0; i < res->count; i++) { + const char *address; - partner = talloc(service, struct wreplsrv_partner); - if (partner == NULL) goto failed; + address = ldb_msg_find_attr_as_string(res->msgs[i], "address", NULL); + if (!address) { + goto failed; + } - partner->address = ldb_msg_find_string(res[i], "address", NULL); - if (!partner->address) goto failed; - partner->name = ldb_msg_find_string(res[i], "name", partner->address); - partner->type = ldb_msg_find_int(res[i], "type", WINSREPL_PARTNER_BOTH); - partner->pull.interval = ldb_msg_find_int(res[i], "pullInterval", WINSREPL_DEFAULT_PULL_INTERVAL); - partner->our_address = ldb_msg_find_string(res[i], "ourAddress", NULL); + partner = wreplsrv_find_partner(service, address); + if (partner) { + if (partner->name != partner->address) { + talloc_free(discard_const(partner->name)); + } + partner->name = NULL; + talloc_free(discard_const(partner->our_address)); + partner->our_address = NULL; + + /* force rescheduling of pulling */ + partner->pull.next_run = timeval_zero(); + } else { + partner = talloc_zero(service, struct wreplsrv_partner); + if (partner == NULL) goto failed; + + partner->service = service; + partner->address = address; + talloc_steal(partner, partner->address); + + DLIST_ADD_END(service->partners, partner, struct wreplsrv_partner *); + } - talloc_steal(partner, partner->address); + partner->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", partner->address); talloc_steal(partner, partner->name); + partner->our_address = ldb_msg_find_attr_as_string(res->msgs[i], "ourAddress", NULL); talloc_steal(partner, partner->our_address); - DLIST_ADD(service->partners, partner); + partner->type = ldb_msg_find_attr_as_uint(res->msgs[i], "type", WINSREPL_PARTNER_BOTH); + partner->pull.interval = ldb_msg_find_attr_as_uint(res->msgs[i], "pullInterval", + WINSREPL_DEFAULT_PULL_INTERVAL); + partner->pull.retry_interval = ldb_msg_find_attr_as_uint(res->msgs[i], "pullRetryInterval", + WINSREPL_DEFAULT_PULL_RETRY_INTERVAL); + partner->push.change_count = ldb_msg_find_attr_as_uint(res->msgs[i], "pushChangeCount", + WINSREPL_DEFAULT_PUSH_CHANGE_COUNT); + partner->push.use_inform = ldb_msg_find_attr_as_uint(res->msgs[i], "pushUseInform", False); + + DEBUG(3,("wreplsrv_load_partners: found partner: %s type: 0x%X\n", + partner->address, partner->type)); } -done: + + DEBUG(2,("wreplsrv_load_partners: %u partners found: wins_config_db seqnumber %llu\n", + res->count, (unsigned long long)service->config.seqnumber)); + talloc_free(tmp_ctx); return NT_STATUS_OK; failed: @@ -104,42 +214,74 @@ failed: return NT_STATUS_FOOBAR; } -uint64_t wreplsrv_local_max_version(struct wreplsrv_service *service) +NTSTATUS wreplsrv_fill_wrepl_table(struct wreplsrv_service *service, + TALLOC_CTX *mem_ctx, + struct wrepl_table *table_out, + const char *initiator, + BOOL full_table) { - int ret; - struct ldb_context *ldb = service->wins_db; - struct ldb_dn *dn; - struct ldb_message **res = NULL; - TALLOC_CTX *tmp_ctx = talloc_new(service); - uint64_t maxVersion = 0; + struct wreplsrv_owner *cur; + uint32_t i = 0; - dn = ldb_dn_explode(tmp_ctx, "CN=VERSION"); - if (!dn) goto failed; + table_out->partner_count = 0; + table_out->partners = NULL; + table_out->initiator = initiator; - /* find the record in the WINS database */ - ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, - NULL, NULL, &res); - if (res != NULL) { - talloc_steal(tmp_ctx, res); + for (cur = service->table; cur; cur = cur->next) { + if (full_table) { + table_out->partner_count++; + continue; + } + + if (strcmp(initiator, cur->owner.address) != 0) continue; + + table_out->partner_count++; + break; } - if (ret < 0) goto failed; - if (ret > 1) goto failed; - if (ret == 1) { - maxVersion = ldb_msg_find_uint64(res[0], "maxVersion", 0); + table_out->partners = talloc_array(mem_ctx, struct wrepl_wins_owner, table_out->partner_count); + NT_STATUS_HAVE_NO_MEMORY(table_out->partners); + + for (cur = service->table; cur && i < table_out->partner_count; cur = cur->next) { + /* + * if it's our local entry + * update the max version + */ + if (cur == service->owner) { + cur->owner.max_version = winsdb_get_maxVersion(service->wins_db); + } + + if (full_table) { + table_out->partners[i] = cur->owner; + i++; + continue; + } + + if (strcmp(initiator, cur->owner.address) != 0) continue; + + table_out->partners[i] = cur->owner; + i++; + break; } -failed: - talloc_free(tmp_ctx); - return maxVersion; + return NT_STATUS_OK; } -struct wreplsrv_owner *wreplsrv_find_owner(struct wreplsrv_owner *table, const char *wins_owner) +struct wreplsrv_owner *wreplsrv_find_owner(struct wreplsrv_service *service, + struct wreplsrv_owner *table, + const char *wins_owner) { struct wreplsrv_owner *cur; for (cur = table; cur; cur = cur->next) { if (strcmp(cur->owner.address, wins_owner) == 0) { + /* + * if it's our local entry + * update the max version + */ + if (cur == service->owner) { + cur->owner.max_version = winsdb_get_maxVersion(service->wins_db); + } return cur; } } @@ -151,18 +293,18 @@ struct wreplsrv_owner *wreplsrv_find_owner(struct wreplsrv_owner *table, const c update the wins_owner_table max_version, if the given version is the highest version if no entry for the wins_owner exists yet, create one */ -static NTSTATUS wreplsrv_add_table(struct wreplsrv_service *service, - TALLOC_CTX *mem_ctx, struct wreplsrv_owner **_table, - const char *wins_owner, uint64_t version) +NTSTATUS wreplsrv_add_table(struct wreplsrv_service *service, + TALLOC_CTX *mem_ctx, struct wreplsrv_owner **_table, + const char *wins_owner, uint64_t version) { struct wreplsrv_owner *table = *_table; struct wreplsrv_owner *cur; - if (strcmp(WINSDB_OWNER_LOCAL, wins_owner) == 0) { - return NT_STATUS_OK; + if (!wins_owner || strcmp(wins_owner, "0.0.0.0") == 0) { + wins_owner = service->wins_db->local_owner; } - cur = wreplsrv_find_owner(table, wins_owner); + cur = wreplsrv_find_owner(service, table, wins_owner); /* if it doesn't exists yet, create one */ if (!cur) { @@ -177,15 +319,26 @@ static NTSTATUS wreplsrv_add_table(struct wreplsrv_service *service, cur->partner = wreplsrv_find_partner(service, wins_owner); - DLIST_ADD(table, cur); + DLIST_ADD_END(table, cur, struct wreplsrv_owner *); *_table = table; } /* the min_version is always 0 here, and won't be updated */ - /* if the given version is higher the then current nax_version, update */ + /* if the given version is higher than the current max_version, update */ if (cur->owner.max_version < version) { cur->owner.max_version = version; + /* if it's for our local db, we need to update the wins.ldb too */ + if (cur == service->owner) { + uint64_t ret; + ret = winsdb_set_maxVersion(service->wins_db, cur->owner.max_version); + if (ret != cur->owner.max_version) { + DEBUG(0,("winsdb_set_maxVersion(%llu) failed: %llu\n", + (unsigned long long)cur->owner.max_version, + (unsigned long long)ret)); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + } } return NT_STATUS_OK; @@ -196,44 +349,69 @@ static NTSTATUS wreplsrv_add_table(struct wreplsrv_service *service, */ static NTSTATUS wreplsrv_load_table(struct wreplsrv_service *service) { - struct ldb_message **res = NULL; + struct ldb_result *res = NULL; int ret; NTSTATUS status; TALLOC_CTX *tmp_ctx = talloc_new(service); + struct ldb_context *ldb = service->wins_db->ldb; int i; + struct wreplsrv_owner *local_owner; const char *wins_owner; uint64_t version; const char * const attrs[] = { "winsOwner", - "version", + "versionID", NULL }; - /* find the record in the WINS database */ - ret = ldb_search(service->wins_db, NULL, LDB_SCOPE_SUBTREE, - "(objectClass=wins)", attrs, &res); - if (res != NULL) { - talloc_steal(tmp_ctx, res); + /* + * make sure we have our local entry in the list, + * but we set service->owner when we're done + * to avoid to many calls to wreplsrv_local_max_version() + */ + status = wreplsrv_add_table(service, + service, &service->table, + service->wins_db->local_owner, 0); + if (!NT_STATUS_IS_OK(status)) goto failed; + local_owner = wreplsrv_find_owner(service, service->table, service->wins_db->local_owner); + if (!local_owner) { + status = NT_STATUS_INTERNAL_ERROR; + goto failed; } - status = NT_STATUS_INTERNAL_DB_CORRUPTION; - if (ret < 0) goto failed; - if (ret == 0) goto done; - - for (i=0; i < ret; i++) { - wins_owner = ldb_msg_find_string(res[i], "winsOwner", NULL); - version = ldb_msg_find_uint64(res[i], "version", 0); - - if (wins_owner) { - status = wreplsrv_add_table(service, - service, &service->table, - wins_owner, version); - if (!NT_STATUS_IS_OK(status)) goto failed; - } - talloc_free(res[i]); - /* TODO: what's abut the per address owners? */ + /* find the record in the WINS database */ + ret = ldb_search(ldb, NULL, LDB_SCOPE_SUBTREE, + "(objectClass=winsRecord)", attrs, &res); + status = NT_STATUS_INTERNAL_DB_CORRUPTION; + if (ret != LDB_SUCCESS) goto failed; + talloc_steal(tmp_ctx, res); + + for (i=0; i < res->count; i++) { + wins_owner = ldb_msg_find_attr_as_string(res->msgs[i], "winsOwner", NULL); + version = ldb_msg_find_attr_as_uint64(res->msgs[i], "versionID", 0); + + status = wreplsrv_add_table(service, + service, &service->table, + wins_owner, version); + if (!NT_STATUS_IS_OK(status)) goto failed; + talloc_free(res->msgs[i]); } -done: + + /* + * this makes sure we call wreplsrv_local_max_version() before returning in + * wreplsrv_find_owner() + */ + service->owner = local_owner; + + /* + * this makes sure the maxVersion in the database is updated, + * with the highest version we found, if this is higher than the current stored one + */ + status = wreplsrv_add_table(service, + service, &service->table, + service->wins_db->local_owner, local_owner->owner.max_version); + if (!NT_STATUS_IS_OK(status)) goto failed; + talloc_free(tmp_ctx); return NT_STATUS_OK; failed: @@ -265,13 +443,16 @@ static void wreplsrv_task_init(struct task_server *task) NTSTATUS status; struct wreplsrv_service *service; + task_server_set_title(task, "task[wreplsrv]"); + service = talloc_zero(task, struct wreplsrv_service); if (!service) { task_server_terminate(task, "wreplsrv_task_init: out of memory"); return; } - service->task = task; - task->private = service; + service->task = task; + service->startup_time = timeval_current(); + task->private = service; /* * setup up all partners, and open the winsdb @@ -301,6 +482,12 @@ static void wreplsrv_task_init(struct task_server *task) return; } + status = wreplsrv_setup_periodic(service); + if (!NT_STATUS_IS_OK(status)) { + task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_periodic() failed"); + return; + } + irpc_add_name(task->msg_ctx, "wrepl_server"); } @@ -309,6 +496,10 @@ static void wreplsrv_task_init(struct task_server *task) */ static NTSTATUS wreplsrv_init(struct event_context *event_ctx, const struct model_ops *model_ops) { + if (!lp_wins_support(global_loadparm)) { + return NT_STATUS_OK; + } + return task_server_startup(event_ctx, model_ops, wreplsrv_task_init); }