add a function ctdb_writerecord() to write a record to the database.
[sahlberg/ctdb.git] / client / ctdb_client.c
index 6158fe24100ba2cc8035b134d6e0320513387dc5..4d492939d90e1c2d3c128e83a16b945e75659344 100644 (file)
 #include "system/filesys.h"
 #include "system/locale.h"
 #include <stdlib.h>
-#include "../include/ctdb_private.h"
+#include "include/ctdb_protocol.h"
+#include "include/ctdb_private.h"
 #include "lib/util/dlinklist.h"
 
-pid_t ctdbd_pid;
 
-/*
-  allocate a packet for use in client<->daemon communication
- */
-struct ctdb_req_header *_ctdbd_allocate_pkt(struct ctdb_context *ctdb,
-                                           TALLOC_CTX *mem_ctx, 
-                                           enum ctdb_operation operation, 
-                                           size_t length, size_t slength,
-                                           const char *type)
-{
-       int size;
-       struct ctdb_req_header *hdr;
-
-       length = MAX(length, slength);
-       size = (length+(CTDB_DS_ALIGNMENT-1)) & ~(CTDB_DS_ALIGNMENT-1);
-
-       hdr = (struct ctdb_req_header *)talloc_size(mem_ctx, size);
-       if (hdr == NULL) {
-               DEBUG(DEBUG_ERR,("Unable to allocate packet for operation %u of length %u\n",
-                        operation, (unsigned)length));
-               return NULL;
-       }
-       talloc_set_name_const(hdr, type);
-       memset(hdr, 0, slength);
-       hdr->length       = length;
-       hdr->operation    = operation;
-       hdr->ctdb_magic   = CTDB_MAGIC;
-       hdr->ctdb_version = CTDB_VERSION;
-       hdr->srcnode      = ctdb->pnn;
-       if (ctdb->vnn_map) {
-               hdr->generation = ctdb->vnn_map->generation;
-       }
-
-       return hdr;
-}
-
-/*
-  local version of ctdb_call
-*/
-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)
-{
-       struct ctdb_call_info *c;
-       struct ctdb_registered_call *fn;
-       struct ctdb_context *ctdb = ctdb_db->ctdb;
-       
-       c = talloc(ctdb, struct ctdb_call_info);
-       CTDB_NO_MEMORY(ctdb, c);
-
-       c->key = call->key;
-       c->call_data = &call->call_data;
-       c->record_data.dptr = talloc_memdup(c, data->dptr, data->dsize);
-       c->record_data.dsize = data->dsize;
-       CTDB_NO_MEMORY(ctdb, c->record_data.dptr);
-       c->new_data = NULL;
-       c->reply_data = NULL;
-       c->status = 0;
-
-       for (fn=ctdb_db->calls;fn;fn=fn->next) {
-               if (fn->id == call->call_id) break;
-       }
-       if (fn == NULL) {
-               ctdb_set_error(ctdb, "Unknown call id %u\n", call->call_id);
-               talloc_free(c);
-               return -1;
-       }
-
-       if (fn->fn(c) != 0) {
-               ctdb_set_error(ctdb, "ctdb_call %u failed\n", call->call_id);
-               talloc_free(c);
-               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) {
-               c->new_data = &c->record_data;
-       }
-
-       if (c->new_data) {
-               /* 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");
-                       talloc_free(c);
-                       return -1;
-               }
-       }
-
-       if (c->reply_data) {
-               call->reply_data = *c->reply_data;
-
-               talloc_steal(call, call->reply_data.dptr);
-               talloc_set_name_const(call->reply_data.dptr, __location__);
-       } else {
-               call->reply_data.dptr = NULL;
-               call->reply_data.dsize = 0;
-       }
-       call->status = c->status;
-
-       talloc_free(c);
-
-       return 0;
-}
-
-
-/*
-  queue a packet for sending from client to daemon
-*/
-static int ctdb_client_queue_pkt(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
-{
-       return ctdb_queue_send(ctdb->daemon.queue, (uint8_t *)hdr, hdr->length);
-}
-
-
-/*
-  called when a CTDB_REPLY_CALL packet comes in in the client
-
-  This packet comes in response to a CTDB_REQ_CALL request packet. It
-  contains any reply data from the call
-*/
-static void ctdb_client_reply_call(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
-{
-       struct ctdb_reply_call *c = (struct ctdb_reply_call *)hdr;
-       struct ctdb_client_call_state *state;
-
-       state = ctdb_reqid_find(ctdb, hdr->reqid, struct ctdb_client_call_state);
-       if (state == NULL) {
-               DEBUG(DEBUG_ERR,(__location__ " reqid %u not found\n", hdr->reqid));
-               return;
-       }
-
-       if (hdr->reqid != state->reqid) {
-               /* we found a record  but it was the wrong one */
-               DEBUG(DEBUG_ERR, ("Dropped client call reply with reqid:%u\n",hdr->reqid));
-               return;
-       }
-
-       state->call->reply_data.dptr = c->data;
-       state->call->reply_data.dsize = c->datalen;
-       state->call->status = c->status;
-
-       talloc_steal(state, c);
-
-       state->state = CTDB_CALL_DONE;
-
-       if (state->async.fn) {
-               state->async.fn(state);
-       }
-}
-
-static void ctdb_client_reply_control(struct ctdb_context *ctdb, struct ctdb_req_header *hdr);
-
-/*
-  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)
-{
-       struct ctdb_context *ctdb = talloc_get_type(args, struct ctdb_context);
-       struct ctdb_req_header *hdr = (struct ctdb_req_header *)data;
-       TALLOC_CTX *tmp_ctx;
-
-       /* place the packet as a child of a tmp_ctx. We then use
-          talloc_free() below to free it. If any of the calls want
-          to keep it, then they will steal it somewhere else, and the
-          talloc_free() will be a no-op */
-       tmp_ctx = talloc_new(ctdb);
-       talloc_steal(tmp_ctx, hdr);
-
-       if (cnt == 0) {
-               DEBUG(DEBUG_INFO,("Daemon has exited - shutting down client\n"));
-               exit(0);
-       }
-
-       if (cnt < sizeof(*hdr)) {
-               DEBUG(DEBUG_CRIT,("Bad packet length %u in client\n", (unsigned)cnt));
-               goto done;
-       }
-       if (cnt != hdr->length) {
-               ctdb_set_error(ctdb, "Bad header length %u expected %u in client\n", 
-                              (unsigned)hdr->length, (unsigned)cnt);
-               goto done;
-       }
-
-       if (hdr->ctdb_magic != CTDB_MAGIC) {
-               ctdb_set_error(ctdb, "Non CTDB packet rejected in client\n");
-               goto done;
-       }
-
-       if (hdr->ctdb_version != CTDB_VERSION) {
-               ctdb_set_error(ctdb, "Bad CTDB version 0x%x rejected in client\n", hdr->ctdb_version);
-               goto done;
-       }
-
-       switch (hdr->operation) {
-       case CTDB_REPLY_CALL:
-               ctdb_client_reply_call(ctdb, hdr);
-               break;
-
-       case CTDB_REQ_MESSAGE:
-               ctdb_request_message(ctdb, hdr);
-               break;
-
-       case CTDB_REPLY_CONTROL:
-               ctdb_client_reply_control(ctdb, hdr);
-               break;
-
-       default:
-               DEBUG(DEBUG_CRIT,("bogus operation code:%u\n",hdr->operation));
-       }
-
-done:
-       talloc_free(tmp_ctx);
-}
-
-/*
-  connect to a unix domain socket
-*/
-int ctdb_socket_connect(struct ctdb_context *ctdb)
-{
-       struct sockaddr_un addr;
-
-       memset(&addr, 0, sizeof(addr));
-       addr.sun_family = AF_UNIX;
-       strncpy(addr.sun_path, ctdb->daemon.name, sizeof(addr.sun_path));
-
-       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));
-               return -1;
-       }
-
-       set_nonblocking(ctdb->daemon.sd);
-       set_close_on_exec(ctdb->daemon.sd);
-       
-       if (connect(ctdb->daemon.sd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
-               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);
-       return 0;
-}
-
-
-struct ctdb_record_handle {
-       struct ctdb_db_context *ctdb_db;
-       TDB_DATA key;
-       TDB_DATA *data;
-       struct ctdb_ltdb_header header;
-};
-
-
-/*
-  make a recv call to the local ctdb daemon - called from client context
-
-  This is called when the program wants to wait for a ctdb_call to complete and get the 
-  results. This call will block unless the call has already completed.
-*/
-int ctdb_call_recv(struct ctdb_client_call_state *state, struct ctdb_call *call)
-{
-       if (state == NULL) {
-               return -1;
-       }
-
-       while (state->state < CTDB_CALL_DONE) {
-               event_loop_once(state->ctdb_db->ctdb->ev);
-       }
-       if (state->state != CTDB_CALL_DONE) {
-               DEBUG(DEBUG_ERR,(__location__ " ctdb_call_recv failed\n"));
-               talloc_free(state);
-               return -1;
-       }
-
-       if (state->call->reply_data.dsize) {
-               call->reply_data.dptr = talloc_memdup(state->ctdb_db,
-                                                     state->call->reply_data.dptr,
-                                                     state->call->reply_data.dsize);
-               call->reply_data.dsize = state->call->reply_data.dsize;
-       } else {
-               call->reply_data.dptr = NULL;
-               call->reply_data.dsize = 0;
-       }
-       call->status = state->call->status;
-       talloc_free(state);
-
-       return 0;
-}
-
-
-
-
-/*
-  destroy a ctdb_call in client
-*/
-static int ctdb_client_call_destructor(struct ctdb_client_call_state *state)   
-{
-       ctdb_reqid_remove(state->ctdb_db->ctdb, state->reqid);
-       return 0;
-}
-
-/*
-  construct an event driven local ctdb_call
-
-  this is used so that locally processed ctdb_call requests are processed
-  in an event driven manner
-*/
-static struct ctdb_client_call_state *ctdb_client_call_local_send(struct ctdb_db_context *ctdb_db, 
-                                                                 struct ctdb_call *call,
-                                                                 struct ctdb_ltdb_header *header,
-                                                                 TDB_DATA *data)
-{
-       struct ctdb_client_call_state *state;
-       struct ctdb_context *ctdb = ctdb_db->ctdb;
-       int ret;
-
-       state = talloc_zero(ctdb_db, struct ctdb_client_call_state);
-       CTDB_NO_MEMORY_NULL(ctdb, state);
-       state->call = talloc_zero(state, struct ctdb_call);
-       CTDB_NO_MEMORY_NULL(ctdb, state->call);
-
-       talloc_steal(state, data->dptr);
-
-       state->state   = CTDB_CALL_DONE;
-       *(state->call) = *call;
-       state->ctdb_db = ctdb_db;
-
-       ret = ctdb_call_local(ctdb_db, state->call, header, state, data, ctdb->pnn);
-
-       return state;
-}
-
-/*
-  make a ctdb call to the local daemon - async send. Called from client context.
-
-  This constructs a ctdb_call request and queues it for processing. 
-  This call never blocks.
-*/
-struct ctdb_client_call_state *ctdb_call_send(struct ctdb_db_context *ctdb_db, 
-                                             struct ctdb_call *call)
-{
-       struct ctdb_client_call_state *state;
-       struct ctdb_context *ctdb = ctdb_db->ctdb;
-       struct ctdb_ltdb_header header;
-       TDB_DATA data;
-       int ret;
-       size_t len;
-       struct ctdb_req_call *c;
-
-       /* if the domain socket is not yet open, open it */
-       if (ctdb->daemon.sd==-1) {
-               ctdb_socket_connect(ctdb);
-       }
-
-       ret = ctdb_ltdb_lock(ctdb_db, call->key);
-       if (ret != 0) {
-               DEBUG(DEBUG_ERR,(__location__ " Failed to get chainlock\n"));
-               return NULL;
-       }
-
-       ret = ctdb_ltdb_fetch(ctdb_db, call->key, &header, ctdb_db, &data);
-
-       if (ret == 0 && header.dmaster == ctdb->pnn) {
-               state = ctdb_client_call_local_send(ctdb_db, call, &header, &data);
-               talloc_free(data.dptr);
-               ctdb_ltdb_unlock(ctdb_db, call->key);
-               return state;
-       }
-
-       ctdb_ltdb_unlock(ctdb_db, call->key);
-       talloc_free(data.dptr);
-
-       state = talloc_zero(ctdb_db, struct ctdb_client_call_state);
-       if (state == NULL) {
-               DEBUG(DEBUG_ERR, (__location__ " failed to allocate state\n"));
-               return NULL;
-       }
-       state->call = talloc_zero(state, struct ctdb_call);
-       if (state->call == NULL) {
-               DEBUG(DEBUG_ERR, (__location__ " failed to allocate state->call\n"));
-               return NULL;
-       }
-
-       len = offsetof(struct ctdb_req_call, data) + call->key.dsize + call->call_data.dsize;
-       c = ctdbd_allocate_pkt(ctdb, state, CTDB_REQ_CALL, len, struct ctdb_req_call);
-       if (c == NULL) {
-               DEBUG(DEBUG_ERR, (__location__ " failed to allocate packet\n"));
-               return NULL;
-       }
-
-       state->reqid     = ctdb_reqid_new(ctdb, state);
-       state->ctdb_db = ctdb_db;
-       talloc_set_destructor(state, ctdb_client_call_destructor);
-
-       c->hdr.reqid     = state->reqid;
-       c->flags         = call->flags;
-       c->db_id         = ctdb_db->db_id;
-       c->callid        = call->call_id;
-       c->hopcount      = 0;
-       c->keylen        = call->key.dsize;
-       c->calldatalen   = call->call_data.dsize;
-       memcpy(&c->data[0], call->key.dptr, call->key.dsize);
-       memcpy(&c->data[call->key.dsize], 
-              call->call_data.dptr, call->call_data.dsize);
-       *(state->call)              = *call;
-       state->call->call_data.dptr = &c->data[call->key.dsize];
-       state->call->key.dptr       = &c->data[0];
-
-       state->state  = CTDB_CALL_WAIT;
-
-
-       ctdb_client_queue_pkt(ctdb, &c->hdr);
-
-       return state;
-}
-
-
-/*
-  full ctdb_call. Equivalent to a ctdb_call_send() followed by a ctdb_call_recv()
-*/
-int ctdb_call(struct ctdb_db_context *ctdb_db, struct ctdb_call *call)
-{
-       struct ctdb_client_call_state *state;
-
-       state = ctdb_call_send(ctdb_db, call);
-       return ctdb_call_recv(state, 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,
-                            void *private_data)
-                                   
-{
-       int res;
-       int32_t status;
-       
-       res = ctdb_control(ctdb, CTDB_CURRENT_NODE, srvid, CTDB_CONTROL_REGISTER_SRVID, 0, 
-                          tdb_null, NULL, NULL, &status, NULL, NULL);
-       if (res != 0 || status != 0) {
-               DEBUG(DEBUG_ERR,("Failed to register srvid %llu\n", (unsigned long long)srvid));
-               return -1;
-       }
-
-       /* also need to register the handler with our own ctdb structure */
-       return ctdb_register_message_handler(ctdb, ctdb, srvid, handler, private_data);
-}
-
-/*
-  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 res;
-       int32_t status;
-       
-       res = ctdb_control(ctdb, CTDB_CURRENT_NODE, srvid, CTDB_CONTROL_DEREGISTER_SRVID, 0, 
-                          tdb_null, NULL, NULL, &status, NULL, NULL);
-       if (res != 0 || status != 0) {
-               DEBUG(DEBUG_ERR,("Failed to deregister srvid %llu\n", (unsigned long long)srvid));
-               return -1;
-       }
-
-       /* also need to register the handler with our own ctdb structure */
-       ctdb_deregister_message_handler(ctdb, srvid, private_data);
-       return 0;
-}
-
-
-/*
-  send a message - from client context
- */
-int ctdb_send_message(struct ctdb_context *ctdb, uint32_t pnn,
-                     uint64_t srvid, TDB_DATA data)
-{
-       struct ctdb_req_message *r;
-       int len, res;
-
-       len = offsetof(struct ctdb_req_message, data) + data.dsize;
-       r = ctdbd_allocate_pkt(ctdb, ctdb, CTDB_REQ_MESSAGE, 
-                              len, struct ctdb_req_message);
-       CTDB_NO_MEMORY(ctdb, r);
-
-       r->hdr.destnode  = pnn;
-       r->srvid         = srvid;
-       r->datalen       = data.dsize;
-       memcpy(&r->data[0], data.dptr, data.dsize);
-       
-       res = ctdb_client_queue_pkt(ctdb, &r->hdr);
-       if (res != 0) {
-               return res;
-       }
-
-       talloc_free(r);
-       return 0;
-}
-
-
-/*
-  cancel a ctdb_fetch_lock operation, releasing the lock
- */
-static int fetch_lock_destructor(struct ctdb_record_handle *h)
-{
-       ctdb_ltdb_unlock(h->ctdb_db, h->key);
-       return 0;
-}
-
-/*
-  force the migration of a record to this node
- */
-static int ctdb_client_force_migration(struct ctdb_db_context *ctdb_db, TDB_DATA key)
-{
-       struct ctdb_call call;
-       ZERO_STRUCT(call);
-       call.call_id = CTDB_NULL_FUNC;
-       call.key = key;
-       call.flags = CTDB_IMMEDIATE_MIGRATION;
-       return ctdb_call(ctdb_db, &call);
-}
-
-/*
-  get a lock on a record, and return the records data. Blocks until it gets the lock
- */
-struct ctdb_record_handle *ctdb_fetch_lock(struct ctdb_db_context *ctdb_db, TALLOC_CTX *mem_ctx, 
-                                          TDB_DATA key, TDB_DATA *data)
-{
-       int ret;
-       struct ctdb_record_handle *h;
-
-       /*
-         procedure is as follows:
-
-         1) get the chain lock. 
-         2) check if we are dmaster
-         3) if we are the dmaster then return handle 
-         4) if not dmaster then ask ctdb daemon to make us dmaster, and wait for
-            reply from ctdbd
-         5) when we get the reply, goto (1)
-        */
-
-       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;
-
-       DEBUG(DEBUG_DEBUG,("ctdb_fetch_lock: key=%*.*s\n", (int)key.dsize, (int)key.dsize, 
-                (const char *)key.dptr));
-
-again:
-       /* step 1 - get the chain lock */
-       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;
-       }
-
-       DEBUG(DEBUG_DEBUG,("ctdb_fetch_lock: got chain lock\n"));
-
-       talloc_set_destructor(h, fetch_lock_destructor);
-
-       ret = ctdb_ltdb_fetch(ctdb_db, key, &h->header, h, data);
-
-       /* when torturing, ensure we test the remote path */
-       if ((ctdb_db->ctdb->flags & CTDB_FLAG_TORTURE) &&
-           random() % 5 == 0) {
-               h->header.dmaster = (uint32_t)-1;
-       }
 
 
-       DEBUG(DEBUG_DEBUG,("ctdb_fetch_lock: done local fetch\n"));
-
-       if (ret != 0 || h->header.dmaster != ctdb_db->ctdb->pnn) {
-               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;
-       }
-
-       DEBUG(DEBUG_DEBUG,("ctdb_fetch_lock: we are dmaster - done\n"));
-       return h;
-}
-
-/*
-  store some data to the record that was locked with ctdb_fetch_lock()
-*/
-int ctdb_record_store(struct ctdb_record_handle *h, TDB_DATA data)
-{
-       if (h->ctdb_db->persistent) {
-               DEBUG(DEBUG_ERR, (__location__ " ctdb_record_store prohibited for persistent dbs\n"));
-               return -1;
-       }
-
-       return ctdb_ltdb_store(h->ctdb_db, h->key, &h->header, data);
-}
-
 /*
   non-locking fetch of a record
  */
