add a command to write a record to a persistent database
[metze/ctdb/wip.git] / tools / ctdb.c
index 2f78ebe76fbfc11cb8bae026ab00e31183eaf83f..b986f4e7ccb8111515f9c1ed72311074bc33f743 100644 (file)
@@ -19,7 +19,7 @@
 */
 
 #include "includes.h"
-#include "lib/events/events.h"
+#include "lib/tevent/tevent.h"
 #include "system/time.h"
 #include "system/filesys.h"
 #include "system/network.h"
 #include "popt.h"
 #include "cmdline.h"
 #include "../include/ctdb.h"
+#include "../include/ctdb_client.h"
 #include "../include/ctdb_private.h"
 #include "../common/rb_tree.h"
 #include "db_wrap.h"
 
-
 #define ERR_TIMEOUT    20      /* timed out trying to reach node */
 #define ERR_NONODE     21      /* node does not exist */
 #define ERR_DISNODE    22      /* node is disconnected */
 
+struct ctdb_connection *ctdb_connection;
+
 static void usage(void);
 
 static struct {
        int timelimit;
        uint32_t pnn;
        int machinereadable;
+       int verbose;
        int maxruntime;
 } options;
 
 #define TIMELIMIT() timeval_current_ofs(options.timelimit, 0)
+#define LONGTIMELIMIT() timeval_current_ofs(options.timelimit*10, 0)
 
 #ifdef CTDB_VERS
 static int control_version(struct ctdb_context *ctdb, int argc, const char **argv)
@@ -94,7 +98,7 @@ static void verify_node(struct ctdb_context *ctdb)
        /* verify we can access the node */
        ret = ctdb_ctrl_getpnn(ctdb, TIMELIMIT(), options.pnn);
        if (ret == -1) {
-               DEBUG(DEBUG_ERR,("Can not ban node. Node is not operational.\n"));
+               DEBUG(DEBUG_ERR,("Can not access node. Node is not operational.\n"));
                exit(10);
        }
 }
@@ -159,6 +163,7 @@ static void show_statistics(struct ctdb_statistics *s)
        int i;
        const char *prefix=NULL;
        int preflen=0;
+       int tmp, days, hours, minutes, seconds;
        const struct {
                const char *name;
                uint32_t offset;
@@ -167,6 +172,7 @@ static void show_statistics(struct ctdb_statistics *s)
                STATISTICS_FIELD(num_clients),
                STATISTICS_FIELD(frozen),
                STATISTICS_FIELD(recovering),
+               STATISTICS_FIELD(num_recoveries),
                STATISTICS_FIELD(client_packets_sent),
                STATISTICS_FIELD(client_packets_recv),
                STATISTICS_FIELD(node_packets_sent),
@@ -196,7 +202,19 @@ static void show_statistics(struct ctdb_statistics *s)
                STATISTICS_FIELD(memory_used),
                STATISTICS_FIELD(max_hop_count),
        };
+       tmp = s->statistics_current_time.tv_sec - s->statistics_start_time.tv_sec;
+       seconds = tmp%60;
+       tmp    /= 60;
+       minutes = tmp%60;
+       tmp    /= 60;
+       hours   = tmp%24;
+       tmp    /= 24;
+       days    = tmp;
+
        printf("CTDB version %u\n", CTDB_VERSION);
