X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=client%2Fctdb_client.c;h=89eeb4836a14bca81a422765b558854f1fb777e0;hb=0fedef0ffba4178126eee9544c5e2db52f5db893;hp=9e5f7fc8ba0b3928520aa6f58b59fe75e2f8d25f;hpb=787274374411116266ee66455a67c127b8d558c1;p=sahlberg%2Fctdb.git diff --git a/client/ctdb_client.c b/client/ctdb_client.c index 9e5f7fc8..89eeb483 100644 --- a/client/ctdb_client.c +++ b/client/ctdb_client.c @@ -22,7 +22,7 @@ #include "db_wrap.h" #include "lib/tdb/include/tdb.h" #include "lib/util/dlinklist.h" -#include "lib/events/events.h" +#include "lib/tevent/tevent.h" #include "system/network.h" #include "system/filesys.h" #include "system/locale.h" @@ -72,7 +72,7 @@ struct ctdb_req_header *_ctdbd_allocate_pkt(struct ctdb_context *ctdb, */ int ctdb_call_local(struct ctdb_db_context *ctdb_db, struct ctdb_call *call, struct ctdb_ltdb_header *header, TALLOC_CTX *mem_ctx, - TDB_DATA *data, uint32_t caller) + TDB_DATA *data, bool updatetdb) { struct ctdb_call_info *c; struct ctdb_registered_call *fn; @@ -89,6 +89,7 @@ int ctdb_call_local(struct ctdb_db_context *ctdb_db, struct ctdb_call *call, c->new_data = NULL; c->reply_data = NULL; c->status = 0; + c->header = header; for (fn=ctdb_db->calls;fn;fn=fn->next) { if (fn->id == call->call_id) break; @@ -105,19 +106,12 @@ int ctdb_call_local(struct ctdb_db_context *ctdb_db, struct ctdb_call *call, return -1; } - if (header->laccessor != caller) { - header->lacount = 0; - } - header->laccessor = caller; - header->lacount++; - - /* we need to force the record to be written out if this was a remote access, - so that the lacount is updated */ - if (c->new_data == NULL && header->laccessor != ctdb->pnn) { + /* we need to force the record to be written out if this was a remote access */ + if (c->new_data == NULL) { c->new_data = &c->record_data; } - if (c->new_data) { + if (c->new_data && updatetdb) { /* XXX check that we always have the lock here? */ if (ctdb_ltdb_store(ctdb_db, call->key, header, *c->new_data) != 0) { ctdb_set_error(ctdb, "ctdb_call tdb_store failed\n"); @@ -193,7 +187,7 @@ static void ctdb_client_reply_control(struct ctdb_context *ctdb, struct ctdb_req /* this is called in the client, when data comes in from the daemon */ -static void ctdb_client_read_cb(uint8_t *data, size_t cnt, void *args) +void ctdb_client_read_cb(uint8_t *data, size_t cnt, void *args) { struct ctdb_context *ctdb = talloc_get_type(args, struct ctdb_context); struct ctdb_req_header *hdr = (struct ctdb_req_header *)data; @@ -253,16 +247,41 @@ done: } /* - connect to a unix domain socket + connect with exponential backoff, thanks Stevens */ -int ctdb_socket_connect(struct ctdb_context *ctdb) +#define CONNECT_MAXSLEEP 64 +static int ctdb_connect_retry(struct ctdb_context *ctdb) { struct sockaddr_un addr; + int secs; + int ret = 0; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, ctdb->daemon.name, sizeof(addr.sun_path)); + for (secs = 1; secs <= CONNECT_MAXSLEEP; secs *= 2) { + ret = connect(ctdb->daemon.sd, (struct sockaddr *)&addr, + sizeof(addr)); + if ((ret == 0) || (errno != EAGAIN)) { + break; + } + + if (secs <= (CONNECT_MAXSLEEP / 2)) { + DEBUG(DEBUG_ERR,("connect failed: %s, retry in %d second(s)\n", + strerror(errno), secs)); + sleep(secs); + } + } + + return ret; +} + +/* + connect to a unix domain socket +*/ +int ctdb_socket_connect(struct ctdb_context *ctdb) +{ ctdb->daemon.sd = socket(AF_UNIX, SOCK_STREAM, 0); if (ctdb->daemon.sd == -1) { DEBUG(DEBUG_ERR,(__location__ " Failed to open client socket. Errno:%s(%d)\n", strerror(errno), errno)); @@ -271,17 +290,17 @@ int ctdb_socket_connect(struct ctdb_context *ctdb) set_nonblocking(ctdb->daemon.sd); set_close_on_exec(ctdb->daemon.sd); - - if (connect(ctdb->daemon.sd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + + if (ctdb_connect_retry(ctdb) == -1) { + DEBUG(DEBUG_ERR,(__location__ " Failed to connect client socket to daemon. Errno:%s(%d)\n", strerror(errno), errno)); close(ctdb->daemon.sd); ctdb->daemon.sd = -1; - DEBUG(DEBUG_ERR,(__location__ " Failed to connect client socket to daemon. Errno:%s(%d)\n", strerror(errno), errno)); return -1; } ctdb->daemon.queue = ctdb_queue_setup(ctdb, ctdb, ctdb->daemon.sd, CTDB_DS_ALIGNMENT, - ctdb_client_read_cb, ctdb); + ctdb_client_read_cb, ctdb, "to-ctdbd"); return 0; } @@ -327,7 +346,7 @@ int ctdb_call_recv(struct ctdb_client_call_state *state, struct ctdb_call *call) call->status = state->call->status; talloc_free(state); - return 0; + return call->status; } @@ -368,7 +387,7 @@ static struct ctdb_client_call_state *ctdb_client_call_local_send(struct ctdb_db *(state->call) = *call; state->ctdb_db = ctdb_db; - ret = ctdb_call_local(ctdb_db, state->call, header, state, data, ctdb->pnn); + ret = ctdb_call_local(ctdb_db, state->call, header, state, data, true); return state; } @@ -403,6 +422,10 @@ struct ctdb_client_call_state *ctdb_call_send(struct ctdb_db_context *ctdb_db, ret = ctdb_ltdb_fetch(ctdb_db, call->key, &header, ctdb_db, &data); + if ((call->flags & CTDB_IMMEDIATE_MIGRATION) && (header.flags & CTDB_REC_RO_HAVE_DELEGATIONS)) { + ret = -1; + } + if (ret == 0 && header.dmaster == ctdb->pnn) { state = ctdb_client_call_local_send(ctdb_db, call, &header, &data); talloc_free(data.dptr); @@ -474,8 +497,8 @@ int ctdb_call(struct ctdb_db_context *ctdb_db, struct ctdb_call *call) tell the daemon what messaging srvid we will use, and register the message handler function in the client */ -int ctdb_set_message_handler(struct ctdb_context *ctdb, uint64_t srvid, - ctdb_message_fn_t handler, +int ctdb_client_set_message_handler(struct ctdb_context *ctdb, uint64_t srvid, + ctdb_msg_fn_t handler, void *private_data) { @@ -496,7 +519,7 @@ int ctdb_set_message_handler(struct ctdb_context *ctdb, uint64_t srvid, /* tell the daemon we no longer want a srvid */ -int ctdb_remove_message_handler(struct ctdb_context *ctdb, uint64_t srvid, void *private_data) +int ctdb_client_remove_message_handler(struct ctdb_context *ctdb, uint64_t srvid, void *private_data) { int res; int32_t status; @@ -517,7 +540,7 @@ int ctdb_remove_message_handler(struct ctdb_context *ctdb, uint64_t srvid, void /* send a message - from client context */ -int ctdb_send_message(struct ctdb_context *ctdb, uint32_t pnn, +int ctdb_client_send_message(struct ctdb_context *ctdb, uint32_t pnn, uint64_t srvid, TDB_DATA data) { struct ctdb_req_message *r; @@ -565,6 +588,48 @@ static int ctdb_client_force_migration(struct ctdb_db_context *ctdb_db, TDB_DATA return ctdb_call(ctdb_db, &call); } +/* + try to fetch a readonly copy of a record + */ +static int +ctdb_client_fetch_readonly(struct ctdb_db_context *ctdb_db, TDB_DATA key, TALLOC_CTX *mem_ctx, struct ctdb_ltdb_header **hdr, TDB_DATA *data) +{ + int ret; + + struct ctdb_call call; + ZERO_STRUCT(call); + + call.call_id = CTDB_FETCH_WITH_HEADER_FUNC; + call.call_data.dptr = NULL; + call.call_data.dsize = 0; + call.key = key; + call.flags = CTDB_WANT_READONLY; + ret = ctdb_call(ctdb_db, &call); + + if (ret != 0) { + return -1; + } + if (call.reply_data.dsize < sizeof(struct ctdb_ltdb_header)) { + return -1; + } + + *hdr = talloc_memdup(mem_ctx, &call.reply_data.dptr[0], sizeof(struct ctdb_ltdb_header)); + if (*hdr == NULL) { + talloc_free(call.reply_data.dptr); + return -1; + } + + data->dsize = call.reply_data.dsize - sizeof(struct ctdb_ltdb_header); + data->dptr = talloc_memdup(mem_ctx, &call.reply_data.dptr[sizeof(struct ctdb_ltdb_header)], data->dsize); + if (data->dptr == NULL) { + talloc_free(call.reply_data.dptr); + talloc_free(hdr); + return -1; + } + + return 0; +} + /* get a lock on a record, and return the records data. Blocks until it gets the lock */ @@ -641,6 +706,185 @@ again: return h; } +/* + get a readonly lock on a record, and return the records data. Blocks until it gets the lock + */ +struct ctdb_record_handle * +ctdb_fetch_readonly_lock( + struct ctdb_db_context *ctdb_db, TALLOC_CTX *mem_ctx, + TDB_DATA key, TDB_DATA *data, + int read_only) +{ + int ret; + struct ctdb_record_handle *h; + struct ctdb_ltdb_header *roheader = NULL; + + h = talloc_zero(mem_ctx, struct ctdb_record_handle); + if (h == NULL) { + return NULL; + } + + h->ctdb_db = ctdb_db; + h->key = key; + h->key.dptr = talloc_memdup(h, key.dptr, key.dsize); + if (h->key.dptr == NULL) { + talloc_free(h); + return NULL; + } + h->data = data; + + data->dptr = NULL; + data->dsize = 0; + + +again: + talloc_free(roheader); + roheader = NULL; + + talloc_free(data->dptr); + data->dptr = NULL; + data->dsize = 0; + + /* Lock the record/chain */ + ret = ctdb_ltdb_lock(ctdb_db, key); + if (ret != 0) { + DEBUG(DEBUG_ERR, (__location__ " failed to lock ltdb record\n")); + talloc_free(h); + return NULL; + } + + talloc_set_destructor(h, fetch_lock_destructor); + + /* Check if record exists yet in the TDB */ + ret = ctdb_ltdb_fetch_readonly(ctdb_db, key, &h->header, h, data); + if (ret != 0) { + ctdb_ltdb_unlock(ctdb_db, key); + ret = ctdb_client_force_migration(ctdb_db, key); + if (ret != 0) { + DEBUG(DEBUG_DEBUG,("ctdb_fetch_readonly_lock: force_migration failed\n")); + talloc_free(h); + return NULL; + } + goto again; + } + + /* if this is a request for read/write and we have delegations + we have to revoke all delegations first + */ + if ((read_only == 0) + && (h->header.dmaster == ctdb_db->ctdb->pnn) + && (h->header.flags & CTDB_REC_RO_HAVE_DELEGATIONS)) { + ctdb_ltdb_unlock(ctdb_db, key); + ret = ctdb_client_force_migration(ctdb_db, key); + if (ret != 0) { + DEBUG(DEBUG_DEBUG,("ctdb_fetch_readonly_lock: force_migration failed\n")); + talloc_free(h); + return NULL; + } + goto again; + } + + /* if we are dmaster, just return the handle */ + if (h->header.dmaster == ctdb_db->ctdb->pnn) { + return h; + } + + if (read_only != 0) { + TDB_DATA rodata = {NULL, 0}; + + if ((h->header.flags & CTDB_REC_RO_HAVE_READONLY) + || (h->header.flags & CTDB_REC_RO_HAVE_DELEGATIONS)) { + return h; + } + + ctdb_ltdb_unlock(ctdb_db, key); + ret = ctdb_client_fetch_readonly(ctdb_db, key, h, &roheader, &rodata); + if (ret != 0) { + DEBUG(DEBUG_ERR,("ctdb_fetch_readonly_lock: failed. force migration and try again\n")); + ret = ctdb_client_force_migration(ctdb_db, key); + if (ret != 0) { + DEBUG(DEBUG_DEBUG,("ctdb_fetch_readonly_lock: force_migration failed\n")); + talloc_free(h); + return NULL; + } + + goto again; + } + + if (!(roheader->flags&CTDB_REC_RO_HAVE_READONLY)) { + ret = ctdb_client_force_migration(ctdb_db, key); + if (ret != 0) { + DEBUG(DEBUG_DEBUG,("ctdb_fetch_readonly_lock: force_migration failed\n")); + talloc_free(h); + return NULL; + } + + goto again; + } + + ret = ctdb_ltdb_lock(ctdb_db, key); + if (ret != 0) { + DEBUG(DEBUG_ERR, (__location__ " failed to lock ltdb record\n")); + talloc_free(h); + return NULL; + } + + ret = ctdb_ltdb_fetch_readonly(ctdb_db, key, &h->header, h, data); + if (ret != 0) { + ctdb_ltdb_unlock(ctdb_db, key); + + ret = ctdb_client_force_migration(ctdb_db, key); + if (ret != 0) { + DEBUG(DEBUG_DEBUG,("ctdb_fetch_readonly_lock: force_migration failed\n")); + talloc_free(h); + return NULL; + } + + goto again; + } + + if (h->header.rsn >= roheader->rsn) { + DEBUG(DEBUG_ERR,("READONLY RECORD: Too small RSN, migrate and try again\n")); + ctdb_ltdb_unlock(ctdb_db, key); + + ret = ctdb_client_force_migration(ctdb_db, key); + if (ret != 0) { + DEBUG(DEBUG_DEBUG,("ctdb_fetch_readonly_lock: force_migration failed\n")); + talloc_free(h); + return NULL; + } + + goto again; + } + + if (ctdb_ltdb_store(ctdb_db, key, roheader, rodata) != 0) { + ctdb_ltdb_unlock(ctdb_db, key); + + ret = ctdb_client_force_migration(ctdb_db, key); + if (ret != 0) { + DEBUG(DEBUG_DEBUG,("ctdb_fetch_readonly_lock: force_migration failed\n")); + talloc_free(h); + return NULL; + } + + goto again; + } + return h; + } + + /* we are not dmaster and this was not a request for a readonly lock + * so unlock the record, migrate it and try again + */ + ctdb_ltdb_unlock(ctdb_db, key); + ret = ctdb_client_force_migration(ctdb_db, key); + if (ret != 0) { + DEBUG(DEBUG_DEBUG,("ctdb_fetch_lock: force_migration failed\n")); + talloc_free(h); + return NULL; + } + goto again; +} + /* store some data to the record that was locked with ctdb_fetch_lock() */ @@ -666,6 +910,7 @@ int ctdb_fetch(struct ctdb_db_context *ctdb_db, TALLOC_CTX *mem_ctx, call.call_id = CTDB_FETCH_FUNC; call.call_data.dptr = NULL; call.call_data.dsize = 0; + call.key = key; ret = ctdb_call(ctdb_db, &call); @@ -1675,10 +1920,35 @@ static int ctdb_fetch_func(struct ctdb_call_info *call) return 0; } +/* + this is a plain fetch procedure that all databases support + this returns the full record including the ltdb header +*/ +static int ctdb_fetch_with_header_func(struct ctdb_call_info *call) +{ + call->reply_data = talloc(call, TDB_DATA); + if (call->reply_data == NULL) { + return -1; + } + call->reply_data->dsize = sizeof(struct ctdb_ltdb_header) + call->record_data.dsize; + call->reply_data->dptr = talloc_size(call->reply_data, call->reply_data->dsize); + if (call->reply_data->dptr == NULL) { + return -1; + } + memcpy(call->reply_data->dptr, call->header, sizeof(struct ctdb_ltdb_header)); + memcpy(&call->reply_data->dptr[sizeof(struct ctdb_ltdb_header)], call->record_data.dptr, call->record_data.dsize); + + return 0; +} + /* attach to a specific database - client call */ -struct ctdb_db_context *ctdb_attach(struct ctdb_context *ctdb, const char *name, bool persistent, uint32_t tdb_flags) +struct ctdb_db_context *ctdb_attach(struct ctdb_context *ctdb, + struct timeval timeout, + const char *name, + bool persistent, + uint32_t tdb_flags) { struct ctdb_db_context *ctdb_db; TDB_DATA data; @@ -1713,7 +1983,7 @@ struct ctdb_db_context *ctdb_attach(struct ctdb_context *ctdb, const char *name, ctdb_db->db_id = *(uint32_t *)data.dptr; talloc_free(data.dptr); - ret = ctdb_ctrl_getdbpath(ctdb, timeval_current_ofs(2, 0), CTDB_CURRENT_NODE, ctdb_db->db_id, ctdb_db, &ctdb_db->db_path); + ret = ctdb_ctrl_getdbpath(ctdb, timeout, CTDB_CURRENT_NODE, ctdb_db->db_id, ctdb_db, &ctdb_db->db_path); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to get dbpath for database '%s'\n", name)); talloc_free(ctdb_db); @@ -1740,6 +2010,7 @@ struct ctdb_db_context *ctdb_attach(struct ctdb_context *ctdb, const char *name, /* add well known functions */ ctdb_set_call(ctdb_db, ctdb_null_func, CTDB_NULL_FUNC); ctdb_set_call(ctdb_db, ctdb_fetch_func, CTDB_FETCH_FUNC); + ctdb_set_call(ctdb_db, ctdb_fetch_with_header_func, CTDB_FETCH_WITH_HEADER_FUNC); return ctdb_db; } @@ -1849,7 +2120,7 @@ int ctdb_traverse(struct ctdb_db_context *ctdb_db, ctdb_traverse_func fn, void * state.private_data = private_data; state.fn = fn; - ret = ctdb_set_message_handler(ctdb_db->ctdb, srvid, traverse_handler, &state); + ret = ctdb_client_set_message_handler(ctdb_db->ctdb, srvid, traverse_handler, &state); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to setup traverse handler\n")); return -1; @@ -1866,7 +2137,7 @@ int ctdb_traverse(struct ctdb_db_context *ctdb_db, ctdb_traverse_func fn, void * data, NULL, NULL, &status, NULL, NULL); if (ret != 0 || status != 0) { DEBUG(DEBUG_ERR,("ctdb_traverse_all failed\n")); - ctdb_remove_message_handler(ctdb_db->ctdb, srvid, &state); + ctdb_client_remove_message_handler(ctdb_db->ctdb, srvid, &state); return -1; } @@ -1874,7 +2145,7 @@ int ctdb_traverse(struct ctdb_db_context *ctdb_db, ctdb_traverse_func fn, void * event_loop_once(ctdb_db->ctdb->ev); } - ret = ctdb_remove_message_handler(ctdb_db->ctdb, srvid, &state); + ret = ctdb_client_remove_message_handler(ctdb_db->ctdb, srvid, &state); if (ret != 0) { DEBUG(DEBUG_ERR,("Failed to remove ctdb_traverse handler\n")); return -1; @@ -1883,7 +2154,7 @@ int ctdb_traverse(struct ctdb_db_context *ctdb_db, ctdb_traverse_func fn, void * return state.count; } -#define ISASCII(x) ((x>31)&&(x<128)) +#define ISASCII(x) (isprint(x) && !strchr("\"\\", (x))) /* called on each key during a catdb */ @@ -1905,8 +2176,17 @@ int ctdb_dumpdb_record(struct ctdb_context *ctdb, TDB_DATA key, TDB_DATA data, v fprintf(f, "dmaster: %u\n", h->dmaster); fprintf(f, "rsn: %llu\n", (unsigned long long)h->rsn); + fprintf(f, "flags: 0x%08x", h->flags); + if (h->flags & CTDB_REC_FLAG_MIGRATED_WITH_DATA) printf(" MIGRATED_WITH_DATA"); + if (h->flags & CTDB_REC_FLAG_VACUUM_MIGRATED) printf(" VACUUM_MIGRATED"); + if (h->flags & CTDB_REC_FLAG_AUTOMATIC) printf(" AUTOMATIC"); + if (h->flags & CTDB_REC_RO_HAVE_DELEGATIONS) printf(" RO_HAVE_DELEGATIONS"); + if (h->flags & CTDB_REC_RO_HAVE_READONLY) printf(" RO_HAVE_READONLY"); + if (h->flags & CTDB_REC_RO_REVOKING_READONLY) printf(" RO_REVOKING_READONLY"); + if (h->flags & CTDB_REC_RO_REVOKE_COMPLETE) printf(" RO_REVOKE_COMPLETE"); + fprintf(f, "\n"); - fprintf(f, "data(%u) = \"", (unsigned)data.dsize - sizeof(*h)); + fprintf(f, "data(%u) = \"", (unsigned)(data.dsize - sizeof(*h))); for (i=sizeof(*h);iev = ev; ctdb->idr = idr_init(ctdb); + /* Wrap early to exercise code. */ + ctdb->lastid = INT_MAX-200; CTDB_NO_MEMORY_NULL(ctdb, ctdb->idr); ret = ctdb_set_socketname(ctdb, CTDB_PATH); @@ -2925,6 +3207,8 @@ struct ctdb_context *ctdb_init(struct event_context *ev) return NULL; } + ctdb->statistics.statistics_start_time = timeval_current(); + return ctdb; } @@ -2948,6 +3232,11 @@ int ctdb_set_socketname(struct ctdb_context *ctdb, const char *socketname) return 0; } +const char *ctdb_get_socketname(struct ctdb_context *ctdb) +{ + return ctdb->daemon.name; +} + /* return the pnn of this node */ @@ -3818,9 +4107,15 @@ int ctdb_ctrl_recd_ping(struct ctdb_context *ctdb) * to the daemon as a client process, this function can be used to change * the ctdb context from daemon into client mode */ -int switch_from_server_to_client(struct ctdb_context *ctdb) +int switch_from_server_to_client(struct ctdb_context *ctdb, const char *fmt, ...) { int ret; + va_list ap; + + /* Add extra information so we can identify this in the logs */ + va_start(ap, fmt); + debug_extra = talloc_strdup_append(talloc_vasprintf(NULL, fmt, ap), ":"); + va_end(ap); /* shutdown the transport */ if (ctdb->methods) { @@ -3830,10 +4125,16 @@ int switch_from_server_to_client(struct ctdb_context *ctdb) /* get a new event context */ talloc_free(ctdb->ev); ctdb->ev = event_context_init(ctdb); + tevent_loop_allow_nesting(ctdb->ev); close(ctdb->daemon.sd); ctdb->daemon.sd = -1; + /* the client does not need to be realtime */ + if (ctdb->do_setsched) { + ctdb_restore_scheduler(ctdb); + } + /* initialise ctdb */ ret = ctdb_socket_connect(ctdb); if (ret != 0) { @@ -4200,3 +4501,141 @@ int ctdb_ctrl_get_db_priority(struct ctdb_context *ctdb, struct timeval timeout, return 0; } + +int ctdb_ctrl_getstathistory(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, TALLOC_CTX *mem_ctx, struct ctdb_statistics_wire **stats) +{ + int ret; + TDB_DATA outdata; + int32_t res; + + ret = ctdb_control(ctdb, destnode, 0, + CTDB_CONTROL_GET_STAT_HISTORY, 0, tdb_null, + mem_ctx, &outdata, &res, &timeout, NULL); + if (ret != 0 || res != 0 || outdata.dsize == 0) { + DEBUG(DEBUG_ERR,(__location__ " ctdb_control for getstathistory failed ret:%d res:%d\n", ret, res)); + return -1; + } + + *stats = (struct ctdb_statistics_wire *)talloc_memdup(mem_ctx, outdata.dptr, outdata.dsize); + talloc_free(outdata.dptr); + + return 0; +} + +struct ctdb_ltdb_header *ctdb_header_from_record_handle(struct ctdb_record_handle *h) +{ + if (h == NULL) { + return NULL; + } + + return &h->header; +} + + +struct ctdb_client_control_state * +ctdb_ctrl_updaterecord_send(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx, struct timeval timeout, uint32_t destnode, struct ctdb_db_context *ctdb_db, TDB_DATA key, struct ctdb_ltdb_header *header, TDB_DATA data) +{ + struct ctdb_client_control_state *handle; + struct ctdb_marshall_buffer *m; + struct ctdb_rec_data *rec; + TDB_DATA outdata; + + m = talloc_zero(mem_ctx, struct ctdb_marshall_buffer); + if (m == NULL) { + DEBUG(DEBUG_ERR, ("Failed to allocate marshall buffer for update record\n")); + return NULL; + } + + m->db_id = ctdb_db->db_id; + + rec = ctdb_marshall_record(m, 0, key, header, data); + if (rec == NULL) { + DEBUG(DEBUG_ERR,("Failed to marshall record for update record\n")); + talloc_free(m); + return NULL; + } + m = talloc_realloc_size(mem_ctx, m, rec->length + offsetof(struct ctdb_marshall_buffer, data)); + if (m == NULL) { + DEBUG(DEBUG_CRIT,(__location__ " Failed to expand recdata\n")); + talloc_free(m); + return NULL; + } + m->count++; + memcpy((uint8_t *)m + offsetof(struct ctdb_marshall_buffer, data), rec, rec->length); + + + outdata.dptr = (uint8_t *)m; + outdata.dsize = talloc_get_size(m); + + handle = ctdb_control_send(ctdb, destnode, 0, + CTDB_CONTROL_UPDATE_RECORD, 0, outdata, + mem_ctx, &timeout, NULL); + talloc_free(m); + return handle; +} + +int ctdb_ctrl_updaterecord_recv(struct ctdb_context *ctdb, struct ctdb_client_control_state *state) +{ + int ret; + int32_t res; + + ret = ctdb_control_recv(ctdb, state, state, NULL, &res, NULL); + if ( (ret != 0) || (res != 0) ){ + DEBUG(DEBUG_ERR,(__location__ " ctdb_ctrl_update_record_recv failed\n")); + return -1; + } + + return 0; +} + +int +ctdb_ctrl_updaterecord(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx, struct timeval timeout, uint32_t destnode, struct ctdb_db_context *ctdb_db, TDB_DATA key, struct ctdb_ltdb_header *header, TDB_DATA data) +{ + struct ctdb_client_control_state *state; + + state = ctdb_ctrl_updaterecord_send(ctdb, mem_ctx, timeout, destnode, ctdb_db, key, header, data); + return ctdb_ctrl_updaterecord_recv(ctdb, state); +} + + + + + + +/* + set a database to be readonly + */ +struct ctdb_client_control_state * +ctdb_ctrl_set_db_readonly_send(struct ctdb_context *ctdb, uint32_t destnode, uint32_t dbid) +{ + TDB_DATA data; + + data.dptr = (uint8_t *)&dbid; + data.dsize = sizeof(dbid); + + return ctdb_control_send(ctdb, destnode, 0, + CTDB_CONTROL_SET_DB_READONLY, 0, data, + ctdb, NULL, NULL); +} + +int ctdb_ctrl_set_db_readonly_recv(struct ctdb_context *ctdb, struct ctdb_client_control_state *state) +{ + int ret; + int32_t res; + + ret = ctdb_control_recv(ctdb, state, ctdb, NULL, &res, NULL); + if (ret != 0 || res != 0) { + DEBUG(DEBUG_ERR,(__location__ " ctdb_ctrl_set_db_readonly_recv failed ret:%d res:%d\n", ret, res)); + return -1; + } + + return 0; +} + +int ctdb_ctrl_set_db_readonly(struct ctdb_context *ctdb, uint32_t destnode, uint32_t dbid) +{ + struct ctdb_client_control_state *state; + + state = ctdb_ctrl_set_db_readonly_send(ctdb, destnode, dbid); + return ctdb_ctrl_set_db_readonly_recv(ctdb, state); +}