@@ -679,242 +59,8 @@ int ctdb_fetch(struct ctdb_db_context *ctdb_db, TALLOC_CTX *mem_ctx,
 
 
 
-/*
-   called when a control completes or timesout to invoke the callback
-   function the user provided
-*/
-static void invoke_control_callback(struct event_context *ev, struct timed_event *te, 
-       struct timeval t, void *private_data)
-{
-       struct ctdb_client_control_state *state;
-       TALLOC_CTX *tmp_ctx = talloc_new(NULL);
-       int ret;
-
-       state = talloc_get_type(private_data, struct ctdb_client_control_state);
-       talloc_steal(tmp_ctx, state);
-
-       ret = ctdb_control_recv(state->ctdb, state, state,
-                       NULL, 
-                       NULL, 
-                       NULL);
-
-       talloc_free(tmp_ctx);
-}
-
-/*
-  called when a CTDB_REPLY_CONTROL packet comes in in the client
-
-  This packet comes in response to a CTDB_REQ_CONTROL request packet. It
-  contains any reply data from the control
-*/
-static void ctdb_client_reply_control(struct ctdb_context *ctdb, 
-                                     struct ctdb_req_header *hdr)
-{
-       struct ctdb_reply_control *c = (struct ctdb_reply_control *)hdr;
-       struct ctdb_client_control_state *state;
 
-       state = ctdb_reqid_find(ctdb, hdr->reqid, struct ctdb_client_control_state);
-       if (state == NULL) {
-               DEBUG(DEBUG_ERR,(__location__ " reqid %u not found\n", hdr->reqid));
-               return;
-       }
 
-       if (hdr->reqid != state->reqid) {
-               /* we found a record  but it was the wrong one */
-               DEBUG(DEBUG_ERR, ("Dropped orphaned reply control with reqid:%u\n",hdr->reqid));
-               return;
-       }
-
-       state->outdata.dptr = c->data;
-       state->outdata.dsize = c->datalen;
-       state->status = c->status;
-       if (c->errorlen) {
-               state->errormsg = talloc_strndup(state, 
-                                                (char *)&c->data[c->datalen], 
-                                                c->errorlen);
-       }
-
-       /* state->outdata now uses resources from c so we dont want c
-          to just dissappear from under us while state is still alive
-       */
-       talloc_steal(state, c);
-
-       state->state = CTDB_CONTROL_DONE;
-
-       /* if we had a callback registered for this control, pull the response
-          and call the callback.
-       */
-       if (state->async.fn) {
-               event_add_timed(ctdb->ev, state, timeval_zero(), invoke_control_callback, state);
-       }
-}
-
-
-/*
-  destroy a ctdb_control in client
-*/
-static int ctdb_control_destructor(struct ctdb_client_control_state *state)    
-{
-       ctdb_reqid_remove(state->ctdb, state->reqid);
-       return 0;
-}
-
-
-/* time out handler for ctdb_control */
-static void control_timeout_func(struct event_context *ev, struct timed_event *te, 
-       struct timeval t, void *private_data)
-{
-       struct ctdb_client_control_state *state = talloc_get_type(private_data, struct ctdb_client_control_state);
-
-       DEBUG(DEBUG_ERR,(__location__ " control timed out. reqid:%u opcode:%u "
-                        "dstnode:%u\n", state->reqid, state->c->opcode,
-                        state->c->hdr.destnode));
-
-       state->state = CTDB_CONTROL_TIMEOUT;
-
-       /* if we had a callback registered for this control, pull the response
-          and call the callback.
-       */
-       if (state->async.fn) {
-               event_add_timed(state->ctdb->ev, state, timeval_zero(), invoke_control_callback, state);
-       }
-}
-
-/* async version of send control request */
-struct ctdb_client_control_state *ctdb_control_send(struct ctdb_context *ctdb, 
-               uint32_t destnode, uint64_t srvid, 
-               uint32_t opcode, uint32_t flags, TDB_DATA data, 
-               TALLOC_CTX *mem_ctx,
-               struct timeval *timeout,
-               char **errormsg)
-{
-       struct ctdb_client_control_state *state;
-       size_t len;
-       struct ctdb_req_control *c;
-       int ret;
-
-       if (errormsg) {
-               *errormsg = NULL;
-       }
-
-       /* if the domain socket is not yet open, open it */
-       if (ctdb->daemon.sd==-1) {
-               ctdb_socket_connect(ctdb);
-       }
-
-       state = talloc_zero(mem_ctx, struct ctdb_client_control_state);
-       CTDB_NO_MEMORY_NULL(ctdb, state);
-
-       state->ctdb       = ctdb;
-       state->reqid      = ctdb_reqid_new(ctdb, state);
-       state->state      = CTDB_CONTROL_WAIT;
-       state->errormsg   = NULL;
-
-       talloc_set_destructor(state, ctdb_control_destructor);
-
-       len = offsetof(struct ctdb_req_control, data) + data.dsize;
-       c = ctdbd_allocate_pkt(ctdb, state, CTDB_REQ_CONTROL, 
-                              len, struct ctdb_req_control);
-       state->c            = c;        
-       CTDB_NO_MEMORY_NULL(ctdb, c);
-       c->hdr.reqid        = state->reqid;
-       c->hdr.destnode     = destnode;
-       c->opcode           = opcode;
-       c->client_id        = 0;
-       c->flags            = flags;
-       c->srvid            = srvid;
-       c->datalen          = data.dsize;
-       if (data.dsize) {
-               memcpy(&c->data[0], data.dptr, data.dsize);
-       }
-
-       /* timeout */
-       if (timeout && !timeval_is_zero(timeout)) {
-               event_add_timed(ctdb->ev, state, *timeout, control_timeout_func, state);
-       }
-
-       ret = ctdb_client_queue_pkt(ctdb, &(c->hdr));
-       if (ret != 0) {
-               talloc_free(state);
-               return NULL;
-       }
-
-       if (flags & CTDB_CTRL_FLAG_NOREPLY) {
-               talloc_free(state);
-               return NULL;
-       }
-
-       return state;
-}
-
-
-/* async version of receive control reply */
-int ctdb_control_recv(struct ctdb_context *ctdb, 
-               struct ctdb_client_control_state *state, 
-               TALLOC_CTX *mem_ctx,
-               TDB_DATA *outdata, int32_t *status, char **errormsg)
-{
-       TALLOC_CTX *tmp_ctx;
-
-       if (status != NULL) {
-               *status = -1;
-       }
-       if (errormsg != NULL) {
-               *errormsg = NULL;
-       }
-
-       if (state == NULL) {
-               return -1;
-       }
-
-       /* prevent double free of state */
-       tmp_ctx = talloc_new(ctdb);
-       talloc_steal(tmp_ctx, state);
-
-       /* loop one event at a time until we either timeout or the control
-          completes.
-       */
-       while (state->state == CTDB_CONTROL_WAIT) {
-               event_loop_once(ctdb->ev);
-       }
-
-       if (state->state != CTDB_CONTROL_DONE) {
-               DEBUG(DEBUG_ERR,(__location__ " ctdb_control_recv failed\n"));
-               if (state->async.fn) {
-                       state->async.fn(state);
-               }
-               talloc_free(tmp_ctx);
-               return -1;
-       }
-
-       if (state->errormsg) {
-               DEBUG(DEBUG_ERR,("ctdb_control error: '%s'\n", state->errormsg));
-               if (errormsg) {
-                       (*errormsg) = talloc_move(mem_ctx, &state->errormsg);
-               }
-               if (state->async.fn) {
-                       state->async.fn(state);
-               }
-               talloc_free(tmp_ctx);
-               return -1;
-       }
-
-       if (outdata) {
-               *outdata = state->outdata;
-               outdata->dptr = talloc_memdup(mem_ctx, outdata->dptr, outdata->dsize);
-       }
-
-       if (status) {
-               *status = state->status;
-       }
-
-       if (state->async.fn) {
-               state->async.fn(state);
-       }
-
-       talloc_free(tmp_ctx);
-       return 0;
-}
 
 
 
@@ -931,9 +77,13 @@ int ctdb_control(struct ctdb_context *ctdb, uint32_t destnode, uint64_t srvid,
 {
        struct ctdb_client_control_state *state;
 
-       state = ctdb_control_send(ctdb, destnode, srvid, opcode, 
-                       flags, data, mem_ctx,
-                       timeout, errormsg);
+       state = ctdb_control_send(ctdb, destnode, srvid, opcode, 
+                       flags, data, mem_ctx,
+                       errormsg);
+       if (state != NULL && timeout && !timeval_is_zero(timeout)) {
+               event_add_timed(ctdb->ev, state, *timeout, ctdb_control_timeout_func, state);
+       }
+
        return ctdb_control_recv(ctdb, state, mem_ctx, outdata, status, 
                        errormsg);
 }
@@ -1002,11 +152,14 @@ int ctdb_ctrl_shutdown(struct ctdb_context *ctdb, struct timeval timeout, uint32
 
        state = ctdb_control_send(ctdb, destnode, 0, 
                           CTDB_CONTROL_SHUTDOWN, 0, tdb_null, 
-                          NULL, &timeout, NULL);
+                          NULL, NULL);
        if (state == NULL) {
                DEBUG(DEBUG_ERR,(__location__ " ctdb_control for shutdown failed\n"));
                return -1;
        }
+       if (!timeval_is_zero(&timeout)) {
+               event_add_timed(ctdb->ev, state, timeout, ctdb_control_timeout_func, state);
+       }
 
        return 0;
 }
@@ -1056,9 +209,17 @@ int ctdb_ctrl_getvnnmap(struct ctdb_context *ctdb, struct timeval timeout, uint3
 struct ctdb_client_control_state *
 ctdb_ctrl_getrecmode_send(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx, struct timeval timeout, uint32_t destnode)
 {
-       return ctdb_control_send(ctdb, destnode, 0, 
+       struct ctdb_client_control_state *state;
+
+       state = ctdb_control_send(ctdb, destnode, 0, 
                           CTDB_CONTROL_GET_RECMODE, 0, tdb_null, 
-                          mem_ctx, &timeout, NULL);
+                          mem_ctx, NULL);
+
+       if (state != NULL && !timeval_is_zero(&timeout)) {
+               event_add_timed(ctdb->ev, state, timeout, ctdb_control_timeout_func, state);
+       }
+
+       return state;
 }
 
 int ctdb_ctrl_getrecmode_recv(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx, struct ctdb_client_control_state *state, uint32_t *recmode)
@@ -1122,9 +283,16 @@ struct ctdb_client_control_state *
 ctdb_ctrl_getrecmaster_send(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx, 
                        struct timeval timeout, uint32_t destnode)
 {
-       return ctdb_control_send(ctdb, destnode, 0, 
+       struct ctdb_client_control_state *state;
+
+       state = ctdb_control_send(ctdb, destnode, 0, 
                           CTDB_CONTROL_GET_RECMASTER, 0, tdb_null, 
-                          mem_ctx, &timeout, NULL);
+                          mem_ctx, NULL);
+       if (state != NULL && !timeval_is_zero(&timeout)) {
+               event_add_timed(ctdb->ev, state, timeout, ctdb_control_timeout_func, state);
+       }
+
+       return state;
 }
 
 int ctdb_ctrl_getrecmaster_recv(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx, struct ctdb_client_control_state *state, uint32_t *recmaster)
@@ -1351,7 +519,11 @@ struct ctdb_client_control_state *ctdb_ctrl_pulldb_send(
 
        state = ctdb_control_send(ctdb, destnode, 0, 
                                  CTDB_CONTROL_PULL_DB, 0, indata, 
-                                 mem_ctx, &timeout, NULL);
+                                 mem_ctx, NULL);
+       if (state != NULL && !timeval_is_zero(&timeout)) {
+               event_add_timed(ctdb->ev, state, timeout, ctdb_control_timeout_func, state);
+       }
+
        talloc_free(pull);
 
        return state;
@@ -1444,25 +616,38 @@ int ctdb_ctrl_getdbpath(struct ctdb_context *ctdb, struct timeval timeout, uint3
                   const char **path)
 {
        int ret;
-       int32_t res;
-       TDB_DATA data;
+       const char *tmppath = NULL;
+       ctdb_handle *handle;
 
-       data.dptr = (uint8_t *)&dbid;
-       data.dsize = sizeof(dbid);
+       handle = ctdb_getdbpath_send(ctdb, destnode, dbid, NULL, NULL);
+       if (handle == NULL) {
+               DEBUG(DEBUG_ERR, (__location__  " Failed to send getdbpath control\n"));
+               return -1;
+       }
 
-       ret = ctdb_control(ctdb, destnode, 0, 
-                          CTDB_CONTROL_GETDBPATH, 0, data, 
-                          mem_ctx, &data, &res, &timeout, NULL);
-       if (ret != 0 || res != 0) {
+       if (!timeval_is_zero(&timeout)) {
+               event_add_timed(ctdb->ev, handle, timeout, ctdb_control_timeout_func, handle);
+       }
+
+       ret = ctdb_getdbpath_recv(ctdb, handle, &tmppath);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,(__location__ " ctdb control for getdbpath failed\n"));
+               if (tmppath != NULL) {
+                       talloc_free(discard_const(tmppath));
+               }
                return -1;
        }
 
-       (*path) = talloc_strndup(mem_ctx, (const char *)data.dptr, data.dsize);
-       if ((*path) == NULL) {
+       if (tmppath == NULL) {
                return -1;
        }
 
-       talloc_free(data.dptr);
+       *path = talloc_strdup(mem_ctx, (const char *)tmppath);
+       talloc_free(discard_const(tmppath));
+
+       if (*path == NULL) {
+               return -1;
+       }
 
        return 0;
 }
@@ -1538,22 +723,26 @@ int ctdb_ctrl_getdbhealth(struct ctdb_context *ctdb,
 /*
   create a database
  */
-int ctdb_ctrl_createdb(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, 
-                      TALLOC_CTX *mem_ctx, const char *name, bool persistent)
+int ctdb_ctrl_createdb(struct ctdb_context *ctdb, struct timeval timeout,
+                       uint32_t destnode, 
+                       const char *name, bool persistent)
 {
        int ret;
-       int32_t res;
-       TDB_DATA data;
+       ctdb_handle *handle;
 
-       data.dptr = discard_const(name);
-       data.dsize = strlen(name)+1;
+       handle = ctdb_createdb_send(ctdb, destnode, name, persistent, 0, NULL, NULL);
+       if (handle == NULL) {
+               DEBUG(DEBUG_ERR, (__location__  " Failed to send createdb control\n"));
+               return -1;
+       }
 
-       ret = ctdb_control(ctdb, destnode, 0, 
-                          persistent?CTDB_CONTROL_DB_ATTACH_PERSISTENT:CTDB_CONTROL_DB_ATTACH, 
-                          0, data, 
-                          mem_ctx, &data, &res, &timeout, NULL);
+       if (!timeval_is_zero(&timeout)) {
+               event_add_timed(ctdb->ev, handle, timeout, ctdb_control_timeout_func, handle);
+       }
 
-       if (ret != 0 || res != 0) {
+       ret = ctdb_createdb_recv(ctdb, handle, NULL);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,(__location__ " ctdb control for createdb failed\n"));
                return -1;
        }
 
@@ -1658,132 +847,6 @@ int ctdb_statistics_reset(struct ctdb_context *ctdb, uint32_t destnode)
        return 0;
 }
 
-/*
-  this is the dummy null procedure that all databases support
-*/
-static int ctdb_null_func(struct ctdb_call_info *call)
-{
-       return 0;
-}
-
-/*
-  this is a plain fetch procedure that all databases support
-*/
-static int ctdb_fetch_func(struct ctdb_call_info *call)
-{
-       call->reply_data = &call->record_data;
-       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_db;
-       TDB_DATA data;
-       int ret;
-       int32_t res;
-
-       ctdb_db = ctdb_db_handle(ctdb, name);
-       if (ctdb_db) {
-               return ctdb_db;
-       }
-
-       ctdb_db = talloc_zero(ctdb, struct ctdb_db_context);
-       CTDB_NO_MEMORY_NULL(ctdb, ctdb_db);
-
-       ctdb_db->ctdb = ctdb;
-       ctdb_db->db_name = talloc_strdup(ctdb_db, name);
-       CTDB_NO_MEMORY_NULL(ctdb, ctdb_db->db_name);
-
-       data.dptr = discard_const(name);
-       data.dsize = strlen(name)+1;
-
-       /* tell ctdb daemon to attach */
-       ret = ctdb_control(ctdb, CTDB_CURRENT_NODE, tdb_flags, 
-                          persistent?CTDB_CONTROL_DB_ATTACH_PERSISTENT:CTDB_CONTROL_DB_ATTACH,
-                          0, data, ctdb_db, &data, &res, NULL, NULL);
-       if (ret != 0 || res != 0 || data.dsize != sizeof(uint32_t)) {
-               DEBUG(DEBUG_ERR,("Failed to attach to database '%s'\n", name));
-               talloc_free(ctdb_db);
-               return NULL;
-       }
-       
-       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);
-       if (ret != 0) {
-               DEBUG(DEBUG_ERR,("Failed to get dbpath for database '%s'\n", name));
-               talloc_free(ctdb_db);
-               return NULL;
-       }
-
-       tdb_flags = persistent?TDB_DEFAULT:TDB_NOSYNC;
-       if (!ctdb->do_setsched) {
-               tdb_flags |= TDB_NOMMAP;
-       }
-       tdb_flags |= TDB_DISALLOW_NESTING;
-
-       ctdb_db->ltdb = tdb_wrap_open(ctdb, ctdb_db->db_path, 0, tdb_flags, O_RDWR, 0);
-       if (ctdb_db->ltdb == NULL) {
-               ctdb_set_error(ctdb, "Failed to open tdb '%s'\n", ctdb_db->db_path);
-               talloc_free(ctdb_db);
-               return NULL;
-       }
-
-       ctdb_db->persistent = persistent;
-
-       DLIST_ADD(ctdb->db_list, ctdb_db);
-
-       /* 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);
-
-       return ctdb_db;
-}
-
-
-/*
-  setup a call for a database
- */
-int ctdb_set_call(struct ctdb_db_context *ctdb_db, ctdb_fn_t fn, uint32_t id)
-{
-       struct ctdb_registered_call *call;
-
-#if 0
-       TDB_DATA data;
-       int32_t status;
-       struct ctdb_control_set_call c;
-       int ret;
-
-       /* this is no longer valid with the separate daemon architecture */
-       c.db_id = ctdb_db->db_id;
-       c.fn    = fn;
-       c.id    = id;
-
-       data.dptr = (uint8_t *)&c;
-       data.dsize = sizeof(c);
-
-       ret = ctdb_control(ctdb_db->ctdb, CTDB_CURRENT_NODE, 0, CTDB_CONTROL_SET_CALL, 0,
-                          data, NULL, NULL, &status, NULL, NULL);
-       if (ret != 0 || status != 0) {
-               DEBUG(DEBUG_ERR,("ctdb_set_call failed for call %u\n", id));
-               return -1;
-       }
-#endif
-
-       /* also register locally */
-       call = talloc(ctdb_db, struct ctdb_registered_call);
-       call->fn = fn;
-       call->id = id;
-
-       DLIST_ADD(ctdb_db->calls, call);        
-       return 0;
-}
-
-
 struct traverse_state {
        bool done;
        uint32_t count;
@@ -1866,7 +929,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_remove_message_handler(ctdb_db->ctdb, srvid);
                return -1;
        }
 
@@ -1874,7 +937,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_remove_message_handler(ctdb_db->ctdb, srvid);
        if (ret != 0) {
                DEBUG(DEBUG_ERR,("Failed to remove ctdb_traverse handler\n"));
                return -1;
@@ -1887,7 +950,7 @@ int ctdb_traverse(struct ctdb_db_context *ctdb_db, ctdb_traverse_func fn, void *
 /*
   called on each key during a catdb
  */
-static int dumpdb_fn(struct ctdb_context *ctdb, TDB_DATA key, TDB_DATA data, void *p)
+int ctdb_dumpdb_record(struct ctdb_context *ctdb, TDB_DATA key, TDB_DATA data, void *p)
 {
        int i;
        FILE *f = (FILE *)p;
@@ -1906,7 +969,7 @@ static int dumpdb_fn(struct ctdb_context *ctdb, TDB_DATA key, TDB_DATA data, voi
        fprintf(f, "dmaster: %u\n", h->dmaster);
        fprintf(f, "rsn: %llu\n", (unsigned long long)h->rsn);
 
-       fprintf(f, "data(%u) = \"", (unsigned)data.dsize);
+       fprintf(f, "data(%u) = \"", (unsigned)(data.dsize - sizeof(*h)));
        for (i=sizeof(*h);i<data.dsize;i++) {
                if (ISASCII(data.dptr[i])) {
                        fprintf(f, "%c", data.dptr[i]);
@@ -1926,7 +989,7 @@ static int dumpdb_fn(struct ctdb_context *ctdb, TDB_DATA key, TDB_DATA data, voi
  */
 int ctdb_dump_db(struct ctdb_db_context *ctdb_db, FILE *f)
 {
-       return ctdb_traverse(ctdb_db, dumpdb_fn, f);
+       return ctdb_traverse(ctdb_db, ctdb_dumpdb_record, f);
 }
 
 /*
@@ -1957,9 +1020,16 @@ int ctdb_ctrl_getpid(struct ctdb_context *ctdb, struct timeval timeout, uint32_t
 struct ctdb_client_control_state *
 ctdb_ctrl_freeze_send(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx, struct timeval timeout, uint32_t destnode, uint32_t priority)
 {
-       return ctdb_control_send(ctdb, destnode, priority, 
+       struct ctdb_client_control_state *state;
+
+       state = ctdb_control_send(ctdb, destnode, priority, 
                           CTDB_CONTROL_FREEZE, 0, tdb_null, 
-                          mem_ctx, &timeout, NULL);
+                          mem_ctx, NULL);
+       if (state != NULL && !timeval_is_zero(&timeout)) {
+               event_add_timed(ctdb->ev, state, timeout, ctdb_control_timeout_func, state);
+       }
+
+       return state;
 }
 
 /* 
@@ -2039,17 +1109,26 @@ int ctdb_ctrl_thaw(struct ctdb_context *ctdb, struct timeval timeout, uint32_t d
 int ctdb_ctrl_getpnn(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode)
 {
        int ret;
-       int32_t res;
+       uint32_t pnn;
+       ctdb_handle *handle;
 
-       ret = ctdb_control(ctdb, destnode, 0, 
-                          CTDB_CONTROL_GET_PNN, 0, tdb_null, 
-                          NULL, NULL, &res, &timeout, NULL);
+       handle = ctdb_getpnn_send(ctdb, destnode, NULL, NULL);
+       if (handle == NULL) {
+               DEBUG(DEBUG_ERR, (__location__ " Failed to send getpnn control\n"));
+               return -1;
+       }
+
+       if (!timeval_is_zero(&timeout)) {
+               event_add_timed(ctdb->ev, handle, timeout, ctdb_control_timeout_func, handle);
+       }
+
+       ret = ctdb_getpnn_recv(ctdb, handle, &pnn);
        if (ret != 0) {
-               DEBUG(DEBUG_ERR,(__location__ " ctdb_control for getpnn failed\n"));
+               DEBUG(DEBUG_ERR,(__location__ " ctdb control for getpnn failed\n"));
                return -1;
        }
 
-       return res;
+       return pnn;
 }
 
 /*
@@ -2318,16 +1397,18 @@ int ctdb_ctrl_list_tunables(struct ctdb_context *ctdb,
 }
 
 
-int ctdb_ctrl_get_public_ips(struct ctdb_context *ctdb, 
-                       struct timeval timeout, uint32_t destnode, 
-                       TALLOC_CTX *mem_ctx, struct ctdb_all_public_ips **ips)
+int ctdb_ctrl_get_public_ips_flags(struct ctdb_context *ctdb,
+                                  struct timeval timeout, uint32_t destnode,
+                                  TALLOC_CTX *mem_ctx,
+                                  uint32_t flags,
+                                  struct ctdb_all_public_ips **ips)
 {
        int ret;
        TDB_DATA outdata;
        int32_t res;
 
        ret = ctdb_control(ctdb, destnode, 0, 
-                          CTDB_CONTROL_GET_PUBLIC_IPS, 0, tdb_null, 
+                          CTDB_CONTROL_GET_PUBLIC_IPS, flags, tdb_null,
                           mem_ctx, &outdata, &res, &timeout, NULL);
        if (ret == 0 && res == -1) {
                DEBUG(DEBUG_ERR,(__location__ " ctdb_control to get public ips failed, falling back to ipv4-only version\n"));
@@ -2344,6 +1425,16 @@ int ctdb_ctrl_get_public_ips(struct ctdb_context *ctdb,
        return 0;
 }
 
+int ctdb_ctrl_get_public_ips(struct ctdb_context *ctdb,
+                            struct timeval timeout, uint32_t destnode,
+                            TALLOC_CTX *mem_ctx,
+                            struct ctdb_all_public_ips **ips)
+{
+       return ctdb_ctrl_get_public_ips_flags(ctdb, timeout,
+                                             destnode, mem_ctx,
+                                             0, ips);
+}
+
 int ctdb_ctrl_get_public_ipsv4(struct ctdb_context *ctdb, 
                        struct timeval timeout, uint32_t destnode, 
                        TALLOC_CTX *mem_ctx, struct ctdb_all_public_ips **ips)
@@ -2377,6 +1468,162 @@ int ctdb_ctrl_get_public_ipsv4(struct ctdb_context *ctdb,
        return 0;
 }
 
+int ctdb_ctrl_get_public_ip_info(struct ctdb_context *ctdb,
+                                struct timeval timeout, uint32_t destnode,
+                                TALLOC_CTX *mem_ctx,
+                                const ctdb_sock_addr *addr,
+                                struct ctdb_control_public_ip_info **_info)
+{
+       int ret;
+       TDB_DATA indata;
+       TDB_DATA outdata;
+       int32_t res;
+       struct ctdb_control_public_ip_info *info;
+       uint32_t len;
+       uint32_t i;
+
+       indata.dptr = discard_const_p(uint8_t, addr);
+       indata.dsize = sizeof(*addr);
+
+       ret = ctdb_control(ctdb, destnode, 0,
+                          CTDB_CONTROL_GET_PUBLIC_IP_INFO, 0, indata,
+                          mem_ctx, &outdata, &res, &timeout, NULL);
+       if (ret != 0 || res != 0) {
+               DEBUG(DEBUG_ERR,(__location__ " ctdb_control for get public ip info "
+                               "failed ret:%d res:%d\n",
+                               ret, res));
+               return -1;
+       }
+
+       len = offsetof(struct ctdb_control_public_ip_info, ifaces);
+       if (len > outdata.dsize) {
+               DEBUG(DEBUG_ERR,(__location__ " ctdb_control for get public ip info "
+                               "returned invalid data with size %u > %u\n",
+                               (unsigned int)outdata.dsize,
+                               (unsigned int)len));
+               dump_data(DEBUG_DEBUG, outdata.dptr, outdata.dsize);
+               return -1;
+       }
+
+       info = (struct ctdb_control_public_ip_info *)outdata.dptr;
+       len += info->num*sizeof(struct ctdb_control_iface_info);
+
+       if (len > outdata.dsize) {
+               DEBUG(DEBUG_ERR,(__location__ " ctdb_control for get public ip info "
+                               "returned invalid data with size %u > %u\n",
+                               (unsigned int)outdata.dsize,
+                               (unsigned int)len));
+               dump_data(DEBUG_DEBUG, outdata.dptr, outdata.dsize);
+               return -1;
+       }
+
+       /* make sure we null terminate the returned strings */
+       for (i=0; i < info->num; i++) {
+               info->ifaces[i].name[CTDB_IFACE_SIZE] = '\0';
+       }
+
+       *_info = (struct ctdb_control_public_ip_info *)talloc_memdup(mem_ctx,
+                                                               outdata.dptr,
+                                                               outdata.dsize);
+       talloc_free(outdata.dptr);
+       if (*_info == NULL) {
+               DEBUG(DEBUG_ERR,(__location__ " ctdb_control for get public ip info "
+                               "talloc_memdup size %u failed\n",
+                               (unsigned int)outdata.dsize));
+               return -1;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_get_ifaces(struct ctdb_context *ctdb,
+                        struct timeval timeout, uint32_t destnode,
+                        TALLOC_CTX *mem_ctx,
+                        struct ctdb_control_get_ifaces **_ifaces)
+{
+       int ret;
+       TDB_DATA outdata;
+       int32_t res;
+       struct ctdb_control_get_ifaces *ifaces;
+       uint32_t len;
+       uint32_t i;
+
+       ret = ctdb_control(ctdb, destnode, 0,
+                          CTDB_CONTROL_GET_IFACES, 0, tdb_null,
+                          mem_ctx, &outdata, &res, &timeout, NULL);
+       if (ret != 0 || res != 0) {
+               DEBUG(DEBUG_ERR,(__location__ " ctdb_control for get ifaces "
+                               "failed ret:%d res:%d\n",
+                               ret, res));
+               return -1;
+       }
+
+       len = offsetof(struct ctdb_control_get_ifaces, ifaces);
+       if (len > outdata.dsize) {
+               DEBUG(DEBUG_ERR,(__location__ " ctdb_control for get ifaces "
+                               "returned invalid data with size %u > %u\n",
+                               (unsigned int)outdata.dsize,
+                               (unsigned int)len));
+               dump_data(DEBUG_DEBUG, outdata.dptr, outdata.dsize);
+               return -1;
+       }
+
+       ifaces = (struct ctdb_control_get_ifaces *)outdata.dptr;
+       len += ifaces->num*sizeof(struct ctdb_control_iface_info);
+
+       if (len > outdata.dsize) {
+               DEBUG(DEBUG_ERR,(__location__ " ctdb_control for get ifaces "
+                               "returned invalid data with size %u > %u\n",
+                               (unsigned int)outdata.dsize,
+                               (unsigned int)len));
+               dump_data(DEBUG_DEBUG, outdata.dptr, outdata.dsize);
+               return -1;
+       }
+
+       /* make sure we null terminate the returned strings */
+       for (i=0; i < ifaces->num; i++) {
+               ifaces->ifaces[i].name[CTDB_IFACE_SIZE] = '\0';
+       }
+
+       *_ifaces = (struct ctdb_control_get_ifaces *)talloc_memdup(mem_ctx,
+                                                                 outdata.dptr,
+                                                                 outdata.dsize);
+       talloc_free(outdata.dptr);
+       if (*_ifaces == NULL) {
+               DEBUG(DEBUG_ERR,(__location__ " ctdb_control for get ifaces "
+                               "talloc_memdup size %u failed\n",
+                               (unsigned int)outdata.dsize));
+               return -1;
+       }
+
+       return 0;
+}
+
+int ctdb_ctrl_set_iface_link(struct ctdb_context *ctdb,
+                            struct timeval timeout, uint32_t destnode,
+                            TALLOC_CTX *mem_ctx,
+                            const struct ctdb_control_iface_info *info)
+{
+       int ret;
+       TDB_DATA indata;
+       int32_t res;
+
+       indata.dptr = discard_const_p(uint8_t, info);
+       indata.dsize = sizeof(*info);
+
+       ret = ctdb_control(ctdb, destnode, 0,
+                          CTDB_CONTROL_SET_IFACE_LINK_STATE, 0, indata,
+                          mem_ctx, NULL, &res, &timeout, NULL);
+       if (ret != 0 || res != 0) {
+               DEBUG(DEBUG_ERR,(__location__ " ctdb_control for set iface link "
+                               "failed ret:%d res:%d\n",
+                               ret, res));
+               return -1;
+       }
+
+       return 0;
+}
+
 /*
   set/clear the permanent disabled bit on a remote node
  */
@@ -2730,37 +1977,6 @@ int ctdb_ctrl_get_server_id_list(struct ctdb_context *ctdb,
        return 0;
 }
 
-/*
-  initialise the ctdb daemon for client applications
-
-  NOTE: In current code the daemon does not fork. This is for testing purposes only
-  and to simplify the code.
-*/
-struct ctdb_context *ctdb_init(struct event_context *ev)
-{
-       int ret;
-       struct ctdb_context *ctdb;
-
-       ctdb = talloc_zero(ev, struct ctdb_context);
-       if (ctdb == NULL) {
-               DEBUG(DEBUG_ERR,(__location__ " talloc_zero failed.\n"));
-               return NULL;
-       }
-       ctdb->ev  = ev;
-       ctdb->idr = idr_init(ctdb);
-       CTDB_NO_MEMORY_NULL(ctdb, ctdb->idr);
-
-       ret = ctdb_set_socketname(ctdb, CTDB_PATH);
-       if (ret != 0) {
-               DEBUG(DEBUG_ERR,(__location__ " ctdb_set_socketname failed.\n"));
-               talloc_free(ctdb);
-               return NULL;
-       }
-
-       return ctdb;
-}
-
-
 /*
   set some ctdb flags
 */
@@ -2769,16 +1985,6 @@ void ctdb_set_flags(struct ctdb_context *ctdb, unsigned flags)
        ctdb->flags |= flags;
 }
 
-/*
-  setup the local socket name
-*/
-int ctdb_set_socketname(struct ctdb_context *ctdb, const char *socketname)
-{
-       ctdb->daemon.name = talloc_strdup(ctdb, socketname);
-       CTDB_NO_MEMORY(ctdb, ctdb->daemon.name);
-
-       return 0;
-}
 
 /*
   return the pnn of this node
@@ -2795,9 +2001,16 @@ uint32_t ctdb_get_pnn(struct ctdb_context *ctdb)
 struct ctdb_client_control_state *
 ctdb_ctrl_uptime_send(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx, struct timeval timeout, uint32_t destnode)
 {
-       return ctdb_control_send(ctdb, destnode, 0, 
+       struct ctdb_client_control_state *state;
+
+       state = ctdb_control_send(ctdb, destnode, 0, 
                           CTDB_CONTROL_UPTIME, 0, tdb_null, 
-                          mem_ctx, &timeout, NULL);
+                          mem_ctx, NULL);
+       if (state != NULL && !timeval_is_zero(&timeout)) {
+               event_add_timed(ctdb->ev, state, timeout, ctdb_control_timeout_func, state);
+       }
+
+       return state;
 }
 
 int ctdb_ctrl_uptime_recv(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx, struct ctdb_client_control_state *state, struct ctdb_uptime **uptime)
@@ -2959,12 +2172,15 @@ int ctdb_client_async_control(struct ctdb_context *ctdb,
                uint32_t pnn = nodes[j];
 
                state = ctdb_control_send(ctdb, pnn, srvid, opcode, 
-                                         0, data, async_data, &timeout, NULL);
+                                         0, data, async_data, NULL);
                if (state == NULL) {
                        DEBUG(DEBUG_ERR,(__location__ " Failed to call async control %u\n", (unsigned)opcode));
                        talloc_free(async_data);
                        return -1;
                }
+               if (!timeval_is_zero(&timeout)) {
+                       event_add_timed(ctdb->ev, state, timeout, ctdb_control_timeout_func, state);
+               }
                
                ctdb_client_async_add(async_data, state);
        }
@@ -3148,9 +2364,16 @@ ctdb_read_pnn_lock(int fd, int32_t pnn)
 struct ctdb_client_control_state *
 ctdb_ctrl_getcapabilities_send(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx, struct timeval timeout, uint32_t destnode)
 {
-       return ctdb_control_send(ctdb, destnode, 0, 
+       struct ctdb_client_control_state *state;
+
+       state = ctdb_control_send(ctdb, destnode, 0, 
                           CTDB_CONTROL_GET_CAPABILITIES, 0, tdb_null, 
-                          mem_ctx, &timeout, NULL);
+                          mem_ctx, NULL);
+       if (state != NULL && !timeval_is_zero(&timeout)) {
+               event_add_timed(ctdb->ev, state, timeout, ctdb_control_timeout_func, state);
+       }
+
+       return state;
 }
 
 int ctdb_ctrl_getcapabilities_recv(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx, struct ctdb_client_control_state *state, uint32_t *capabilities)
@@ -3666,11 +2889,6 @@ int switch_from_server_to_client(struct ctdb_context *ctdb)
        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) {
@@ -4037,3 +3255,24 @@ int ctdb_ctrl_get_db_priority(struct ctdb_context *ctdb, struct timeval timeout,
 
        return 0;
 }
+
+/* time out handler for ctdb_control */
+void ctdb_control_timeout_func(struct event_context *ev, struct timed_event *te, 
+       struct timeval t, void *private_data)
+{
+       struct ctdb_client_control_state *state = talloc_get_type(private_data, struct ctdb_client_control_state);
+
+       DEBUG(DEBUG_ERR,(__location__ " control timed out. reqid:%u opcode:%u "
+                        "dstnode:%u\n", state->reqid, state->c->opcode,
+                        state->c->hdr.destnode));
+
+       state->state = CTDB_CONTROL_TIMEOUT;
+
+       /* if we had a callback registered for this control, pull the response
+          and call the callback.
+       */
+       if (state->async.fn) {
+               event_add_timed(state->ctdb->ev, state, timeval_zero(), ctdb_invoke_control_callback, state);
+       }
+}
+