+       printf("Current time of statistics  :                %s", ctime(&s->statistics_current_time.tv_sec));
+       printf("Statistics collected since  : (%03d %02d:%02d:%02d) %s", days, hours, minutes, seconds, ctime(&s->statistics_start_time.tv_sec));
+
        for (i=0;i<ARRAY_SIZE(fields);i++) {
                if (strchr(fields[i].name, '.')) {
                        preflen = strcspn(fields[i].name, ".")+1;
@@ -219,6 +237,8 @@ static void show_statistics(struct ctdb_statistics *s)
        printf(" %-30s     %.6f sec\n", "max_call_latency", s->max_call_latency);
        printf(" %-30s     %.6f sec\n", "max_lockwait_latency", s->max_lockwait_latency);
        printf(" %-30s     %.6f sec\n", "max_childwrite_latency", s->max_childwrite_latency);
+       printf(" %-30s     %.6f sec\n", "max_childwrite_latency", s->max_childwrite_latency);
+
        talloc_free(tmp_ctx);
 }
 
@@ -319,7 +339,7 @@ static int control_uptime(struct ctdb_context *ctdb, int argc, const char **argv
        }
 
        if (options.machinereadable){
-               printf(":Current Node Time:Ctdb Start Time:Last Recovery Time:Last Recovery Duration:\n");
+               printf(":Current Node Time:Ctdb Start Time:Last Recovery/Failover Time:Last Recovery/IPFailover Duration:\n");
                printf(":%u:%u:%u:%lf\n",
                        (unsigned int)uptime->current_time.tv_sec,
                        (unsigned int)uptime->ctdbd_start_time.tv_sec,
@@ -330,7 +350,7 @@ static int control_uptime(struct ctdb_context *ctdb, int argc, const char **argv
                return 0;
        }
 
-       printf("Current time of node  : %s", ctime(&uptime->current_time.tv_sec));
+       printf("Current time of node          :                %s", ctime(&uptime->current_time.tv_sec));
 
        tmp = uptime->current_time.tv_sec - uptime->ctdbd_start_time.tv_sec;
        seconds = tmp%60;
@@ -340,7 +360,7 @@ static int control_uptime(struct ctdb_context *ctdb, int argc, const char **argv
        hours   = tmp%24;
        tmp    /= 24;
        days    = tmp;
-       printf("Ctdbd start time      : (%03d %02d:%02d:%02d) %s", days, hours, minutes, seconds, ctime(&uptime->ctdbd_start_time.tv_sec));
+       printf("Ctdbd start time              : (%03d %02d:%02d:%02d) %s", days, hours, minutes, seconds, ctime(&uptime->ctdbd_start_time.tv_sec));
 
        tmp = uptime->current_time.tv_sec - uptime->last_recovery_finished.tv_sec;
        seconds = tmp%60;
@@ -350,9 +370,9 @@ static int control_uptime(struct ctdb_context *ctdb, int argc, const char **argv
        hours   = tmp%24;
        tmp    /= 24;
        days    = tmp;
-       printf("Time of last recovery : (%03d %02d:%02d:%02d) %s", days, hours, minutes, seconds, ctime(&uptime->last_recovery_finished.tv_sec));
+       printf("Time of last recovery/failover: (%03d %02d:%02d:%02d) %s", days, hours, minutes, seconds, ctime(&uptime->last_recovery_finished.tv_sec));
        
-       printf("Duration of last recovery : %lf seconds\n",
+       printf("Duration of last recovery/failover: %lf seconds\n",
                timeval_delta(&uptime->last_recovery_finished,
                              &uptime->last_recovery_started));
 
@@ -364,11 +384,12 @@ static int control_uptime(struct ctdb_context *ctdb, int argc, const char **argv
  */
 static int control_pnn(struct ctdb_context *ctdb, int argc, const char **argv)
 {
-       int mypnn;
+       uint32_t mypnn;
+       bool ret;
 
-       mypnn = ctdb_ctrl_getpnn(ctdb, TIMELIMIT(), options.pnn);
-       if (mypnn == -1) {
-               DEBUG(DEBUG_ERR, ("Unable to get pnn from local node."));
+       ret = ctdb_getpnn(ctdb_connection, options.pnn, &mypnn);
+       if (!ret) {
+               DEBUG(DEBUG_ERR, ("Unable to get pnn from node."));
                return -1;
        }
 
@@ -503,18 +524,40 @@ static int control_status(struct ctdb_context *ctdb, int argc, const char **argv
        }
 
        if(options.machinereadable){
-               printf(":Node:IP:Disconnected:Banned:Disabled:Unhealthy:Stopped:\n");
+               printf(":Node:IP:Disconnected:Banned:Disabled:Unhealthy:Stopped:Inactive:PartiallyOnline:\n");
                for(i=0;i<nodemap->num;i++){
+                       int partially_online = 0;
+                       int j;
+
                        if (nodemap->nodes[i].flags & NODE_FLAGS_DELETED) {
                                continue;
                        }
-                       printf(":%d:%s:%d:%d:%d:%d:%d:\n", nodemap->nodes[i].pnn,
+                       if (nodemap->nodes[i].flags == 0) {
+                               struct ctdb_control_get_ifaces *ifaces;
+
+                               ret = ctdb_ctrl_get_ifaces(ctdb, TIMELIMIT(),
+                                                          nodemap->nodes[i].pnn,
+                                                          ctdb, &ifaces);
+                               if (ret == 0) {
+                                       for (j=0; j < ifaces->num; j++) {
+                                               if (ifaces->ifaces[j].link_state != 0) {
+                                                       continue;
+                                               }
+                                               partially_online = 1;
+                                               break;
+                                       }
+                                       talloc_free(ifaces);
+                               }
+                       }
+                       printf(":%d:%s:%d:%d:%d:%d:%d:%d:%d:\n", nodemap->nodes[i].pnn,
                                ctdb_addr_to_str(&nodemap->nodes[i].addr),
                               !!(nodemap->nodes[i].flags&NODE_FLAGS_DISCONNECTED),
                               !!(nodemap->nodes[i].flags&NODE_FLAGS_BANNED),
                               !!(nodemap->nodes[i].flags&NODE_FLAGS_PERMANENTLY_DISABLED),
                               !!(nodemap->nodes[i].flags&NODE_FLAGS_UNHEALTHY),
-                              !!(nodemap->nodes[i].flags&NODE_FLAGS_STOPPED));
+                              !!(nodemap->nodes[i].flags&NODE_FLAGS_STOPPED),
+                              !!(nodemap->nodes[i].flags&NODE_FLAGS_INACTIVE),
+                              partially_online);
                }
                return 0;
        }
@@ -531,6 +574,7 @@ static int control_status(struct ctdb_context *ctdb, int argc, const char **argv
                        { NODE_FLAGS_UNHEALTHY,             "UNHEALTHY" },
                        { NODE_FLAGS_DELETED,               "DELETED" },
                        { NODE_FLAGS_STOPPED,               "STOPPED" },
+                       { NODE_FLAGS_INACTIVE,              "INACTIVE" },
                };
                char *flags_str = NULL;
                int j;
@@ -538,6 +582,23 @@ static int control_status(struct ctdb_context *ctdb, int argc, const char **argv
                if (nodemap->nodes[i].flags & NODE_FLAGS_DELETED) {
                        continue;
                }
+               if (nodemap->nodes[i].flags == 0) {
+                       struct ctdb_control_get_ifaces *ifaces;
+
+                       ret = ctdb_ctrl_get_ifaces(ctdb, TIMELIMIT(),
+                                                  nodemap->nodes[i].pnn,
+                                                  ctdb, &ifaces);
+                       if (ret == 0) {
+                               for (j=0; j < ifaces->num; j++) {
+                                       if (ifaces->ifaces[j].link_state != 0) {
+                                               continue;
+                                       }
+                                       flags_str = talloc_strdup(ctdb, "PARTIALLYONLINE");
+                                       break;
+                               }
+                               talloc_free(ifaces);
+                       }
+               }
                for (j=0;j<ARRAY_SIZE(flag_names);j++) {
                        if (nodemap->nodes[i].flags & flag_names[j].flag) {
                                if (flags_str == NULL) {
@@ -677,15 +738,24 @@ static int control_natgwlist(struct ctdb_context *ctdb, int argc, const char **a
                i++;
        }               
 
-       /* print the natgw master
-        * we dont allow STOPPED or DELETED nodes to become the natgwmaster
+       /* pick a node to be natgwmaster
+        * we dont allow STOPPED, DELETED, BANNED or UNHEALTHY nodes to become the natgwmaster
         */
        for(i=0;i<nodemap->num;i++){
-               if (!(nodemap->nodes[i].flags & (NODE_FLAGS_DISCONNECTED|NODE_FLAGS_STOPPED|NODE_FLAGS_DELETED))) {
+               if (!(nodemap->nodes[i].flags & (NODE_FLAGS_DISCONNECTED|NODE_FLAGS_STOPPED|NODE_FLAGS_DELETED|NODE_FLAGS_BANNED|NODE_FLAGS_UNHEALTHY))) {
                        printf("%d %s\n", nodemap->nodes[i].pnn,ctdb_addr_to_str(&nodemap->nodes[i].addr));
                        break;
                }
        }
+       /* we couldnt find any healthy node, try unhealthy ones */
+       if (i == nodemap->num) {
+               for(i=0;i<nodemap->num;i++){
+                       if (!(nodemap->nodes[i].flags & (NODE_FLAGS_DISCONNECTED|NODE_FLAGS_STOPPED|NODE_FLAGS_DELETED))) {
+                               printf("%d %s\n", nodemap->nodes[i].pnn,ctdb_addr_to_str(&nodemap->nodes[i].addr));
+                               break;
+                       }
+               }
+       }
        /* unless all nodes are STOPPED, when we pick one anyway */
        if (i == nodemap->num) {
                for(i=0;i<nodemap->num;i++){
@@ -717,48 +787,141 @@ static int control_natgwlist(struct ctdb_context *ctdb, int argc, const char **a
        return 0;
 }
 
-
 /*
-  display the status of the monitoring scripts
+  display the status of the scripts for monitoring (or other events)
  */
-static int control_scriptstatus(struct ctdb_context *ctdb, int argc, const char **argv)
+static int control_one_scriptstatus(struct ctdb_context *ctdb,
+                                   enum ctdb_eventscript_call type)
 {
-       int i, ret;
-       struct ctdb_monitoring_wire *script_status;
+       struct ctdb_scripts_wire *script_status;
+       int ret, i;
 
-       ret = ctdb_ctrl_getscriptstatus(ctdb, TIMELIMIT(), options.pnn, ctdb, &script_status);
+       ret = ctdb_ctrl_getscriptstatus(ctdb, TIMELIMIT(), options.pnn, ctdb, type, &script_status);
        if (ret != 0) {
                DEBUG(DEBUG_ERR, ("Unable to get script status from node %u\n", options.pnn));
                return ret;
        }
 
-       printf("%d scripts were executed last monitoring cycle\n", script_status->num_scripts);
+       if (script_status == NULL) {
+               if (!options.machinereadable) {
+                       printf("%s cycle never run\n",
+                              ctdb_eventscript_call_names[type]);
+               }
+               return 0;
+       }
+
+       if (!options.machinereadable) {
+               printf("%d scripts were executed last %s cycle\n",
+                      script_status->num_scripts,
+                      ctdb_eventscript_call_names[type]);
+       }
        for (i=0; i<script_status->num_scripts; i++) {
-               if (script_status->scripts[i].disabled) {
-                       printf("%-20s Status:DISABLED\n",
-                               script_status->scripts[i].name);
+               const char *status = NULL;
+
+               switch (script_status->scripts[i].status) {
+               case -ETIME:
+                       status = "TIMEDOUT";
+                       break;
+               case -ENOEXEC:
+                       status = "DISABLED";
+                       break;
+               case 0:
+                       status = "OK";
+                       break;
+               default:
+                       if (script_status->scripts[i].status > 0)
+                               status = "ERROR";
+                       break;
+               }
+               if (options.machinereadable) {
+                       printf("%s:%s:%i:%s:%lu.%06lu:%lu.%06lu:%s:\n",
+                              ctdb_eventscript_call_names[type],
+                              script_status->scripts[i].name,
+                              script_status->scripts[i].status,
+                              status,
+                              (long)script_status->scripts[i].start.tv_sec,
+                              (long)script_status->scripts[i].start.tv_usec,
+                              (long)script_status->scripts[i].finished.tv_sec,
+                              (long)script_status->scripts[i].finished.tv_usec,
+                              script_status->scripts[i].output);
                        continue;
-               } 
-               printf("%-20s Status:%s    ",
-                       script_status->scripts[i].name,
-                       script_status->scripts[i].timedout?"TIMEDOUT":script_status->scripts[i].status==0?"OK":"ERROR");
-               if (script_status->scripts[i].timedout == 0) {
+               }
+               if (status)
+                       printf("%-20s Status:%s    ",
+                              script_status->scripts[i].name, status);
+               else
+                       /* Some other error, eg from stat. */
+                       printf("%-20s Status:CANNOT RUN (%s)",
+                              script_status->scripts[i].name,
+                              strerror(-script_status->scripts[i].status));
+
+               if (script_status->scripts[i].status >= 0) {
                        printf("Duration:%.3lf ",
                        timeval_delta(&script_status->scripts[i].finished,
                              &script_status->scripts[i].start));
                }
-               printf("%s",
-                       ctime(&script_status->scripts[i].start.tv_sec));
-               if ((script_status->scripts[i].timedout != 0)
-               ||  (script_status->scripts[i].status != 0) ) {
-                       printf("   OUTPUT:%s\n",
-                               script_status->scripts[i].output);
+               if (script_status->scripts[i].status != -ENOEXEC) {
+                       printf("%s",
+                              ctime(&script_status->scripts[i].start.tv_sec));
+                       if (script_status->scripts[i].status != 0) {
+                               printf("   OUTPUT:%s\n",
+                                      script_status->scripts[i].output);
+                       }
+               } else {
+                       printf("\n");
+               }
+       }
+       return 0;
+}
+
+
+static int control_scriptstatus(struct ctdb_context *ctdb,
+                               int argc, const char **argv)
+{
+       int ret;
+       enum ctdb_eventscript_call type, min, max;
+       const char *arg;
+
+       if (argc > 1) {
+               DEBUG(DEBUG_ERR, ("Unknown arguments to scriptstatus\n"));
+               return -1;
+       }
+
+       if (argc == 0)
+               arg = ctdb_eventscript_call_names[CTDB_EVENT_MONITOR];
+       else
+               arg = argv[0];
+
+       for (type = 0; type < CTDB_EVENT_MAX; type++) {
+               if (strcmp(arg, ctdb_eventscript_call_names[type]) == 0) {
+                       min = type;
+                       max = type+1;
+                       break;
+               }
+       }
+       if (type == CTDB_EVENT_MAX) {
+               if (strcmp(arg, "all") == 0) {
+                       min = 0;
+                       max = CTDB_EVENT_MAX;
+               } else {
+                       DEBUG(DEBUG_ERR, ("Unknown event type %s\n", argv[0]));
+                       return -1;
+               }
+       }
+
+       if (options.machinereadable) {
+               printf(":Type:Name:Code:Status:Start:End:Error Output...:\n");
+       }
+
+       for (type = min; type < max; type++) {
+               ret = control_one_scriptstatus(ctdb, type);
+               if (ret != 0) {
+                       return ret;
                }
        }
 
        return 0;
 }
-       
 
 /*
   enable an eventscript
@@ -818,6 +981,80 @@ static int control_recmaster(struct ctdb_context *ctdb, int argc, const char **a
        return 0;
 }
 
+/*
+  add a tickle to a public address
+ */
+static int control_add_tickle(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       struct ctdb_tcp_connection t;
+       TDB_DATA data;
+       int ret;
+
+       if (argc < 2) {
+               usage();
+       }
+
+       if (parse_ip_port(argv[0], &t.src_addr) == 0) {
+               DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0]));
+               return -1;
+       }
+       if (parse_ip_port(argv[1], &t.dst_addr) == 0) {
+               DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[1]));
+               return -1;
+       }
+
+       data.dptr = (uint8_t *)&t;
+       data.dsize = sizeof(t);
+
+       /* tell all nodes about this tcp connection */
+       ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_TCP_ADD_DELAYED_UPDATE,
+                          0, data, ctdb, NULL, NULL, NULL, NULL);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,("Failed to add tickle\n"));
+               return -1;
+       }
+       
+       return 0;
+}
+
+
+/*
+  delete a tickle from a node
+ */
+static int control_del_tickle(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       struct ctdb_tcp_connection t;
+       TDB_DATA data;
+       int ret;
+
+       if (argc < 2) {
+               usage();
+       }
+
+       if (parse_ip_port(argv[0], &t.src_addr) == 0) {
+               DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0]));
+               return -1;
+       }
+       if (parse_ip_port(argv[1], &t.dst_addr) == 0) {
+               DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[1]));
+               return -1;
+       }
+
+       data.dptr = (uint8_t *)&t;
+       data.dsize = sizeof(t);
+
+       /* tell all nodes about this tcp connection */
+       ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_TCP_REMOVE,
+                          0, data, ctdb, NULL, NULL, NULL, NULL);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,("Failed to remove tickle\n"));
+               return -1;
+       }
+       
+       return 0;
+}
+
+
 /*
   get a list of all tickles for this pnn
  */
@@ -826,11 +1063,16 @@ static int control_get_tickles(struct ctdb_context *ctdb, int argc, const char *
        struct ctdb_control_tcp_tickle_list *list;
        ctdb_sock_addr addr;
        int i, ret;
+       unsigned port = 0;
 
        if (argc < 1) {
                usage();
        }
 
+       if (argc == 2) {
+               port = atoi(argv[1]);
+       }
+
        if (parse_ip(argv[0], NULL, 0, &addr) == 0) {
                DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0]));
                return -1;
@@ -842,11 +1084,25 @@ static int control_get_tickles(struct ctdb_context *ctdb, int argc, const char *
                return -1;
        }
 
-       printf("Tickles for ip:%s\n", ctdb_addr_to_str(&list->addr));
-       printf("Num tickles:%u\n", list->tickles.num);
-       for (i=0;i<list->tickles.num;i++) {
-               printf("SRC: %s:%u   ", ctdb_addr_to_str(&list->tickles.connections[i].src_addr), ntohs(list->tickles.connections[i].src_addr.ip.sin_port));
-               printf("DST: %s:%u\n", ctdb_addr_to_str(&list->tickles.connections[i].dst_addr), ntohs(list->tickles.connections[i].dst_addr.ip.sin_port));
+       if (options.machinereadable){
+               printf(":source ip:port:destination ip:port:\n");
+               for (i=0;i<list->tickles.num;i++) {
+                       if (port && port != ntohs(list->tickles.connections[i].dst_addr.ip.sin_port)) {
+                               continue;
+                       }
+                       printf(":%s:%u", ctdb_addr_to_str(&list->tickles.connections[i].src_addr), ntohs(list->tickles.connections[i].src_addr.ip.sin_port));
+                       printf(":%s:%u:\n", ctdb_addr_to_str(&list->tickles.connections[i].dst_addr), ntohs(list->tickles.connections[i].dst_addr.ip.sin_port));
+               }
+       } else {
+               printf("Tickles for ip:%s\n", ctdb_addr_to_str(&list->addr));
+               printf("Num tickles:%u\n", list->tickles.num);
+               for (i=0;i<list->tickles.num;i++) {
+                       if (port && port != ntohs(list->tickles.connections[i].dst_addr.ip.sin_port)) {
+                               continue;
+                       }
+                       printf("SRC: %s:%u   ", ctdb_addr_to_str(&list->tickles.connections[i].src_addr), ntohs(list->tickles.connections[i].src_addr.ip.sin_port));
+                       printf("DST: %s:%u\n", ctdb_addr_to_str(&list->tickles.connections[i].dst_addr), ntohs(list->tickles.connections[i].dst_addr.ip.sin_port));
+               }
        }
 
        talloc_free(list);
@@ -855,7 +1111,6 @@ static int control_get_tickles(struct ctdb_context *ctdb, int argc, const char *
 }
 
 
-
 static int move_ip(struct ctdb_context *ctdb, ctdb_sock_addr *addr, uint32_t pnn)
 {
        struct ctdb_all_public_ips *ips;
@@ -870,7 +1125,7 @@ static int move_ip(struct ctdb_context *ctdb, ctdb_sock_addr *addr, uint32_t pnn
        disable_time = 30;
        data.dptr  = (uint8_t*)&disable_time;
        data.dsize = sizeof(disable_time);
-       ret = ctdb_send_message(ctdb, CTDB_BROADCAST_CONNECTED, CTDB_SRVID_DISABLE_IP_CHECK, data);
+       ret = ctdb_client_send_message(ctdb, CTDB_BROADCAST_CONNECTED, CTDB_SRVID_DISABLE_IP_CHECK, data);
        if (ret != 0) {
                DEBUG(DEBUG_ERR,("Failed to send message to disable ipcheck\n"));
                return -1;
@@ -914,7 +1169,7 @@ static int move_ip(struct ctdb_context *ctdb, ctdb_sock_addr *addr, uint32_t pnn
                nodes = list_of_active_nodes_except_pnn(ctdb, nodemap, tmp_ctx, pnn);
        ret = ctdb_client_async_control(ctdb, CTDB_CONTROL_RELEASE_IP,
                                        nodes, 0,
-                                       TIMELIMIT(),
+                                       LONGTIMELIMIT(),
                                        false, data,
                                        NULL, NULL,
                                        NULL);
@@ -924,13 +1179,22 @@ static int move_ip(struct ctdb_context *ctdb, ctdb_sock_addr *addr, uint32_t pnn
                return -1;
        }
 
-       ret = ctdb_ctrl_takeover_ip(ctdb, TIMELIMIT(), pnn, &ip);
+       ret = ctdb_ctrl_takeover_ip(ctdb, LONGTIMELIMIT(), pnn, &ip);
        if (ret != 0) {
                DEBUG(DEBUG_ERR,("Failed to take over IP on node %d\n", pnn));
                talloc_free(tmp_ctx);
                return -1;
        }
 
+       /* update the recovery daemon so it now knows to expect the new
+          node assignment for this ip.
+       */
+       ret = ctdb_client_send_message(ctdb, CTDB_BROADCAST_CONNECTED, CTDB_SRVID_RECD_UPDATE_IP, data);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,("Failed to send message to update the ip on the recovery master.\n"));
+               return -1;
+       }
+
        talloc_free(tmp_ctx);
        return 0;
 }
@@ -1418,6 +1682,8 @@ static int regsrvid(struct ctdb_context *ctdb, int argc, const char **argv)
                DEBUG(DEBUG_ERR, ("Unable to register server_id from node %u\n", options.pnn));
                return ret;
        }
+       DEBUG(DEBUG_ERR,("Srvid registered. Sleeping for 999 seconds\n"));
+       sleep(999);
        return -1;
 }
 
@@ -1555,7 +1821,11 @@ static int control_ip(struct ctdb_context *ctdb, int argc, const char **argv)
        }
 
        if (options.machinereadable){
-               printf(":Public IP:Node:\n");
+               printf(":Public IP:Node:");
+               if (options.verbose){
+                       printf("ActiveInterface:AvailableInterfaces:ConfiguredInterfaces:");
+               }
+               printf("\n");
        } else {
                if (options.pnn == CTDB_BROADCAST_ALL) {
                        printf("Public IPs on ALL nodes\n");
@@ -1565,10 +1835,175 @@ static int control_ip(struct ctdb_context *ctdb, int argc, const char **argv)
        }
 
        for (i=1;i<=ips->num;i++) {
+               struct ctdb_control_public_ip_info *info = NULL;
+               int32_t pnn;
+               char *aciface = NULL;
+               char *avifaces = NULL;
+               char *cifaces = NULL;
+
+               if (options.pnn == CTDB_BROADCAST_ALL) {
+                       pnn = ips->ips[ips->num-i].pnn;
+               } else {
+                       pnn = options.pnn;
+               }
+
+               if (pnn != -1) {
+                       ret = ctdb_ctrl_get_public_ip_info(ctdb, TIMELIMIT(), pnn, ctdb,
+                                                  &ips->ips[ips->num-i].addr, &info);
+               } else {
+                       ret = -1;
+               }
+
+               if (ret == 0) {
+                       int j;
+                       for (j=0; j < info->num; j++) {
+                               if (cifaces == NULL) {
+                                       cifaces = talloc_strdup(info,
+                                                               info->ifaces[j].name);
+                               } else {
+                                       cifaces = talloc_asprintf_append(cifaces,
+                                                                        ",%s",
+                                                                        info->ifaces[j].name);
+                               }
+
+                               if (info->active_idx == j) {
+                                       aciface = info->ifaces[j].name;
+                               }
+
+                               if (info->ifaces[j].link_state == 0) {
+                                       continue;
+                               }
+
+                               if (avifaces == NULL) {
+                                       avifaces = talloc_strdup(info, info->ifaces[j].name);
+                               } else {
+                                       avifaces = talloc_asprintf_append(avifaces,
+                                                                         ",%s",
+                                                                         info->ifaces[j].name);
+                               }
+                       }
+               }
+
+               if (options.machinereadable){
+                       printf(":%s:%d:",
+                               ctdb_addr_to_str(&ips->ips[ips->num-i].addr),
+                               ips->ips[ips->num-i].pnn);
+                       if (options.verbose){
+                               printf("%s:%s:%s:",
+                                       aciface?aciface:"",
+                                       avifaces?avifaces:"",
+                                       cifaces?cifaces:"");
+                       }
+                       printf("\n");
+               } else {
+                       if (options.verbose) {
+                               printf("%s node[%d] active[%s] available[%s] configured[%s]\n",
+                                       ctdb_addr_to_str(&ips->ips[ips->num-i].addr),
+                                       ips->ips[ips->num-i].pnn,
+                                       aciface?aciface:"",
+                                       avifaces?avifaces:"",
+                                       cifaces?cifaces:"");
+                       } else {
+                               printf("%s %d\n",
+                                       ctdb_addr_to_str(&ips->ips[ips->num-i].addr),
+                                       ips->ips[ips->num-i].pnn);
+                       }
+               }
+               talloc_free(info);
+       }
+
+       talloc_free(tmp_ctx);
+       return 0;
+}
+
+/*
+  public ip info
+ */
+static int control_ipinfo(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       int i, ret;
+       ctdb_sock_addr addr;
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+       struct ctdb_control_public_ip_info *info;
+
+       if (argc != 1) {
+               talloc_free(tmp_ctx);
+               usage();
+       }
+
+       if (parse_ip(argv[0], NULL, 0, &addr) == 0) {
+               DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0]));
+               return -1;
+       }
+
+       /* read the public ip info from this node */
+       ret = ctdb_ctrl_get_public_ip_info(ctdb, TIMELIMIT(), options.pnn,
+                                          tmp_ctx, &addr, &info);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get public ip[%s]info from node %u\n",
+                                 argv[0], options.pnn));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       printf("Public IP[%s] info on node %u\n",
+              ctdb_addr_to_str(&info->ip.addr),
+              options.pnn);
+
+       printf("IP:%s\nCurrentNode:%d\nNumInterfaces:%u\n",
+              ctdb_addr_to_str(&info->ip.addr),
+              info->ip.pnn, info->num);
+
+       for (i=0; i<info->num; i++) {
+               info->ifaces[i].name[CTDB_IFACE_SIZE] = '\0';
+
+               printf("Interface[%u]: Name:%s Link:%s References:%u%s\n",
+                      i+1, info->ifaces[i].name,
+                      info->ifaces[i].link_state?"up":"down",
+                      (unsigned int)info->ifaces[i].references,
+                      (i==info->active_idx)?" (active)":"");
+       }
+
+       talloc_free(tmp_ctx);
+       return 0;
+}
+
+/*
+  display interfaces status
+ */
+static int control_ifaces(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       int i, ret;
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+       struct ctdb_control_get_ifaces *ifaces;
+
+       /* read the public ip list from this node */
+       ret = ctdb_ctrl_get_ifaces(ctdb, TIMELIMIT(), options.pnn,
+                                  tmp_ctx, &ifaces);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get interfaces from node %u\n",
+                                 options.pnn));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       if (options.machinereadable){
+               printf(":Name:LinkStatus:References:\n");
+       } else {
+               printf("Interfaces on node %u\n", options.pnn);
+       }
+
+       for (i=0; i<ifaces->num; i++) {
                if (options.machinereadable){
-                       printf(":%s:%d:\n", ctdb_addr_to_str(&ips->ips[ips->num-i].addr), ips->ips[ips->num-i].pnn);
+                       printf(":%s:%s:%u\n",
+                              ifaces->ifaces[i].name,
+                              ifaces->ifaces[i].link_state?"1":"0",
+                              (unsigned int)ifaces->ifaces[i].references);
                } else {
-                       printf("%s %d\n", ctdb_addr_to_str(&ips->ips[ips->num-i].addr), ips->ips[ips->num-i].pnn);
+                       printf("name:%s link:%s references:%u\n",
+                              ifaces->ifaces[i].name,
+                              ifaces->ifaces[i].link_state?"up":"down",
+                              (unsigned int)ifaces->ifaces[i].references);
                }
        }
 
@@ -1576,6 +2011,55 @@ static int control_ip(struct ctdb_context *ctdb, int argc, const char **argv)
        return 0;
 }
 
+
+/*
+  set link status of an interface
+ */
+static int control_setifacelink(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       int ret;
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+       struct ctdb_control_iface_info info;
+
+       ZERO_STRUCT(info);
+
+       if (argc != 2) {
+               usage();
+       }
+
+       if (strlen(argv[0]) > CTDB_IFACE_SIZE) {
+               DEBUG(DEBUG_ERR, ("interfaces name '%s' too long\n",
+                                 argv[0]));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+       strcpy(info.name, argv[0]);
+
+       if (strcmp(argv[1], "up") == 0) {
+               info.link_state = 1;
+       } else if (strcmp(argv[1], "down") == 0) {
+               info.link_state = 0;
+       } else {
+               DEBUG(DEBUG_ERR, ("link state invalid '%s' should be 'up' or 'down'\n",
+                                 argv[1]));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       /* read the public ip list from this node */
+       ret = ctdb_ctrl_set_iface_link(ctdb, TIMELIMIT(), options.pnn,
+                                  tmp_ctx, &info);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to set link state for interfaces %s node %u\n",
+                                 argv[0], options.pnn));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       talloc_free(tmp_ctx);
+       return 0;
+}
+
 /*
   display pid of a ctdb daemon
  */
@@ -1594,13 +2078,15 @@ static int control_getpid(struct ctdb_context *ctdb, int argc, const char **argv
        return 0;
 }
 
+static uint32_t ipreallocate_finished;
+
 /*
   handler for receiving the response to ipreallocate
 */
 static void ip_reallocate_handler(struct ctdb_context *ctdb, uint64_t srvid, 
                             TDB_DATA data, void *private_data)
 {
-       exit(0);
+       ipreallocate_finished = 1;
 }
 
 static void ctdb_every_second(struct event_context *ev, struct timed_event *te, struct timeval t, void *p)
@@ -1642,21 +2128,16 @@ static int control_ipreallocate(struct ctdb_context *ctdb, int argc, const char
        /* register a message port for receiveing the reply so that we
           can receive the reply
        */
-       ctdb_set_message_handler(ctdb, rd.srvid, ip_reallocate_handler, NULL);
+       ctdb_client_set_message_handler(ctdb, rd.srvid, ip_reallocate_handler, NULL);
 
        data.dptr = (uint8_t *)&rd;
        data.dsize = sizeof(rd);
 
 again:
-       if (retries>5) {
-               DEBUG(DEBUG_ERR,("Failed waiting for cluster convergense\n"));
-               exit(10);
-       }
-
        /* check that there are valid nodes available */
        if (ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), options.pnn, ctdb, &nodemap) != 0) {
                DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n"));
-               exit(10);
+               return -1;
        }
        for (i=0; i<nodemap->num;i++) {
                if ((nodemap->nodes[i].flags & (NODE_FLAGS_DELETED|NODE_FLAGS_BANNED|NODE_FLAGS_STOPPED)) == 0) {
@@ -1678,7 +2159,7 @@ again:
        /* verify the node exists */
        if (ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), recmaster, ctdb, &nodemap) != 0) {
                DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n"));
-               exit(10);
+               return -1;
        }
 
 
@@ -1711,7 +2192,8 @@ again:
                goto again;
        } 
 
-       ret = ctdb_send_message(ctdb, recmaster, CTDB_SRVID_TAKEOVER_RUN, data);
+       ipreallocate_finished = 0;
+       ret = ctdb_client_send_message(ctdb, recmaster, CTDB_SRVID_TAKEOVER_RUN, data);
        if (ret != 0) {
                DEBUG(DEBUG_ERR,("Failed to send ip takeover run request message to %u\n", options.pnn));
                return -1;
@@ -1719,11 +2201,14 @@ again:
 
        tv = timeval_current();
        /* this loop will terminate when we have received the reply */
-       while (timeval_elapsed(&tv) < 3.0) {    
+       while (timeval_elapsed(&tv) < 3.0) {
                event_loop_once(ctdb->ev);
        }
+       if (ipreallocate_finished == 1) {
+               return 0;
+       }
 
-       DEBUG(DEBUG_INFO,("Timed out waiting for recmaster ipreallocate. Trying again\n"));
+       DEBUG(DEBUG_ERR,("Timed out waiting for recmaster ipreallocate. Trying again\n"));
        retries++;
        sleep(1);
        goto again;
@@ -1740,6 +2225,16 @@ static int control_disable(struct ctdb_context *ctdb, int argc, const char **arg
        int ret;
        struct ctdb_node_map *nodemap=NULL;
 
+       /* check if the node is already disabled */
+       if (ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap) != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n"));
+               exit(10);
+       }
+       if (nodemap->nodes[options.pnn].flags & NODE_FLAGS_PERMANENTLY_DISABLED) {
+               DEBUG(DEBUG_ERR,("Node %d is already disabled.\n", options.pnn));
+               return 0;
+       }
+
        do {
                ret = ctdb_ctrl_modflags(ctdb, TIMELIMIT(), options.pnn, NODE_FLAGS_PERMANENTLY_DISABLED, 0);
                if (ret != 0) {
@@ -1774,6 +2269,17 @@ static int control_enable(struct ctdb_context *ctdb, int argc, const char **argv
 
        struct ctdb_node_map *nodemap=NULL;
 
+
+       /* check if the node is already enabled */
+       if (ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap) != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n"));
+               exit(10);
+       }
+       if (!(nodemap->nodes[options.pnn].flags & NODE_FLAGS_PERMANENTLY_DISABLED)) {
+               DEBUG(DEBUG_ERR,("Node %d is already enabled.\n", options.pnn));
+               return 0;
+       }
+
        do {
                ret = ctdb_ctrl_modflags(ctdb, TIMELIMIT(), options.pnn, 0, NODE_FLAGS_PERMANENTLY_DISABLED);
                if (ret != 0) {
@@ -1790,6 +2296,7 @@ static int control_enable(struct ctdb_context *ctdb, int argc, const char **argv
                }
 
        } while (nodemap->nodes[options.pnn].flags & NODE_FLAGS_PERMANENTLY_DISABLED);
+
        ret = control_ipreallocate(ctdb, argc, argv);
        if (ret != 0) {
                DEBUG(DEBUG_ERR, ("IP Reallocate failed on node %u\n", options.pnn));
@@ -2337,6 +2844,9 @@ static int control_catdb(struct ctdb_context *ctdb, int argc, const char **argv)
        ret = ctdb_dump_db(ctdb_db, stdout);
        if (ret == -1) {
                DEBUG(DEBUG_ERR, ("Unable to dump database\n"));
+               DEBUG(DEBUG_ERR, ("Maybe try 'ctdb getdbstatus %s'"
+                                 " and 'ctdb getvar AllowUnhealthyDBRead'\n",
+                                 db_name));
                return -1;
        }
        talloc_free(ctdb_db);
@@ -2346,6 +2856,266 @@ static int control_catdb(struct ctdb_context *ctdb, int argc, const char **argv)
 }
 
 
+/*
+  fetch a record from a persistent database
+ */
+static int control_pfetch(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       const char *db_name;
+       struct ctdb_db_context *ctdb_db;
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+       struct ctdb_transaction_handle *h;
+       TDB_DATA key, data;
+       int ret;
+
+       if (argc < 2) {
+               talloc_free(tmp_ctx);
+               usage();
+       }
+
+       db_name = argv[0];
+
+
+       if (db_exists(ctdb, db_name)) {
+               DEBUG(DEBUG_ERR,("Database '%s' does not exist\n", db_name));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       ctdb_db = ctdb_attach(ctdb, db_name, true, 0);
+
+       if (ctdb_db == NULL) {
+               DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       h = ctdb_transaction_start(ctdb_db, tmp_ctx);
+       if (h == NULL) {
+               DEBUG(DEBUG_ERR,("Failed to start transaction on database %s\n", db_name));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       key.dptr  = discard_const(argv[1]);
+       key.dsize = strlen(argv[1]);
+       ret = ctdb_transaction_fetch(h, tmp_ctx, key, &data);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,("Failed to fetch record\n"));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       if (data.dsize == 0 || data.dptr == NULL) {
+               DEBUG(DEBUG_ERR,("Record is empty\n"));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       fwrite(data.dptr, data.dsize, 1, stdout);
+
+       /* abort the transaction */
+       talloc_free(h);
+
+
+       talloc_free(tmp_ctx);
+       return 0;
+}
+
+/*
+  write a record to a persistent database
+ */
+static int control_pstore(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       const char *db_name;
+       struct ctdb_db_context *ctdb_db;
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+       struct ctdb_transaction_handle *h;
+       struct stat st;
+       TDB_DATA key, data;
+       int fd, ret;
+
+       if (argc < 3) {
+               talloc_free(tmp_ctx);
+               usage();
+       }
+
+       fd = open(argv[2], O_RDONLY);
+       if (fd == -1) {
+               DEBUG(DEBUG_ERR,("Failed to open file containing record data : %s  %s\n", argv[2], strerror(errno)));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+       
+       ret = fstat(fd, &st);
+       if (ret == -1) {
+               DEBUG(DEBUG_ERR,("fstat of file %s failed: %s\n", argv[2], strerror(errno)));
+               close(fd);
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       if (!S_ISREG(st.st_mode)) {
+               DEBUG(DEBUG_ERR,("Not a regular file %s\n", argv[2]));
+               close(fd);
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       data.dsize = st.st_size;
+       if (data.dsize == 0) {
+               data.dptr  = NULL;
+       } else {
+               data.dptr = talloc_size(tmp_ctx, data.dsize);
+               if (data.dptr == NULL) {
+                       DEBUG(DEBUG_ERR,("Failed to talloc %d of memory to store record data\n", (int)data.dsize));
+                       close(fd);
+                       talloc_free(tmp_ctx);
+                       return -1;
+               }
+               ret = read(fd, data.dptr, data.dsize);
+               if (ret != data.dsize) {
+                       DEBUG(DEBUG_ERR,("Failed to read %d bytes of record data\n", (int)data.dsize));
+                       close(fd);
+                       talloc_free(tmp_ctx);
+                       return -1;
+               }
+       }
+       close(fd);
+
+
+       db_name = argv[0];
+
+       ctdb_db = ctdb_attach(ctdb, db_name, true, 0);
+
+       if (ctdb_db == NULL) {
+               DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       h = ctdb_transaction_start(ctdb_db, tmp_ctx);
+       if (h == NULL) {
+               DEBUG(DEBUG_ERR,("Failed to start transaction on database %s\n", db_name));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       key.dptr  = discard_const(argv[1]);
+       key.dsize = strlen(argv[1]);
+       ret = ctdb_transaction_store(h, key, data);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,("Failed to store record\n"));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       ret = ctdb_transaction_commit(h);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,("Failed to commit transaction\n"));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+
+       talloc_free(tmp_ctx);
+       return 0;
+}
+
+static void log_handler(struct ctdb_context *ctdb, uint64_t srvid, 
+                            TDB_DATA data, void *private_data)
+{
+       DEBUG(DEBUG_ERR,("Log data received\n"));
+       if (data.dsize > 0) {
+               printf("%s", data.dptr);
+       }
+
+       exit(0);
+}
+
+/*
+  display a list of log messages from the in memory ringbuffer
+ */
+static int control_getlog(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       int ret;
+       int32_t res;
+       struct ctdb_get_log_addr log_addr;
+       TDB_DATA data;
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+       char *errmsg;
+       struct timeval tv;
+
+       if (argc != 1) {
+               DEBUG(DEBUG_ERR,("Invalid arguments\n"));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       log_addr.pnn = ctdb_ctrl_getpnn(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE);
+       log_addr.srvid = getpid();
+       if (isalpha(argv[0][0]) || argv[0][0] == '-') { 
+               log_addr.level = get_debug_by_desc(argv[0]);
+       } else {
+               log_addr.level = strtol(argv[0], NULL, 0);
+       }
+
+
+       data.dptr = (unsigned char *)&log_addr;
+       data.dsize = sizeof(log_addr);
+
+       DEBUG(DEBUG_ERR, ("Pulling logs from node %u\n", options.pnn));
+
+       ctdb_client_set_message_handler(ctdb, log_addr.srvid, log_handler, NULL);
+       sleep(1);
+
+       DEBUG(DEBUG_ERR,("Listen for response on %d\n", (int)log_addr.srvid));
+
+       ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_GET_LOG,
+                          0, data, tmp_ctx, NULL, &res, NULL, &errmsg);
+       if (ret != 0 || res != 0) {
+               DEBUG(DEBUG_ERR,("Failed to get logs - %s\n", errmsg));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+
+       tv = timeval_current();
+       /* this loop will terminate when we have received the reply */
+       while (timeval_elapsed(&tv) < 3.0) {    
+               event_loop_once(ctdb->ev);
+       }
+
+       DEBUG(DEBUG_INFO,("Timed out waiting for log data.\n"));
+
+       talloc_free(tmp_ctx);
+       return 0;
+}
+
+/*
+  clear the in memory log area
+ */
+static int control_clearlog(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       int ret;
+       int32_t res;
+       char *errmsg;
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+
+       ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_CLEAR_LOG,
+                          0, tdb_null, tmp_ctx, NULL, &res, NULL, &errmsg);
+       if (ret != 0 || res != 0) {
+               DEBUG(DEBUG_ERR,("Failed to clear logs\n"));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       talloc_free(tmp_ctx);
+       return 0;
+}
+
+
+
 /*
   display a list of the databases on a remote ctdb
  */
@@ -2360,19 +3130,91 @@ static int control_getdbmap(struct ctdb_context *ctdb, int argc, const char **ar
                return ret;
        }
 
+       if(options.machinereadable){
+               printf(":ID:Name:Path:Persistent:Unhealthy:\n");
+               for(i=0;i<dbmap->num;i++){
+                       const char *path;
+                       const char *name;
+                       const char *health;
+                       bool persistent;
+
+                       ctdb_ctrl_getdbpath(ctdb, TIMELIMIT(), options.pnn,
+                                           dbmap->dbs[i].dbid, ctdb, &path);
+                       ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn,
+                                           dbmap->dbs[i].dbid, ctdb, &name);
+                       ctdb_ctrl_getdbhealth(ctdb, TIMELIMIT(), options.pnn,
+                                             dbmap->dbs[i].dbid, ctdb, &health);
+                       persistent = dbmap->dbs[i].persistent;
+                       printf(":0x%08X:%s:%s:%d:%d:\n",
+                              dbmap->dbs[i].dbid, name, path,
+                              !!(persistent), !!(health));
+               }
+               return 0;
+       }
+
        printf("Number of databases:%d\n", dbmap->num);
        for(i=0;i<dbmap->num;i++){
                const char *path;
                const char *name;
+               const char *health;
                bool persistent;
 
                ctdb_ctrl_getdbpath(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &path);
                ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &name);
+               ctdb_ctrl_getdbhealth(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &health);
+               persistent = dbmap->dbs[i].persistent;
+               printf("dbid:0x%08x name:%s path:%s%s%s\n",
+                      dbmap->dbs[i].dbid, name, path,
+                      persistent?" PERSISTENT":"",
+                      health?" UNHEALTHY":"");
+       }
+
+       return 0;
+}
+
+/*
+  display the status of a database on a remote ctdb
+ */
+static int control_getdbstatus(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       int i, ret;
+       struct ctdb_dbid_map *dbmap=NULL;
+       const char *db_name;
+
+       if (argc < 1) {
+               usage();
+       }
+
+       db_name = argv[0];
+
+       ret = ctdb_ctrl_getdbmap(ctdb, TIMELIMIT(), options.pnn, ctdb, &dbmap);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get dbids from node %u\n", options.pnn));
+               return ret;
+       }
+
+       for(i=0;i<dbmap->num;i++){
+               const char *path;
+               const char *name;
+               const char *health;
+               bool persistent;
+
+               ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &name);
+               if (strcmp(name, db_name) != 0) {
+                       continue;
+               }
+
+               ctdb_ctrl_getdbpath(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &path);
+               ctdb_ctrl_getdbhealth(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &health);
                persistent = dbmap->dbs[i].persistent;
-               printf("dbid:0x%08x name:%s path:%s %s\n", dbmap->dbs[i].dbid, name, 
-                      path, persistent?"PERSISTENT":"");
+               printf("dbid: 0x%08x\nname: %s\npath: %s\nPERSISTENT: %s\nHEALTH: %s\n",
+                      dbmap->dbs[i].dbid, name, path,
+                      persistent?"yes":"no",
+                      health?health:"OK");
+               return 0;
        }
 
+       DEBUG(DEBUG_ERR, ("db %s doesn't exist on node %u\n", db_name, options.pnn));
        return 0;
 }
 
@@ -2705,9 +3547,9 @@ static int control_setdebug(struct ctdb_context *ctdb, int argc, const char **ar
 
 
 /*
-  freeze a node
+  thaw a node
  */
-static int control_freeze(struct ctdb_context *ctdb, int argc, const char **argv)
+static int control_thaw(struct ctdb_context *ctdb, int argc, const char **argv)
 {
        int ret;
        uint32_t priority;
@@ -2717,40 +3559,18 @@ static int control_freeze(struct ctdb_context *ctdb, int argc, const char **argv
        } else {
                priority = 0;
        }
-       DEBUG(DEBUG_ERR,("Freeze by priority %u\n", priority));
+       DEBUG(DEBUG_ERR,("Thaw by priority %u\n", priority));
 
-       ret = ctdb_ctrl_freeze_priority(ctdb, TIMELIMIT(), options.pnn, priority);
+       ret = ctdb_ctrl_thaw_priority(ctdb, TIMELIMIT(), options.pnn, priority);
        if (ret != 0) {
-               DEBUG(DEBUG_ERR, ("Unable to freeze node %u\n", options.pnn));
+               DEBUG(DEBUG_ERR, ("Unable to thaw node %u\n", options.pnn));
        }               
        return 0;
 }
 
+
 /*
-  thaw a node
- */
-static int control_thaw(struct ctdb_context *ctdb, int argc, const char **argv)
-{
-       int ret;
-       uint32_t priority;
-       
-       if (argc == 1) {
-               priority = strtol(argv[0], NULL, 0);
-       } else {
-               priority = 0;
-       }
-       DEBUG(DEBUG_ERR,("Thaw by priority %u\n", priority));
-
-       ret = ctdb_ctrl_thaw_priority(ctdb, TIMELIMIT(), options.pnn, priority);
-       if (ret != 0) {
-               DEBUG(DEBUG_ERR, ("Unable to thaw node %u\n", options.pnn));
-       }               
-       return 0;
-}
-
-
-/*
-  attach to a database
+  attach to a database
  */
 static int control_attach(struct ctdb_context *ctdb, int argc, const char **argv)
 {
@@ -2909,6 +3729,7 @@ static int control_backupdb(struct ctdb_context *ctdb, int argc, const char **ar
        struct backup_data *bd;
        int fh = -1;
        int status = -1;
+       const char *reason = NULL;
 
        if (argc != 2) {
                DEBUG(DEBUG_ERR,("Invalid arguments\n"));
@@ -2937,6 +3758,37 @@ static int control_backupdb(struct ctdb_context *ctdb, int argc, const char **ar
                return -1;
        }
 
+       ret = ctdb_ctrl_getdbhealth(ctdb, TIMELIMIT(), options.pnn,
+                                   dbmap->dbs[i].dbid, tmp_ctx, &reason);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,("Unable to get dbhealth for database '%s'\n",
+                                argv[0]));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+       if (reason) {
+               uint32_t allow_unhealthy = 0;
+
+               ctdb_ctrl_get_tunable(ctdb, TIMELIMIT(), options.pnn,
+                                     "AllowUnhealthyDBRead",
+                                     &allow_unhealthy);
+
+               if (allow_unhealthy != 1) {
+                       DEBUG(DEBUG_ERR,("database '%s' is unhealthy: %s\n",
+                                        argv[0], reason));
+
+                       DEBUG(DEBUG_ERR,("disallow backup : tunnable AllowUnhealthyDBRead = %u\n",
+                                        allow_unhealthy));
+                       talloc_free(tmp_ctx);
+                       return -1;
+               }
+
+               DEBUG(DEBUG_WARNING,("WARNING database '%s' is unhealthy - see 'ctdb getdbstatus %s'\n",
+                                    argv[0], argv[0]));
+               DEBUG(DEBUG_WARNING,("WARNING! allow backup of unhealthy database: "
+                                    "tunnable AllowUnhealthyDBRead = %u\n",
+                                    allow_unhealthy));
+       }
 
        ctdb_db = ctdb_attach(ctdb, argv[0], dbmap->dbs[i].persistent, 0);
        if (ctdb_db == NULL) {
@@ -3039,8 +3891,9 @@ static int control_restoredb(struct ctdb_context *ctdb, int argc, const char **a
        uint32_t generation;
        struct tm *tm;
        char tbuf[100];
+       char *dbname;
 
-       if (argc != 1) {
+       if (argc < 1 || argc > 2) {
                DEBUG(DEBUG_ERR,("Invalid arguments\n"));
                return -1;
        }
@@ -3059,6 +3912,11 @@ static int control_restoredb(struct ctdb_context *ctdb, int argc, const char **a
                return -1;
        }
 
+       dbname = discard_const(dbhdr.name);
+       if (argc == 2) {
+               dbname = discard_const(argv[1]);
+       }
+
        outdata.dsize = dbhdr.size;
        outdata.dptr = talloc_size(tmp_ctx, outdata.dsize);
        if (outdata.dptr == NULL) {
@@ -3073,12 +3931,12 @@ static int control_restoredb(struct ctdb_context *ctdb, int argc, const char **a
        tm = localtime(&dbhdr.timestamp);
        strftime(tbuf,sizeof(tbuf)-1,"%Y/%m/%d %H:%M:%S", tm);
        printf("Restoring database '%s' from backup @ %s\n",
-               dbhdr.name, tbuf);
+               dbname, tbuf);
 
 
-       ctdb_db = ctdb_attach(ctdb, dbhdr.name, dbhdr.persistent, 0);
+       ctdb_db = ctdb_attach(ctdb, dbname, dbhdr.persistent, 0);
        if (ctdb_db == NULL) {
-               DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", dbhdr.name));
+               DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", dbname));
                talloc_free(tmp_ctx);
                return -1;
        }
@@ -3162,6 +4020,22 @@ static int control_restoredb(struct ctdb_context *ctdb, int argc, const char **a
                return -1;
        }
 
+       data.dptr = (void *)&ctdb_db->db_id;
+       data.dsize = sizeof(ctdb_db->db_id);
+
+       /* mark the database as healthy */
+       nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true);
+       if (ctdb_client_async_control(ctdb, CTDB_CONTROL_DB_SET_HEALTHY,
+                                       nodes, 0,
+                                       TIMELIMIT(), false, data,
+                                       NULL, NULL,
+                                       NULL) != 0) {
+               DEBUG(DEBUG_ERR, ("Failed to mark database as healthy.\n"));
+               ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE);
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
        data.dptr = (void *)&generation;
        data.dsize = sizeof(generation);
 
@@ -3197,6 +4071,250 @@ static int control_restoredb(struct ctdb_context *ctdb, int argc, const char **a
        return 0;
 }
 
+/*
+ * dump a database backup from a file
+ */
+static int control_dumpdbbackup(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+       TDB_DATA outdata;
+       struct db_file_header dbhdr;
+       int i, fh;
+       struct tm *tm;
+       char tbuf[100];
+       struct ctdb_rec_data *rec = NULL;
+       struct ctdb_marshall_buffer *m;
+
+       if (argc != 1) {
+               DEBUG(DEBUG_ERR,("Invalid arguments\n"));
+               return -1;
+       }
+
+       fh = open(argv[0], O_RDONLY);
+       if (fh == -1) {
+               DEBUG(DEBUG_ERR,("Failed to open file '%s'\n", argv[0]));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       read(fh, &dbhdr, sizeof(dbhdr));
+       if (dbhdr.version != DB_VERSION) {
+               DEBUG(DEBUG_ERR,("Invalid version of database dump. File is version %lu but expected version was %u\n", dbhdr.version, DB_VERSION));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       outdata.dsize = dbhdr.size;
+       outdata.dptr = talloc_size(tmp_ctx, outdata.dsize);
+       if (outdata.dptr == NULL) {
+               DEBUG(DEBUG_ERR,("Failed to allocate data of size '%lu'\n", dbhdr.size));
+               close(fh);
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+       read(fh, outdata.dptr, outdata.dsize);
+       close(fh);
+       m = (struct ctdb_marshall_buffer *)outdata.dptr;
+
+       tm = localtime(&dbhdr.timestamp);
+       strftime(tbuf,sizeof(tbuf)-1,"%Y/%m/%d %H:%M:%S", tm);
+       printf("Backup of database name:'%s' dbid:0x%x08x from @ %s\n",
+               dbhdr.name, m->db_id, tbuf);
+
+       for (i=0; i < m->count; i++) {
+               uint32_t reqid = 0;
+               TDB_DATA key, data;
+
+               /* we do not want the header splitted, so we pass NULL*/
+               rec = ctdb_marshall_loop_next(m, rec, &reqid,
+                                             NULL, &key, &data);
+
+               ctdb_dumpdb_record(ctdb, key, data, stdout);
+       }
+
+       printf("Dumped %d records\n", i);
+       talloc_free(tmp_ctx);
+       return 0;
+}
+
+/*
+ * wipe a database from a file
+ */
+static int control_wipedb(struct ctdb_context *ctdb, int argc,
+                         const char **argv)
+{
+       int ret;
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+       TDB_DATA data;
+       struct ctdb_db_context *ctdb_db;
+       struct ctdb_node_map *nodemap = NULL;
+       struct ctdb_vnn_map *vnnmap = NULL;
+       int i;
+       struct ctdb_control_wipe_database w;
+       uint32_t *nodes;
+       uint32_t generation;
+       struct ctdb_dbid_map *dbmap = NULL;
+
+       if (argc != 1) {
+               DEBUG(DEBUG_ERR,("Invalid arguments\n"));
+               return -1;
+       }
+
+       ret = ctdb_ctrl_getdbmap(ctdb, TIMELIMIT(), options.pnn, tmp_ctx,
+                                &dbmap);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get dbids from node %u\n",
+                                 options.pnn));
+               return ret;
+       }
+
+       for(i=0;i<dbmap->num;i++){
+               const char *name;
+
+               ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn,
+                                   dbmap->dbs[i].dbid, tmp_ctx, &name);
+               if(!strcmp(argv[0], name)){
+                       talloc_free(discard_const(name));
+                       break;
+               }
+               talloc_free(discard_const(name));
+       }
+       if (i == dbmap->num) {
+               DEBUG(DEBUG_ERR, ("No database with name '%s' found\n",
+                                 argv[0]));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       ctdb_db = ctdb_attach(ctdb, argv[0], dbmap->dbs[i].persistent, 0);
+       if (ctdb_db == NULL) {
+               DEBUG(DEBUG_ERR, ("Unable to attach to database '%s'\n",
+                                 argv[0]));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), options.pnn, ctdb,
+                                  &nodemap);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get nodemap from node %u\n",
+                                 options.pnn));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       ret = ctdb_ctrl_getvnnmap(ctdb, TIMELIMIT(), options.pnn, tmp_ctx,
+                                 &vnnmap);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get vnnmap from node %u\n",
+                                 options.pnn));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       /* freeze all nodes */
+       nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true);
+       for (i=1; i<=NUM_DB_PRIORITIES; i++) {
+               ret = ctdb_client_async_control(ctdb, CTDB_CONTROL_FREEZE,
+                                               nodes, i,
+                                               TIMELIMIT(),
+                                               false, tdb_null,
+                                               NULL, NULL,
+                                               NULL);
+               if (ret != 0) {
+                       DEBUG(DEBUG_ERR, ("Unable to freeze nodes.\n"));
+                       ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn,
+                                            CTDB_RECOVERY_ACTIVE);
+                       talloc_free(tmp_ctx);
+                       return -1;
+               }
+       }
+
+       generation = vnnmap->generation;
+       data.dptr = (void *)&generation;
+       data.dsize = sizeof(generation);
+
+       /* start a cluster wide transaction */
+       nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true);
+       ret = ctdb_client_async_control(ctdb, CTDB_CONTROL_TRANSACTION_START,
+                                       nodes, 0,
+                                       TIMELIMIT(), false, data,
+                                       NULL, NULL,
+                                       NULL);
+       if (ret!= 0) {
+               DEBUG(DEBUG_ERR, ("Unable to start cluster wide "
+                                 "transactions.\n"));
+               return -1;
+       }
+
+       w.db_id = ctdb_db->db_id;
+       w.transaction_id = generation;
+
+       data.dptr = (void *)&w;
+       data.dsize = sizeof(w);
+
+       /* wipe all the remote databases. */
+       nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true);
+       if (ctdb_client_async_control(ctdb, CTDB_CONTROL_WIPE_DATABASE,
+                                       nodes, 0,
+                                       TIMELIMIT(), false, data,
+                                       NULL, NULL,
+                                       NULL) != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to wipe database.\n"));
+               ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE);
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       data.dptr = (void *)&ctdb_db->db_id;
+       data.dsize = sizeof(ctdb_db->db_id);
+
+       /* mark the database as healthy */
+       nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true);
+       if (ctdb_client_async_control(ctdb, CTDB_CONTROL_DB_SET_HEALTHY,
+                                       nodes, 0,
+                                       TIMELIMIT(), false, data,
+                                       NULL, NULL,
+                                       NULL) != 0) {
+               DEBUG(DEBUG_ERR, ("Failed to mark database as healthy.\n"));
+               ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE);
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       data.dptr = (void *)&generation;
+       data.dsize = sizeof(generation);
+
+       /* commit all the changes */
+       if (ctdb_client_async_control(ctdb, CTDB_CONTROL_TRANSACTION_COMMIT,
+                                       nodes, 0,
+                                       TIMELIMIT(), false, data,
+                                       NULL, NULL,
+                                       NULL) != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to commit databases.\n"));
+               ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE);
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       /* thaw all nodes */
+       nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true);
+       if (ctdb_client_async_control(ctdb, CTDB_CONTROL_THAW,
+                                       nodes, 0,
+                                       TIMELIMIT(),
+                                       false, tdb_null,
+                                       NULL, NULL,
+                                       NULL) != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to thaw nodes.\n"));
+               ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.pnn, CTDB_RECOVERY_ACTIVE);
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       talloc_free(tmp_ctx);
+       return 0;
+}
+
 /*
  * set flags of a node in the nodemap
  */
@@ -3292,13 +4410,13 @@ static int control_rddumpmemory(struct ctdb_context *ctdb, int argc, const char
        /* register a message port for receiveing the reply so that we
           can receive the reply
        */
-       ctdb_set_message_handler(ctdb, rd.srvid, mem_dump_handler, NULL);
+       ctdb_client_set_message_handler(ctdb, rd.srvid, mem_dump_handler, NULL);
 
 
        data.dptr = (uint8_t *)&rd;
        data.dsize = sizeof(rd);
 
-       ret = ctdb_send_message(ctdb, options.pnn, CTDB_SRVID_MEM_DUMP, data);
+       ret = ctdb_client_send_message(ctdb, options.pnn, CTDB_SRVID_MEM_DUMP, data);
        if (ret != 0) {
                DEBUG(DEBUG_ERR,("Failed to send memdump request message to %u\n", options.pnn));
                return -1;
@@ -3312,6 +4430,69 @@ static int control_rddumpmemory(struct ctdb_context *ctdb, int argc, const char
        return 0;
 }
 
+/*
+  send a message to a srvid
+ */
+static int control_msgsend(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       unsigned long srvid;
+       int ret;
+       TDB_DATA data;
+
+       if (argc < 2) {
+               usage();
+       }
+
+       srvid      = strtoul(argv[0], NULL, 0);
+
+       data.dptr = (uint8_t *)discard_const(argv[1]);
+       data.dsize= strlen(argv[1]);
+
+       ret = ctdb_client_send_message(ctdb, CTDB_BROADCAST_CONNECTED, srvid, data);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,("Failed to send memdump request message to %u\n", options.pnn));
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+  handler for msglisten
+*/
+static void msglisten_handler(struct ctdb_context *ctdb, uint64_t srvid, 
+                            TDB_DATA data, void *private_data)
+{
+       int i;
+
+       printf("Message received: ");
+       for (i=0;i<data.dsize;i++) {
+               printf("%c", data.dptr[i]);
+       }
+       printf("\n");
+}
+
+/*
+  listen for messages on a messageport
+ */
+static int control_msglisten(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       uint64_t srvid;
+
+       srvid = getpid();
+
+       /* register a message port and listen for messages
+       */
+       ctdb_client_set_message_handler(ctdb, srvid, msglisten_handler, NULL);
+       printf("Listening for messages on srvid:%d\n", (int)srvid);
+
+       while (1) {     
+               event_loop_once(ctdb->ev);
+       }
+
+       return 0;
+}
+
 /*
   list all nodes in the cluster
   if the daemon is running, we read the data from the daemon.
@@ -3440,8 +4621,12 @@ static const struct {
        { "statistics",      control_statistics,        false,  false, "show statistics" },
        { "statisticsreset", control_statistics_reset,  true,   false,  "reset statistics"},
        { "ip",              control_ip,                false,  false,  "show which public ip's that ctdb manages" },
+       { "ipinfo",          control_ipinfo,            true,   false,  "show details about a public ip that ctdb manages", "<ip>" },
+       { "ifaces",          control_ifaces,            true,   false,  "show which interfaces that ctdb manages" },
+       { "setifacelink",    control_setifacelink,      true,   false,  "set interface link status", "<iface> <status>" },
        { "process-exists",  control_process_exists,    true,   false,  "check if a process exists on a node",  "<pid>"},
        { "getdbmap",        control_getdbmap,          true,   false,  "show the database map" },
+       { "getdbstatus",     control_getdbstatus,       true,   false,  "show the status of a database", "<dbname>" },
        { "catdb",           control_catdb,             true,   false,  "dump a database" ,                     "<dbname>"},
        { "getmonmode",      control_getmonmode,        true,   false,  "show monitoring mode" },
        { "getcapabilities", control_getcapabilities,   true,   false,  "show node capabilities" },
@@ -3452,6 +4637,8 @@ static const struct {
        { "enablemonitor",      control_enable_monmode, true,   false,  "set monitoring mode to ACTIVE" },
        { "setdebug",        control_setdebug,          true,   false,  "set debug level",                      "<EMERG|ALERT|CRIT|ERR|WARNING|NOTICE|INFO|DEBUG>" },
        { "getdebug",        control_getdebug,          true,   false,  "get debug level" },
+       { "getlog",          control_getlog,            true,   false,  "get the log data from the in memory ringbuffer", "<level>" },
+       { "clearlog",          control_clearlog,        true,   false,  "clear the log data from the in memory ringbuffer" },
        { "attach",          control_attach,            true,   false,  "attach to a database",                 "<dbname>" },
        { "dumpmemory",      control_dumpmemory,        true,   false,  "dump memory map to stdout" },
        { "rddumpmemory",    control_rddumpmemory,      true,   false,  "dump memory map from the recovery daemon to stdout" },
@@ -3465,14 +4652,17 @@ static const struct {
        { "showban",         control_showban,           true,   false,  "show ban information"},
        { "shutdown",        control_shutdown,          true,   false,  "shutdown ctdbd" },
        { "recover",         control_recover,           true,   false,  "force recovery" },
+       { "sync",            control_ipreallocate,      true,   false,  "wait until ctdbd has synced all state changes" },
        { "ipreallocate",    control_ipreallocate,      true,   false,  "force the recovery daemon to perform a ip reallocation procedure" },
-       { "freeze",          control_freeze,            true,   false,  "freeze databases", "[priority:1-3]" },
        { "thaw",            control_thaw,              true,   false,  "thaw databases", "[priority:1-3]" },
        { "isnotrecmaster",  control_isnotrecmaster,    false,  false,  "check if the local node is recmaster or not" },
        { "killtcp",         kill_tcp,                  false,  false, "kill a tcp connection.", "<srcip:port> <dstip:port>" },
        { "gratiousarp",     control_gratious_arp,      false,  false, "send a gratious arp", "<ip> <interface>" },
        { "tickle",          tickle_tcp,                false,  false, "send a tcp tickle ack", "<srcip:port> <dstip:port>" },
-       { "gettickles",      control_get_tickles,       false,  false, "get the list of tickles registered for this ip", "<ip>" },
+       { "gettickles",      control_get_tickles,       false,  false, "get the list of tickles registered for this ip", "<ip> [<port>]" },
+       { "addtickle",       control_add_tickle,        false,  false, "add a tickle for this ip", "<ip>:<port> <ip>:<port>" },
+
+       { "deltickle",       control_del_tickle,        false,  false, "delete a tickle from this ip", "<ip>:<port> <ip>:<port>" },
 
        { "regsrvid",        regsrvid,                  false,  false, "register a server id", "<pnn> <type> <id>" },
        { "unregsrvid",      unregsrvid,                false,  false, "unregister a server id", "<pnn> <type> <id>" },
@@ -3487,10 +4677,12 @@ static const struct {
        { "delip",           control_delip,             false,  false, "delete an ip address from a node", "<ip>"},
        { "eventscript",     control_eventscript,       true,   false, "run the eventscript with the given parameters on a node", "<arguments>"},
        { "backupdb",        control_backupdb,          false,  false, "backup the database into a file.", "<database> <file>"},
-       { "restoredb",        control_restoredb,        false,  false, "restore the database from a file.", "<file>"},
+       { "restoredb",        control_restoredb,        false,  false, "restore the database from a file.", "<file> [dbname]"},
+       { "dumpdbbackup",    control_dumpdbbackup,      false,  true,  "dump database backup from a file.", "<file>"},
+       { "wipedb",           control_wipedb,        false,     false, "wipe the contents of a database.", "<dbname>"},
        { "recmaster",        control_recmaster,        false,  false, "show the pnn for the recovery master."},
        { "setflags",        control_setflags,          false,  false, "set flags for a node in the nodemap.", "<node> <flags>"},
-       { "scriptstatus",    control_scriptstatus,  false,      false, "show the status of the monitoring scripts"},
+       { "scriptstatus",    control_scriptstatus,  false,      false, "show the status of the monitoring scripts (or all scripts)", "[all]"},
        { "enablescript",     control_enablescript,  false,     false, "enable an eventscript", "<script>"},
        { "disablescript",    control_disablescript,  false,    false, "disable an eventscript", "<script>"},
        { "natgwlist",        control_natgwlist,        false,  false, "show the nodes belonging to this natgw configuration"},
@@ -3502,6 +4694,11 @@ static const struct {
        { "setrecmasterrole", control_setrecmasterrole, false,  false, "Set RECMASTER role to on/off", "{on|off}"},
        { "setdbprio",        control_setdbprio,        false,  false, "Set DB priority", "<dbid> <prio:1-3>"},
        { "getdbprio",        control_getdbprio,        false,  false, "Get DB priority", "<dbid>"},
+       { "msglisten",        control_msglisten,        false,  false, "Listen on a srvid port for messages", "<msg srvid>"},
+       { "msgsend",          control_msgsend,  false,  false, "Send a message to srvid", "<srvid> <message>"},
+       { "sync",            control_ipreallocate,      true,   false,  "wait until ctdbd has synced all state changes" },
+       { "pfetch",          control_pfetch,            true,   false,  "fetch a record from a persistent database", "<db> <key>" },
+       { "pstore",          control_pstore,            true,   false,  "write a record to a persistent database", "<db> <key> <file containing record>" },
 };
 
 /*
@@ -3515,6 +4712,7 @@ static void usage(void)
 "Options:\n" \
 "   -n <node>          choose node number, or 'all' (defaults to local node)\n"
 "   -Y                 generate machinereadable output\n"
+"   -v                 generate verbose output\n"
 "   -t <timelimit>     set timelimit for control in seconds (default %u)\n", options.timelimit);
        printf("Controls:\n");
        for (i=0;i<ARRAY_SIZE(ctdb_commands);i++) {
@@ -3546,6 +4744,7 @@ int main(int argc, const char *argv[])
                { "timelimit", 't', POPT_ARG_INT, &options.timelimit, 0, "timelimit", "integer" },
                { "node",      'n', POPT_ARG_STRING, &nodestring, 0, "node", "integer|all" },
                { "machinereadable", 'Y', POPT_ARG_NONE, &options.machinereadable, 0, "enable machinereadable output", NULL },
+               { "verbose",    'v', POPT_ARG_NONE, &options.verbose, 0, "enable verbose output", NULL },
                { "maxruntime", 'T', POPT_ARG_INT, &options.maxruntime, 0, "die if runtime exceeds this limit (in seconds)", "integer" },
                POPT_TABLEEND
        };
@@ -3612,6 +4811,11 @@ int main(int argc, const char *argv[])
        control = extra_argv[0];
 
        ev = event_context_init(NULL);
+       if (!ev) {
+               DEBUG(DEBUG_ERR, ("Failed to initialize event system\n"));
+               exit(1);
+       }
+       tevent_loop_allow_nesting(ev);
 
        for (i=0;i<ARRAY_SIZE(ctdb_commands);i++) {
                if (strcmp(control, ctdb_commands[i].name) == 0) {
@@ -3625,11 +4829,22 @@ int main(int argc, const char *argv[])
                        ctdb = ctdb_cmdline_client(ev);
 
                        if (ctdb_commands[i].without_daemon == false) {
+                               const char *socket_name;
+
                                if (ctdb == NULL) {
                                        DEBUG(DEBUG_ERR, ("Failed to init ctdb\n"));
                                        exit(1);
                                }
 
+                               /* initialize a libctdb connection as well */
+                               socket_name = ctdb_get_socketname(ctdb);
+                               ctdb_connection = ctdb_connect(socket_name,
+                                                      ctdb_log_file, stderr);
+                               if (ctdb_connection == NULL) {
+                                       fprintf(stderr, "Failed to connect to daemon from libctdb\n");
+                                       exit(1);
+                               }                               
+                       
                                /* verify the node exists */
                                verify_node(ctdb);