add missing checks on so far ignored return values
[samba.git] / ctdb / tools / ctdb.c
index c8cfe80e5b1aaa72545ffab984a874baddfe89d9..57e1a8dffe351e40694578649c50a7ce342ed00b 100644 (file)
 #include "../include/ctdb.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 */
 
 static void usage(void);
 
@@ -51,6 +57,70 @@ static int control_version(struct ctdb_context *ctdb, int argc, const char **arg
 }
 #endif
 
+
+/*
+  verify that a node exists and is reachable
+ */
+static void verify_node(struct ctdb_context *ctdb)
+{
+       int ret;
+       struct ctdb_node_map *nodemap=NULL;
+
+       if (options.pnn == CTDB_CURRENT_NODE) {
+               return;
+       }
+       if (options.pnn == CTDB_BROADCAST_ALL) {
+               return;
+       }
+
+       /* verify the node exists */
+       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 (options.pnn >= nodemap->num) {
+               DEBUG(DEBUG_ERR, ("Node %u does not exist\n", options.pnn));
+               exit(ERR_NONODE);
+       }
+       if (nodemap->nodes[options.pnn].flags & NODE_FLAGS_DISCONNECTED) {
+               DEBUG(DEBUG_ERR, ("Node %u is DISCONNECTED\n", options.pnn));
+               exit(ERR_DISNODE);
+       }
+
+       /* 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"));
+               exit(10);
+       }
+}
+
+/*
+ check if a database exists
+*/
+static int db_exists(struct ctdb_context *ctdb, const char *db_name)
+{
+       int i, ret;
+       struct ctdb_dbid_map *dbmap=NULL;
+
+       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 -1;
+       }
+
+       for(i=0;i<dbmap->num;i++){
+               const char *name;
+
+               ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.pnn, dbmap->dbs[i].dbid, ctdb, &name);
+               if (!strcmp(name, db_name)) {
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
 /*
   see if a process exists
  */
@@ -139,6 +209,9 @@ static void show_statistics(struct ctdb_statistics *s)
                       preflen?0:4, "",
                       *(uint32_t *)(fields[i].offset+(uint8_t *)s));
        }
+       printf(" %-30s     %.6f sec\n", "max_reclock_ctdbd", s->reclock.ctdbd);
+       printf(" %-30s     %.6f sec\n", "max_reclock_recd", s->reclock.recd);
+
        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);
@@ -232,15 +305,9 @@ static int control_statistics_reset(struct ctdb_context *ctdb, int argc, const c
 static int control_uptime(struct ctdb_context *ctdb, int argc, const char **argv)
 {
        int ret;
-       int mypnn;
        struct ctdb_uptime *uptime = NULL;
        int tmp, days, hours, minutes, seconds;
 
-       mypnn = ctdb_ctrl_getpnn(ctdb, TIMELIMIT(), options.pnn);
-       if (mypnn == -1) {
-               return -1;
-       }
-
        ret = ctdb_ctrl_uptime(ctdb, ctdb, TIMELIMIT(), options.pnn, &uptime);
        if (ret != 0) {
                DEBUG(DEBUG_ERR, ("Unable to get uptime from node %u\n", options.pnn));
@@ -248,11 +315,14 @@ 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:\n");
-               printf(":%u:%u:%u:\n",
+               printf(":Current Node Time:Ctdb Start Time:Last Recovery Time:Last Recovery Duration:\n");
+               printf(":%u:%u:%u:%lf\n",
                        (unsigned int)uptime->current_time.tv_sec,
                        (unsigned int)uptime->ctdbd_start_time.tv_sec,
-                       (unsigned int)uptime->last_recovery_time.tv_sec);
+                       (unsigned int)uptime->last_recovery_finished.tv_sec,
+                       timeval_delta(&uptime->last_recovery_finished,
+                                     &uptime->last_recovery_started)
+               );
                return 0;
        }
 
@@ -268,7 +338,7 @@ static int control_uptime(struct ctdb_context *ctdb, int argc, const char **argv
        days    = tmp;
        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_time.tv_sec;
+       tmp = uptime->current_time.tv_sec - uptime->last_recovery_finished.tv_sec;
        seconds = tmp%60;
        tmp    /= 60;
        minutes = tmp%60;
@@ -276,11 +346,107 @@ 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_time.tv_sec));
+       printf("Time of last recovery : (%03d %02d:%02d:%02d) %s", days, hours, minutes, seconds, ctime(&uptime->last_recovery_finished.tv_sec));
+       
+       printf("Duration of last recovery : %lf seconds\n",
+               timeval_delta(&uptime->last_recovery_finished,
+                             &uptime->last_recovery_started));
+
+       return 0;
+}
+
+/*
+  show the PNN of the current node
+ */
+static int control_pnn(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       int mypnn;
+
+       mypnn = ctdb_ctrl_getpnn(ctdb, TIMELIMIT(), options.pnn);
+       if (mypnn == -1) {
+               DEBUG(DEBUG_ERR, ("Unable to get pnn from local node."));
+               return -1;
+       }
 
+       printf("PNN:%d\n", mypnn);
        return 0;
 }
 
+
+struct pnn_node {
+       struct pnn_node *next;
+       const char *addr;
+       int pnn;
+};
+
+/*
+  show the PNN of the current node
+  discover the pnn by loading the nodes file and try to bind to all
+  addresses one at a time until the ip address is found.
+ */
+static int control_xpnn(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+       const char *nodes_list;
+       int nlines;
+       char **lines;
+       struct pnn_node *pnn_nodes = NULL;
+       struct pnn_node *pnn_node;
+       int i, pnn;
+
+       /* read the nodes file */
+       nodes_list = getenv("CTDB_NODES");
+       if (nodes_list == NULL) {
+               nodes_list = "/etc/ctdb/nodes";
+       }
+       lines = file_lines_load(nodes_list, &nlines, mem_ctx);
+       if (lines == NULL) {
+               ctdb_set_error(ctdb, "Failed to load nodes list '%s'\n", nodes_list);
+               return -1;
+       }
+       while (nlines > 0 && strcmp(lines[nlines-1], "") == 0) {
+               nlines--;
+       }
+       for (i=0, pnn=0; i<nlines; i++) {
+               char *node;
+
+               node = lines[i];
+               /* strip leading spaces */
+               while((*node == ' ') || (*node == '\t')) {
+                       node++;
+               }
+               if (*node == '#') {
+                       continue;
+               }
+               if (strcmp(node, "") == 0) {
+                       continue;
+               }
+               pnn_node = talloc(mem_ctx, struct pnn_node);
+               pnn_node->pnn = pnn++;
+               pnn_node->addr = talloc_strdup(pnn_node, node);
+               CTDB_NO_MEMORY(ctdb, pnn_node->addr);
+               pnn_node->next = pnn_nodes;
+               pnn_nodes = pnn_node;
+       }
+
+       for(pnn_node=pnn_nodes;pnn_node;pnn_node=pnn_node->next) {
+               ctdb_sock_addr addr;
+
+               if (parse_ip(pnn_node->addr, NULL, 63999, &addr) == 0) {
+                       DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s' in nodes file %s\n", pnn_node->addr, nodes_list));
+                       return -1;
+               }
+
+               if (ctdb_sys_have_ip(&addr)) {
+                       printf("PNN:%d\n", pnn_node->pnn);
+                       return 0;
+               }
+       }
+
+       printf("Failed to detect which PNN this node is\n");
+       return -1;
+}
+
 /*
   display remote ctdb status
  */
@@ -307,7 +473,7 @@ static int control_status(struct ctdb_context *ctdb, int argc, const char **argv
                printf(":Node:IP:Disconnected:Banned:Disabled:Unhealthy:\n");
                for(i=0;i<nodemap->num;i++){
                        printf(":%d:%s:%d:%d:%d:%d:\n", nodemap->nodes[i].pnn,
-                               inet_ntoa(nodemap->nodes[i].sin.sin_addr),
+                               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),
@@ -345,7 +511,7 @@ static int control_status(struct ctdb_context *ctdb, int argc, const char **argv
                        CTDB_NO_MEMORY_FATAL(ctdb, flags_str);
                }
                printf("pnn:%d %-16s %s%s\n", nodemap->nodes[i].pnn,
-                      inet_ntoa(nodemap->nodes[i].sin.sin_addr),
+                      ctdb_addr_to_str(&nodemap->nodes[i].addr),
                       flags_str,
                       nodemap->nodes[i].pnn == mypnn?" (THIS NODE)":"");
                talloc_free(flags_str);
@@ -383,36 +549,197 @@ static int control_status(struct ctdb_context *ctdb, int argc, const char **argv
        return 0;
 }
 
+
+struct natgw_node {
+       struct natgw_node *next;
+       const char *addr;
+};
+
+/*
+  display the list of nodes belonging to this natgw configuration
+ */
+static int control_natgwlist(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       int i, ret;
+       const char *natgw_list;
+       int nlines;
+       char **lines;
+       struct natgw_node *natgw_nodes = NULL;
+       struct natgw_node *natgw_node;
+       struct ctdb_node_map *nodemap=NULL;
+
+
+       /* read the natgw nodes file into a linked list */
+       natgw_list = getenv("NATGW_NODES");
+       if (natgw_list == NULL) {
+               natgw_list = "/etc/ctdb/natgw_nodes";
+       }
+       lines = file_lines_load(natgw_list, &nlines, ctdb);
+       if (lines == NULL) {
+               ctdb_set_error(ctdb, "Failed to load natgw node list '%s'\n", natgw_list);
+               return -1;
+       }
+       while (nlines > 0 && strcmp(lines[nlines-1], "") == 0) {
+               nlines--;
+       }
+       for (i=0;i<nlines;i++) {
+               char *node;
+
+               node = lines[i];
+               /* strip leading spaces */
+               while((*node == ' ') || (*node == '\t')) {
+                       node++;
+               }
+               if (*node == '#') {
+                       continue;
+               }
+               if (strcmp(node, "") == 0) {
+                       continue;
+               }
+               natgw_node = talloc(ctdb, struct natgw_node);
+               natgw_node->addr = talloc_strdup(natgw_node, node);
+               CTDB_NO_MEMORY(ctdb, natgw_node->addr);
+               natgw_node->next = natgw_nodes;
+               natgw_nodes = natgw_node;
+       }
+
+       ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node.\n"));
+               return ret;
+       }
+
+       i=0;
+       while(i<nodemap->num) {
+               for(natgw_node=natgw_nodes;natgw_node;natgw_node=natgw_node->next) {
+                       if (!strcmp(natgw_node->addr, ctdb_addr_to_str(&nodemap->nodes[i].addr))) {
+                               break;
+                       }
+               }
+
+               /* this node was not in the natgw so we just remove it from
+                * the list
+                */
+               if ((natgw_node == NULL) 
+               ||  (nodemap->nodes[i].flags & NODE_FLAGS_DISCONNECTED) ) {
+                       int j;
+
+                       for (j=i+1; j<nodemap->num; j++) {
+                               nodemap->nodes[j-1] = nodemap->nodes[j];
+                       }
+                       nodemap->num--;
+                       continue;
+               }
+
+               i++;
+       }               
+
+       /* print the natgw master */
+       for(i=0;i<nodemap->num;i++){
+               if (!(nodemap->nodes[i].flags & NODE_FLAGS_DISCONNECTED)) {
+                       printf("%d\n", nodemap->nodes[i].pnn);
+                       break;
+               }
+       }
+
+       /* print the pruned list of nodes belonging to this natgw list */
+       for(i=0;i<nodemap->num;i++){
+               printf(":%d:%s:%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));
+       }
+
+       return 0;
+}
+
+
+/*
+  display the status of the monitoring scripts
+ */
+static int control_scriptstatus(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       int i, ret;
+       struct ctdb_monitoring_wire *script_status;
+
+       ret = ctdb_ctrl_getscriptstatus(ctdb, TIMELIMIT(), options.pnn, ctdb, &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);
+       for (i=0; i<script_status->num_scripts; i++) {
+               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) {
+                       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);
+               }
+       }
+
+       return 0;
+}
+       
+
+/*
+  display the pnn of the recovery master
+ */
+static int control_recmaster(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       int ret;
+       uint32_t recmaster;
+
+       ret = ctdb_ctrl_getrecmaster(ctdb, ctdb, TIMELIMIT(), options.pnn, &recmaster);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get recmaster from node %u\n", options.pnn));
+               return ret;
+       }
+       printf("%d\n",recmaster);
+
+       return 0;
+}
+
 /*
   get a list of all tickles for this pnn
  */
 static int control_get_tickles(struct ctdb_context *ctdb, int argc, const char **argv)
 {
        struct ctdb_control_tcp_tickle_list *list;
-       struct sockaddr_in ip;
+       ctdb_sock_addr addr;
        int i, ret;
 
        if (argc < 1) {
                usage();
        }
 
-       ip.sin_family = AF_INET;
-       if (inet_aton(argv[0], &ip.sin_addr) == 0) {
+       if (parse_ip(argv[0], NULL, 0, &addr) == 0) {
                DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0]));
                return -1;
        }
 
-       ret = ctdb_ctrl_get_tcp_tickles(ctdb, TIMELIMIT(), options.pnn, ctdb, &ip, &list);
+       ret = ctdb_ctrl_get_tcp_tickles(ctdb, TIMELIMIT(), options.pnn, ctdb, &addr, &list);
        if (ret == -1) {
                DEBUG(DEBUG_ERR, ("Unable to list tickles\n"));
                return -1;
        }
 
-       printf("Tickles for ip:%s\n", inet_ntoa(list->ip.sin_addr));
+       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   ", inet_ntoa(list->tickles.connections[i].saddr.sin_addr), ntohs(list->tickles.connections[i].saddr.sin_port));
-               printf("DST: %s:%u\n", inet_ntoa(list->tickles.connections[i].daddr.sin_addr), ntohs(list->tickles.connections[i].daddr.sin_port));
+               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);
@@ -422,7 +749,7 @@ static int control_get_tickles(struct ctdb_context *ctdb, int argc, const char *
 
 /* send a release ip to all nodes */
 static int control_send_release(struct ctdb_context *ctdb, uint32_t pnn,
-struct sockaddr_in *sin)
+ctdb_sock_addr *addr)
 {
        int ret;
        struct ctdb_public_ip pip;
@@ -436,17 +763,17 @@ struct sockaddr_in *sin)
        }
 
        /* send a moveip message to the recovery master */
-       pip.pnn = pnn;
-       pip.sin.sin_family = AF_INET;
-       pip.sin.sin_addr   = sin->sin_addr;
+       pip.pnn    = pnn;
+       pip.addr   = *addr;
        data.dsize = sizeof(pip);
-       data.dptr = (unsigned char *)&pip;
+       data.dptr  = (unsigned char *)&pip;
 
 
        /* send release ip to all nodes */
        if (ctdb_client_async_control(ctdb, CTDB_CONTROL_RELEASE_IP,
                        list_of_active_nodes(ctdb, nodemap, ctdb, true),
-                       TIMELIMIT(), false, data, NULL) != 0) {
+                       TIMELIMIT(), false, data,
+                       NULL, NULL, NULL) != 0) {
                DEBUG(DEBUG_ERR, (__location__ " Unable to send 'ReleaseIP' to all nodes.\n"));
                return -1;
        }
@@ -460,7 +787,7 @@ struct sockaddr_in *sin)
 static int control_moveip(struct ctdb_context *ctdb, int argc, const char **argv)
 {
        uint32_t pnn;
-       struct sockaddr_in ip;
+       ctdb_sock_addr addr;
        uint32_t value;
        struct ctdb_all_public_ips *ips;
        int i, ret;
@@ -469,8 +796,7 @@ static int control_moveip(struct ctdb_context *ctdb, int argc, const char **argv
                usage();
        }
 
-       ip.sin_family = AF_INET;
-       if (inet_aton(argv[0], &ip.sin_addr) == 0) {
+       if (parse_ip(argv[0], NULL, 0, &addr) == 0) {
                DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0]));
                return -1;
        }
@@ -509,22 +835,22 @@ static int control_moveip(struct ctdb_context *ctdb, int argc, const char **argv
        }
 
        for (i=0;i<ips->num;i++) {
-               if (ctdb_same_ip(&ip, &ips->ips[i].sin)) {
+               if (ctdb_same_ip(&addr, &ips->ips[i].addr)) {
                        break;
                }
        }
        if (i==ips->num) {
                DEBUG(DEBUG_ERR, ("Node %u can not host ip address '%s'\n",
-                       pnn, inet_ntoa(ip.sin_addr)));
+                       pnn, ctdb_addr_to_str(&addr)));
                return -1;
        }
        if (ips->ips[i].pnn == pnn) {
                DEBUG(DEBUG_ERR, ("Host %u is already hosting '%s'\n",
-                       pnn, inet_ntoa(ips->ips[i].sin.sin_addr)));
+                       pnn, ctdb_addr_to_str(&ips->ips[i].addr)));
                return -1;
        }
 
-       ret = control_send_release(ctdb, pnn, &ips->ips[i].sin);
+       ret = control_send_release(ctdb, pnn, &ips->ips[i].addr);
        if (ret != 0) {
                DEBUG(DEBUG_ERR, ("Failed to send 'change ip' to all nodes\n"));;
                return -1;
@@ -533,20 +859,15 @@ static int control_moveip(struct ctdb_context *ctdb, int argc, const char **argv
        return 0;
 }
 
-struct node_ip {
-       uint32_t pnn;
-       struct sockaddr_in sin;
-};
-
 void getips_store_callback(void *param, void *data)
 {
-       struct node_ip *node_ip = (struct node_ip *)data;
+       struct ctdb_public_ip *node_ip = (struct ctdb_public_ip *)data;
        struct ctdb_all_public_ips *ips = param;
        int i;
 
        i = ips->num++;
-       ips->ips[i].pnn = node_ip->pnn;
-       ips->ips[i].sin = node_ip->sin;
+       ips->ips[i].pnn  = node_ip->pnn;
+       ips->ips[i].addr = node_ip->addr;
 }
 
 void getips_count_callback(void *param, void *data)
@@ -556,12 +877,42 @@ void getips_count_callback(void *param, void *data)
        (*count)++;
 }
 
+#define IP_KEYLEN      4
+static uint32_t *ip_key(ctdb_sock_addr *ip)
+{
+       static uint32_t key[IP_KEYLEN];
+
+       bzero(key, sizeof(key));
+
+       switch (ip->sa.sa_family) {
+       case AF_INET:
+               key[0]  = ip->ip.sin_addr.s_addr;
+               break;
+       case AF_INET6:
+               key[0]  = ip->ip6.sin6_addr.s6_addr32[3];
+               key[1]  = ip->ip6.sin6_addr.s6_addr32[2];
+               key[2]  = ip->ip6.sin6_addr.s6_addr32[1];
+               key[3]  = ip->ip6.sin6_addr.s6_addr32[0];
+               break;
+       default:
+               DEBUG(DEBUG_ERR, (__location__ " ERROR, unknown family passed :%u\n", ip->sa.sa_family));
+               return key;
+       }
+
+       return key;
+}
+
+static void *add_ip_callback(void *parm, void *data)
+{
+       return parm;
+}
+
 static int
 control_get_all_public_ips(struct ctdb_context *ctdb, TALLOC_CTX *tmp_ctx, struct ctdb_all_public_ips **ips)
 {
        struct ctdb_all_public_ips *tmp_ips;
        struct ctdb_node_map *nodemap=NULL;
-       trbt_tree_t *tree;
+       trbt_tree_t *ip_tree;
        int i, j, len, ret;
        uint32_t count;
 
@@ -571,7 +922,7 @@ control_get_all_public_ips(struct ctdb_context *ctdb, TALLOC_CTX *tmp_ctx, struc
                return ret;
        }
 
-       tree = trbt_create(tmp_ctx, 0);
+       ip_tree = trbt_create(tmp_ctx, 0);
 
        for(i=0;i<nodemap->num;i++){
                if (nodemap->nodes[i].flags & NODE_FLAGS_DISCONNECTED) {
@@ -586,25 +937,28 @@ control_get_all_public_ips(struct ctdb_context *ctdb, TALLOC_CTX *tmp_ctx, struc
                }
        
                for (j=0; j<tmp_ips->num;j++) {
-                       struct node_ip *node_ip;
+                       struct ctdb_public_ip *node_ip;
 
-                       node_ip = talloc(tmp_ctx, struct node_ip);
-                       node_ip->pnn = tmp_ips->ips[j].pnn;
-                       node_ip->sin = tmp_ips->ips[j].sin;
+                       node_ip = talloc(tmp_ctx, struct ctdb_public_ip);
+                       node_ip->pnn  = tmp_ips->ips[j].pnn;
+                       node_ip->addr = tmp_ips->ips[j].addr;
 
-                       trbt_insert32(tree, tmp_ips->ips[j].sin.sin_addr.s_addr, node_ip);
+                       trbt_insertarray32_callback(ip_tree,
+                               IP_KEYLEN, ip_key(&tmp_ips->ips[j].addr),
+                               add_ip_callback,
+                               node_ip);
                }
                talloc_free(tmp_ips);
        }
 
        /* traverse */
        count = 0;
-       trbt_traversearray32(tree, 1, getips_count_callback, &count);
+       trbt_traversearray32(ip_tree, IP_KEYLEN, getips_count_callback, &count);
 
        len = offsetof(struct ctdb_all_public_ips, ips) + 
                count*sizeof(struct ctdb_public_ip);
        tmp_ips = talloc_zero_size(tmp_ctx, len);
-       trbt_traversearray32(tree, 1, getips_store_callback, tmp_ips);
+       trbt_traversearray32(ip_tree, IP_KEYLEN, getips_store_callback, tmp_ips);
 
        *ips = tmp_ips;
 
@@ -617,7 +971,7 @@ control_get_all_public_ips(struct ctdb_context *ctdb, TALLOC_CTX *tmp_ctx, struc
  * ip address or -1
  */
 static int
-find_other_host_for_public_ip(struct ctdb_context *ctdb, struct sockaddr_in *addr)
+find_other_host_for_public_ip(struct ctdb_context *ctdb, ctdb_sock_addr *addr)
 {
        TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
        struct ctdb_all_public_ips *ips;
@@ -647,7 +1001,7 @@ find_other_host_for_public_ip(struct ctdb_context *ctdb, struct sockaddr_in *add
                }
 
                for (j=0;j<ips->num;j++) {
-                       if (ctdb_same_ip(addr, &ips->ips[j].sin)) {
+                       if (ctdb_same_ip(addr, &ips->ips[j].addr)) {
                                talloc_free(tmp_ctx);
                                return nodemap->nodes[i].pnn;
                        }
@@ -667,7 +1021,7 @@ static int control_addip(struct ctdb_context *ctdb, int argc, const char **argv)
        int i, ret;
        int len;
        unsigned mask;
-       struct sockaddr_in addr;
+       ctdb_sock_addr addr;
        struct ctdb_control_ip_iface *pub;
        TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
        struct ctdb_all_public_ips *ips;
@@ -677,7 +1031,7 @@ static int control_addip(struct ctdb_context *ctdb, int argc, const char **argv)
                usage();
        }
 
-       if (!parse_ip_mask(argv[0], &addr, &mask)) {
+       if (!parse_ip_mask(argv[0], argv[1], &addr, &mask)) {
                DEBUG(DEBUG_ERR, ("Badly formed ip/mask : %s\n", argv[0]));
                talloc_free(tmp_ctx);
                return -1;
@@ -695,7 +1049,7 @@ static int control_addip(struct ctdb_context *ctdb, int argc, const char **argv)
        pub = talloc_size(tmp_ctx, len); 
        CTDB_NO_MEMORY(ctdb, pub);
 
-       pub->sin   = addr;
+       pub->addr  = addr;
        pub->mask  = mask;
        pub->len   = strlen(argv[1])+1;
        memcpy(&pub->iface[0], argv[1], strlen(argv[1])+1);
@@ -712,7 +1066,7 @@ static int control_addip(struct ctdb_context *ctdb, int argc, const char **argv)
         * we will claim it
         */
        for (i=0;i<ips->num;i++) {
-               if (ctdb_same_ip(&addr, &ips->ips[i].sin)) {
+               if (ctdb_same_ip(&addr, &ips->ips[i].addr)) {
                        break;
                }
        }
@@ -732,13 +1086,83 @@ static int control_addip(struct ctdb_context *ctdb, int argc, const char **argv)
        return 0;
 }
 
+static int control_delip(struct ctdb_context *ctdb, int argc, const char **argv);
+
+static int control_delip_all(struct ctdb_context *ctdb, int argc, const char **argv, ctdb_sock_addr *addr)
+{
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+       struct ctdb_node_map *nodemap=NULL;
+       struct ctdb_all_public_ips *ips;
+       int ret, i, j;
+
+       ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, tmp_ctx, &nodemap);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get nodemap from current node\n"));
+               return ret;
+       }
+
+       /* remove it from the nodes that are not hosting the ip currently */
+       for(i=0;i<nodemap->num;i++){
+               if (nodemap->nodes[i].flags & NODE_FLAGS_DISCONNECTED) {
+                       continue;
+               }
+               if (ctdb_ctrl_get_public_ips(ctdb, TIMELIMIT(), nodemap->nodes[i].pnn, tmp_ctx, &ips) != 0) {
+                       DEBUG(DEBUG_ERR, ("Unable to get public ip list from node %d\n", nodemap->nodes[i].pnn));
+                       continue;
+               }
+
+               for (j=0;j<ips->num;j++) {
+                       if (ctdb_same_ip(addr, &ips->ips[j].addr)) {
+                               break;
+                       }
+               }
+               if (j==ips->num) {
+                       continue;
+               }
+
+               if (ips->ips[j].pnn == nodemap->nodes[i].pnn) {
+                       continue;
+               }
+
+               options.pnn = nodemap->nodes[i].pnn;
+               control_delip(ctdb, argc, argv);
+       }
+
+
+       /* remove it from every node (also the one hosting it) */
+       for(i=0;i<nodemap->num;i++){
+               if (nodemap->nodes[i].flags & NODE_FLAGS_DISCONNECTED) {
+                       continue;
+               }
+               if (ctdb_ctrl_get_public_ips(ctdb, TIMELIMIT(), nodemap->nodes[i].pnn, tmp_ctx, &ips) != 0) {
+                       DEBUG(DEBUG_ERR, ("Unable to get public ip list from node %d\n", nodemap->nodes[i].pnn));
+                       continue;
+               }
+
+               for (j=0;j<ips->num;j++) {
+                       if (ctdb_same_ip(addr, &ips->ips[j].addr)) {
+                               break;
+                       }
+               }
+               if (j==ips->num) {
+                       continue;
+               }
+
+               options.pnn = nodemap->nodes[i].pnn;
+               control_delip(ctdb, argc, argv);
+       }
+
+       talloc_free(tmp_ctx);
+       return 0;
+}
+       
 /*
   delete a public ip address from a node
  */
 static int control_delip(struct ctdb_context *ctdb, int argc, const char **argv)
 {
        int i, ret;
-       struct sockaddr_in addr;
+       ctdb_sock_addr addr;
        struct ctdb_control_ip_iface pub;
        TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
        struct ctdb_all_public_ips *ips;
@@ -748,13 +1172,16 @@ static int control_delip(struct ctdb_context *ctdb, int argc, const char **argv)
                usage();
        }
 
-       addr.sin_family = AF_INET;
-       if (inet_aton(argv[0], &addr.sin_addr) == 0) {
+       if (parse_ip(argv[0], NULL, 0, &addr) == 0) {
                DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0]));
                return -1;
        }
 
-       pub.sin   = addr;
+       if (options.pnn == CTDB_BROADCAST_ALL) {
+               return control_delip_all(ctdb, argc, argv, &addr);
+       }
+
+       pub.addr  = addr;
        pub.mask  = 0;
        pub.len   = 0;
 
@@ -766,14 +1193,14 @@ static int control_delip(struct ctdb_context *ctdb, int argc, const char **argv)
        }
        
        for (i=0;i<ips->num;i++) {
-               if (ctdb_same_ip(&addr, &ips->ips[i].sin)) {
+               if (ctdb_same_ip(&addr, &ips->ips[i].addr)) {
                        break;
                }
        }
 
        if (i==ips->num) {
                DEBUG(DEBUG_ERR, ("This node does not support this public address '%s'\n",
-                       inet_ntoa(addr.sin_addr)));
+                       ctdb_addr_to_str(&addr)));
                talloc_free(tmp_ctx);
                return -1;
        }
@@ -811,12 +1238,12 @@ static int kill_tcp(struct ctdb_context *ctdb, int argc, const char **argv)
                usage();
        }
 
-       if (!parse_ip_port(argv[0], (ctdb_sock_addr *)&killtcp.src)) {
+       if (!parse_ip_port(argv[0], &killtcp.src_addr)) {
                DEBUG(DEBUG_ERR, ("Bad IP:port '%s'\n", argv[0]));
                return -1;
        }
 
-       if (!parse_ip_port(argv[1], (ctdb_sock_addr *)&killtcp.dst)) {
+       if (!parse_ip_port(argv[1], &killtcp.dst_addr)) {
                DEBUG(DEBUG_ERR, ("Bad IP:port '%s'\n", argv[1]));
                return -1;
        }
@@ -837,19 +1264,18 @@ static int kill_tcp(struct ctdb_context *ctdb, int argc, const char **argv)
 static int control_gratious_arp(struct ctdb_context *ctdb, int argc, const char **argv)
 {
        int ret;
-       struct sockaddr_in sin;
+       ctdb_sock_addr addr;
 
        if (argc < 2) {
                usage();
        }
 
-       sin.sin_family = AF_INET;
-       if (inet_aton(argv[0], &sin.sin_addr) == 0) {
-               DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0]));
+       if (!parse_ip(argv[0], NULL, 0, &addr)) {
+               DEBUG(DEBUG_ERR, ("Bad IP '%s'\n", argv[0]));
                return -1;
        }
 
-       ret = ctdb_ctrl_gratious_arp(ctdb, TIMELIMIT(), options.pnn, &sin, argv[1]);
+       ret = ctdb_ctrl_gratious_arp(ctdb, TIMELIMIT(), options.pnn, &addr, argv[1]);
        if (ret != 0) {
                DEBUG(DEBUG_ERR, ("Unable to send gratious_arp from node %u\n", options.pnn));
                return ret;
@@ -1027,9 +1453,9 @@ static int control_ip(struct ctdb_context *ctdb, int argc, const char **argv)
 
        for (i=1;i<=ips->num;i++) {
                if (options.machinereadable){
-                       printf(":%s:%d:\n", inet_ntoa(ips->ips[ips->num-i].sin.sin_addr), ips->ips[ips->num-i].pnn);
+                       printf(":%s:%d:\n", ctdb_addr_to_str(&ips->ips[ips->num-i].addr), ips->ips[ips->num-i].pnn);
                } else {
-                       printf("%s %d\n", inet_ntoa(ips->ips[ips->num-i].sin.sin_addr), ips->ips[ips->num-i].pnn);
+                       printf("%s %d\n", ctdb_addr_to_str(&ips->ips[ips->num-i].addr), ips->ips[ips->num-i].pnn);
                }
        }
 
@@ -1087,6 +1513,49 @@ static int control_enable(struct ctdb_context *ctdb, int argc, const char **argv
        return 0;
 }
 
+static uint32_t get_generation(struct ctdb_context *ctdb)
+{
+       struct ctdb_vnn_map *vnnmap=NULL;
+       int ret;
+
+       /* wait until the recmaster is not in recovery mode */
+       while (1) {
+               uint32_t recmode, recmaster;
+               
+               if (vnnmap != NULL) {
+                       talloc_free(vnnmap);
+                       vnnmap = NULL;
+               }
+
+               /* get the recmaster */
+               ret = ctdb_ctrl_getrecmaster(ctdb, ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, &recmaster);
+               if (ret != 0) {
+                       DEBUG(DEBUG_ERR, ("Unable to get recmaster from node %u\n", options.pnn));
+                       exit(10);
+               }
+
+               /* get recovery mode */
+               ret = ctdb_ctrl_getrecmode(ctdb, ctdb, TIMELIMIT(), recmaster, &recmode);
+               if (ret != 0) {
+                       DEBUG(DEBUG_ERR, ("Unable to get recmode from node %u\n", options.pnn));
+                       exit(10);
+               }
+
+               /* get the current generation number */
+               ret = ctdb_ctrl_getvnnmap(ctdb, TIMELIMIT(), recmaster, ctdb, &vnnmap);
+               if (ret != 0) {
+                       DEBUG(DEBUG_ERR, ("Unable to get vnnmap from recmaster (%u)\n", recmaster));
+                       exit(10);
+               }
+
+               if ((recmode == CTDB_RECOVERY_NORMAL)
+               &&  (vnnmap->generation != 1)){
+                       return vnnmap->generation;
+               }
+               sleep(1);
+       }
+}
+
 /*
   ban a node from the cluster
  */
@@ -1096,10 +1565,28 @@ static int control_ban(struct ctdb_context *ctdb, int argc, const char **argv)
        struct ctdb_ban_info b;
        TDB_DATA data;
        uint32_t ban_time;
+       struct ctdb_node_map *nodemap=NULL;
+       uint32_t generation, next_generation;
 
        if (argc < 1) {
                usage();
        }
+       
+       /* record the current generation number */
+       generation = get_generation(ctdb);
+
+
+       /* verify the node exists */
+       ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n"));
+               return ret;
+       }
+
+       if (nodemap->nodes[options.pnn].flags & NODE_FLAGS_BANNED) {
+               DEBUG(DEBUG_ERR,("Node %u is already banned.\n", options.pnn));
+               return -1;
+       }
 
        ban_time = strtoul(argv[0], NULL, 0);
 
@@ -1114,7 +1601,16 @@ static int control_ban(struct ctdb_context *ctdb, int argc, const char **argv)
                DEBUG(DEBUG_ERR,("Failed to ban node %u\n", options.pnn));
                return -1;
        }
-       
+
+       /* wait until we are in a new generation */
+       while (1) {
+               next_generation = get_generation(ctdb);
+               if (next_generation != generation) {
+                       return 0;
+               }
+               sleep(1);
+       }
+
        return 0;
 }
 
@@ -1126,16 +1622,41 @@ static int control_unban(struct ctdb_context *ctdb, int argc, const char **argv)
 {
        int ret;
        TDB_DATA data;
+       uint32_t generation, next_generation;
+       struct ctdb_node_map *nodemap=NULL;
+
+       /* record the current generation number */
+       generation = get_generation(ctdb);
 
        data.dptr = (uint8_t *)&options.pnn;
        data.dsize = sizeof(uint32_t);
 
+       ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, ctdb, &nodemap);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR, ("Unable to get nodemap from local node\n"));
+               return ret;
+       }
+
+       if (!(nodemap->nodes[options.pnn].flags & NODE_FLAGS_BANNED)) {
+               DEBUG(DEBUG_ERR, ("Node %d is not banned. Can not unban\n", options.pnn));
+               return -1;
+       }
+
        ret = ctdb_send_message(ctdb, options.pnn, CTDB_SRVID_UNBAN_NODE, data);
        if (ret != 0) {
                DEBUG(DEBUG_ERR,("Failed to to unban node %u\n", options.pnn));
                return -1;
        }
        
+       /* wait until we are in a new generation */
+       while (1) {
+               next_generation = get_generation(ctdb);
+               if (next_generation != generation) {
+                       return 0;
+               }
+               sleep(1);
+       }
+
        return 0;
 }
 
@@ -1162,6 +1683,10 @@ static int control_shutdown(struct ctdb_context *ctdb, int argc, const char **ar
 static int control_recover(struct ctdb_context *ctdb, int argc, const char **argv)
 {
        int ret;
+       uint32_t generation, next_generation;
+
+       /* record the current generation number */
+       generation = get_generation(ctdb);
 
        ret = ctdb_ctrl_freeze(ctdb, TIMELIMIT(), options.pnn);
        if (ret != 0) {
@@ -1175,6 +1700,15 @@ static int control_recover(struct ctdb_context *ctdb, int argc, const char **arg
                return ret;
        }
 
+       /* wait until we are in a new generation */
+       while (1) {
+               next_generation = get_generation(ctdb);
+               if (next_generation != generation) {
+                       return 0;
+               }
+               sleep(1);
+       }
+
        return 0;
 }
 
@@ -1219,15 +1753,158 @@ static int control_getcapabilities(struct ctdb_context *ctdb, int argc, const ch
        if (!options.machinereadable){
                printf("RECMASTER: %s\n", (capabilities&CTDB_CAP_RECMASTER)?"YES":"NO");
                printf("LMASTER: %s\n", (capabilities&CTDB_CAP_LMASTER)?"YES":"NO");
+               printf("LVS: %s\n", (capabilities&CTDB_CAP_LVS)?"YES":"NO");
        } else {
-               printf(":RECMASTER:LMASTER:\n");
-               printf(":%d:%d:\n",
+               printf(":RECMASTER:LMASTER:LVS:\n");
+               printf(":%d:%d:%d:\n",
                        !!(capabilities&CTDB_CAP_RECMASTER),
-                       !!(capabilities&CTDB_CAP_LMASTER));
+                       !!(capabilities&CTDB_CAP_LMASTER),
+                       !!(capabilities&CTDB_CAP_LVS));
        }
        return 0;
 }
 
+/*
+  display lvs configuration
+ */
+static int control_lvs(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       uint32_t *capabilities;
+       struct ctdb_node_map *nodemap=NULL;
+       int i, ret;
+       int healthy_count = 0;
+
+       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));
+               return ret;
+       }
+
+       capabilities = talloc_array(ctdb, uint32_t, nodemap->num);
+       CTDB_NO_MEMORY(ctdb, capabilities);
+       
+       /* collect capabilities for all connected nodes */
+       for (i=0; i<nodemap->num; i++) {
+               if (nodemap->nodes[i].flags & NODE_FLAGS_INACTIVE) {
+                       continue;
+               }
+               if (nodemap->nodes[i].flags & NODE_FLAGS_PERMANENTLY_DISABLED) {
+                       continue;
+               }
+       
+               ret = ctdb_ctrl_getcapabilities(ctdb, TIMELIMIT(), i, &capabilities[i]);
+               if (ret != 0) {
+                       DEBUG(DEBUG_ERR, ("Unable to get capabilities from node %u\n", i));
+                       return ret;
+               }
+
+               if (!(capabilities[i] & CTDB_CAP_LVS)) {
+                       continue;
+               }
+
+               if (!(nodemap->nodes[i].flags & NODE_FLAGS_UNHEALTHY)) {
+                       healthy_count++;
+               }
+       }
+
+       /* Print all LVS nodes */
+       for (i=0; i<nodemap->num; i++) {
+               if (nodemap->nodes[i].flags & NODE_FLAGS_INACTIVE) {
+                       continue;
+               }
+               if (nodemap->nodes[i].flags & NODE_FLAGS_PERMANENTLY_DISABLED) {
+                       continue;
+               }
+               if (!(capabilities[i] & CTDB_CAP_LVS)) {
+                       continue;
+               }
+
+               if (healthy_count != 0) {
+                       if (nodemap->nodes[i].flags & NODE_FLAGS_UNHEALTHY) {
+                               continue;
+                       }
+               }
+
+               printf("%d:%s\n", i, 
+                       ctdb_addr_to_str(&nodemap->nodes[i].addr));
+       }
+
+       return 0;
+}
+
+/*
+  display who is the lvs master
+ */
+static int control_lvsmaster(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       uint32_t *capabilities;
+       struct ctdb_node_map *nodemap=NULL;
+       int i, ret;
+       int healthy_count = 0;
+
+       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));
+               return ret;
+       }
+
+       capabilities = talloc_array(ctdb, uint32_t, nodemap->num);
+       CTDB_NO_MEMORY(ctdb, capabilities);
+       
+       /* collect capabilities for all connected nodes */
+       for (i=0; i<nodemap->num; i++) {
+               if (nodemap->nodes[i].flags & NODE_FLAGS_INACTIVE) {
+                       continue;
+               }
+               if (nodemap->nodes[i].flags & NODE_FLAGS_PERMANENTLY_DISABLED) {
+                       continue;
+               }
+       
+               ret = ctdb_ctrl_getcapabilities(ctdb, TIMELIMIT(), i, &capabilities[i]);
+               if (ret != 0) {
+                       DEBUG(DEBUG_ERR, ("Unable to get capabilities from node %u\n", i));
+                       return ret;
+               }
+
+               if (!(capabilities[i] & CTDB_CAP_LVS)) {
+                       continue;
+               }
+
+               if (!(nodemap->nodes[i].flags & NODE_FLAGS_UNHEALTHY)) {
+                       healthy_count++;
+               }
+       }
+
+       /* find and show the lvsmaster */
+       for (i=0; i<nodemap->num; i++) {
+               if (nodemap->nodes[i].flags & NODE_FLAGS_INACTIVE) {
+                       continue;
+               }
+               if (nodemap->nodes[i].flags & NODE_FLAGS_PERMANENTLY_DISABLED) {
+                       continue;
+               }
+               if (!(capabilities[i] & CTDB_CAP_LVS)) {
+                       continue;
+               }
+
+               if (healthy_count != 0) {
+                       if (nodemap->nodes[i].flags & NODE_FLAGS_UNHEALTHY) {
+                               continue;
+                       }
+               }
+
+               if (options.machinereadable){
+                       printf("%d\n", i);
+               } else {
+                       printf("Node %d is LVS master\n", i);
+               }
+               return 0;
+       }
+
+       printf("There is no LVS master\n");
+       return -1;
+}
+
 /*
   disable monitoring on a  node
  */
@@ -1278,7 +1955,14 @@ static int control_catdb(struct ctdb_context *ctdb, int argc, const char **argv)
        }
 
        db_name = argv[0];
-       ctdb_db = ctdb_attach(ctdb, db_name, false);
+
+
+       if (db_exists(ctdb, db_name)) {
+               DEBUG(DEBUG_ERR,("Database '%s' does not exist\n", db_name));
+               return -1;
+       }
+
+       ctdb_db = ctdb_attach(ctdb, db_name, false, 0);
 
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name));
@@ -1328,60 +2012,6 @@ static int control_getdbmap(struct ctdb_context *ctdb, int argc, const char **ar
        return 0;
 }
 
-/*
-  get the filename of the reclock file
- */
-static int control_getreclock(struct ctdb_context *ctdb, int argc, const char **argv)
-{
-       int i, ret, fd;
-       const char *reclock;
-       struct ctdb_node_map *nodemap=NULL;
-       char *pnnfile;
-
-       ret = ctdb_ctrl_getreclock(ctdb, TIMELIMIT(), options.pnn, ctdb, &reclock);
-       if (ret != 0) {
-               DEBUG(DEBUG_ERR, ("Unable to get reclock file from node %u\n", options.pnn));
-               return ret;
-       }
-
-       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));
-               return ret;
-       }
-
-
-       pnnfile = talloc_asprintf(ctdb, "%s.pnn", reclock);
-       CTDB_NO_MEMORY(ctdb, pnnfile);
-
-       fd = open(pnnfile, O_RDONLY);
-       if (fd == -1) {
-               DEBUG(DEBUG_CRIT,(__location__ " Failed to open reclock pnn file %s - (%s)\n", 
-                        pnnfile, strerror(errno)));
-               exit(10);
-       }
-
-
-       printf("Reclock file : %s\n", reclock);
-       for (i=0; i<nodemap->num; i++) {
-               int count;
-
-               count = ctdb_read_pnn_lock(fd, nodemap->nodes[i].pnn);
-
-               printf("pnn:%d %-16s", nodemap->nodes[i].pnn,
-                      inet_ntoa(nodemap->nodes[i].sin.sin_addr));
-               if (count == -1) {
-                       printf(" NOT ACTIVE\n");
-               } else {
-                       printf(" ACTIVE with %d connections\n", count);
-               }
-       }
-
-       close(fd);
-       return 0;
-}
-
-
 /*
   check if the local node is recmaster or not
   it will return 1 if this node is the recmaster and 0 if it is not
@@ -1423,6 +2053,7 @@ static int control_ping(struct ctdb_context *ctdb, int argc, const char **argv)
        ret = ctdb_ctrl_ping(ctdb, options.pnn);
        if (ret == -1) {
                printf("Unable to get ping response from node %u\n", options.pnn);
+               return -1;
        } else {
                printf("response from %u time=%.6f sec  (%d clients)\n", 
                       options.pnn, timeval_elapsed(&tv), ret);
@@ -1538,7 +2169,13 @@ static int32_t get_debug_by_desc(const char *desc)
                        return debug_levels[i].level;
                }
        }
-       return DEBUG_ERR;
+
+       fprintf(stderr, "Invalid debug level '%s'\nMust be one of\n", desc);
+       for (i=0;i<ARRAY_SIZE(debug_levels);i++) {
+               fprintf(stderr, "    %s\n", debug_levels[i].description);
+       }
+
+       exit(10);
 }
 
 /*
@@ -1571,7 +2208,7 @@ static int control_getdebug(struct ctdb_context *ctdb, int argc, const char **ar
 static int control_setdebug(struct ctdb_context *ctdb, int argc, const char **argv)
 {
        int ret;
-       uint32_t level;
+       int32_t level;
 
        if (argc < 1) {
                usage();
@@ -1580,7 +2217,7 @@ static int control_setdebug(struct ctdb_context *ctdb, int argc, const char **ar
        if (isalpha(argv[0][0])) { 
                level = get_debug_by_desc(argv[0]);
        } else {
-               level = strtoul(argv[0], NULL, 0);
+               level = strtol(argv[0], NULL, 0);
        }
 
        ret = ctdb_ctrl_set_debuglevel(ctdb, options.pnn, level);
@@ -1633,7 +2270,7 @@ static int control_attach(struct ctdb_context *ctdb, int argc, const char **argv
        }
        db_name = argv[0];
 
-       ctdb_db = ctdb_attach(ctdb, db_name, false);
+       ctdb_db = ctdb_attach(ctdb, db_name, false, 0);
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name));
                return -1;
@@ -1643,7 +2280,7 @@ static int control_attach(struct ctdb_context *ctdb, int argc, const char **argv
 }
 
 /*
-  dump memory usage
+  run an eventscript on a node
  */
 static int control_eventscript(struct ctdb_context *ctdb, int argc, const char **argv)
 {
@@ -1674,6 +2311,391 @@ static int control_eventscript(struct ctdb_context *ctdb, int argc, const char *
        return 0;
 }
 
+#define DB_VERSION 1
+#define MAX_DB_NAME 64
+struct db_file_header {
+       unsigned long version;
+       time_t timestamp;
+       unsigned long persistent;
+       unsigned long size;
+       const char name[MAX_DB_NAME];
+};
+
+struct backup_data {
+       struct ctdb_marshall_buffer *records;
+       uint32_t len;
+       uint32_t total;
+       bool traverse_error;
+};
+
+static int backup_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *private)
+{
+       struct backup_data *bd = talloc_get_type(private, struct backup_data);
+       struct ctdb_rec_data *rec;
+
+       /* add the record */
+       rec = ctdb_marshall_record(bd->records, 0, key, NULL, data);
+       if (rec == NULL) {
+               bd->traverse_error = true;
+               DEBUG(DEBUG_ERR,("Failed to marshall record\n"));
+               return -1;
+       }
+       bd->records = talloc_realloc_size(NULL, bd->records, rec->length + bd->len);
+       if (bd->records == NULL) {
+               DEBUG(DEBUG_ERR,("Failed to expand marshalling buffer\n"));
+               bd->traverse_error = true;
+               return -1;
+       }
+       bd->records->count++;
+       memcpy(bd->len+(uint8_t *)bd->records, rec, rec->length);
+       bd->len += rec->length;
+       talloc_free(rec);
+
+       bd->total++;
+       return 0;
+}
+
+/*
+ * backup a database to a file 
+ */
+static int control_backupdb(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       int i, ret;
+       struct ctdb_dbid_map *dbmap=NULL;
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+       struct db_file_header dbhdr;
+       struct ctdb_db_context *ctdb_db;
+       struct backup_data *bd;
+       int fh = -1;
+       int status = -1;
+
+       if (argc != 2) {
+               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 = tdb_transaction_start(ctdb_db->ltdb->tdb);
+       if (ret == -1) {
+               DEBUG(DEBUG_ERR,("Failed to start transaction\n"));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+
+       bd = talloc_zero(tmp_ctx, struct backup_data);
+       if (bd == NULL) {
+               DEBUG(DEBUG_ERR,("Failed to allocate backup_data\n"));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       bd->records = talloc_zero(bd, struct ctdb_marshall_buffer);
+       if (bd->records == NULL) {
+               DEBUG(DEBUG_ERR,("Failed to allocate ctdb_marshall_buffer\n"));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       bd->len = offsetof(struct ctdb_marshall_buffer, data);
+       bd->records->db_id = ctdb_db->db_id;
+       /* traverse the database collecting all records */
+       if (tdb_traverse_read(ctdb_db->ltdb->tdb, backup_traverse, bd) == -1 ||
+           bd->traverse_error) {
+               DEBUG(DEBUG_ERR,("Traverse error\n"));
+               talloc_free(tmp_ctx);
+               return -1;              
+       }
+
+       tdb_transaction_cancel(ctdb_db->ltdb->tdb);
+
+
+       fh = open(argv[1], O_RDWR|O_CREAT, 0600);
+       if (fh == -1) {
+               DEBUG(DEBUG_ERR,("Failed to open file '%s'\n", argv[1]));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       dbhdr.version = DB_VERSION;
+       dbhdr.timestamp = time(NULL);
+       dbhdr.persistent = dbmap->dbs[i].persistent;
+       dbhdr.size = bd->len;
+       if (strlen(argv[0]) >= MAX_DB_NAME) {
+               DEBUG(DEBUG_ERR,("Too long dbname\n"));
+               goto done;
+       }
+       strncpy(discard_const(dbhdr.name), argv[0], MAX_DB_NAME);
+       ret = write(fh, &dbhdr, sizeof(dbhdr));
+       if (ret == -1) {
+               DEBUG(DEBUG_ERR,("write failed: %s\n", strerror(errno)));
+               goto done;
+       }
+       ret = write(fh, bd->records, bd->len);
+       if (ret == -1) {
+               DEBUG(DEBUG_ERR,("write failed: %s\n", strerror(errno)));
+               goto done;
+       }
+
+       status = 0;
+done:
+       if (fh != -1) {
+               ret = close(fh);
+               if (ret == -1) {
+                       DEBUG(DEBUG_ERR,("close failed: %s\n", strerror(errno)));
+               }
+       }
+       talloc_free(tmp_ctx);
+       return status;
+}
+
+/*
+ * restore a database from a file 
+ */
+static int control_restoredb(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       int ret;
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+       TDB_DATA outdata;
+       TDB_DATA data;
+       struct db_file_header dbhdr;
+       struct ctdb_db_context *ctdb_db;
+       struct ctdb_node_map *nodemap=NULL;
+       struct ctdb_vnn_map *vnnmap=NULL;
+       int fh;
+       struct ctdb_control_wipe_database w;
+       uint32_t *nodes;
+       uint32_t generation;
+       struct tm *tm;
+       char tbuf[100];
+
+       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);
+
+       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);
+
+
+       ctdb_db = ctdb_attach(ctdb, dbhdr.name, dbhdr.persistent, 0);
+       if (ctdb_db == NULL) {
+               DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", dbhdr.name));
+               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);
+       if (ctdb_client_async_control(ctdb, CTDB_CONTROL_FREEZE,
+                                       nodes, TIMELIMIT(),
+                                       false, tdb_null,
+                                       NULL, NULL,
+                                       NULL) != 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);
+       if (ctdb_client_async_control(ctdb, CTDB_CONTROL_TRANSACTION_START,
+                                       nodes,
+                                       TIMELIMIT(), false, data,
+                                       NULL, NULL,
+                                       NULL) != 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,
+                                       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;
+       }
+       
+       /* push the database */
+       nodes = list_of_active_nodes(ctdb, nodemap, tmp_ctx, true);
+       if (ctdb_client_async_control(ctdb, CTDB_CONTROL_PUSH_DB,
+                                       nodes,
+                                       TIMELIMIT(), false, outdata,
+                                       NULL, NULL,
+                                       NULL) != 0) {
+               DEBUG(DEBUG_ERR, ("Failed to push database.\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,
+                                       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, 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
+ */
+static int control_setflags(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       int ret;
+       int32_t status;
+       int node;
+       int flags;
+       TDB_DATA data;
+       struct ctdb_node_flag_change c;
+
+       if (argc != 2) {
+               usage();
+               return -1;
+       }
+
+       if (sscanf(argv[0], "%d", &node) != 1) {
+               DEBUG(DEBUG_ERR, ("Badly formed node\n"));
+               usage();
+               return -1;
+       }
+       if (sscanf(argv[1], "0x%x", &flags) != 1) {
+               DEBUG(DEBUG_ERR, ("Badly formed flags\n"));
+               usage();
+               return -1;
+       }
+
+       c.pnn       = node;
+       c.old_flags = 0;
+       c.new_flags = flags;
+
+       data.dsize = sizeof(c);
+       data.dptr = (unsigned char *)&c;
+
+       ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_MODIFY_FLAGS, 0, 
+                          data, NULL, NULL, &status, NULL, NULL);
+       if (ret != 0 || status != 0) {
+               DEBUG(DEBUG_ERR,("Failed to modify flags\n"));
+               return -1;
+       }
+       return 0;
+}
+
 /*
   dump memory usage
  */
@@ -1760,7 +2782,7 @@ static int control_listnodes(struct ctdb_context *ctdb, int argc, const char **a
        }
 
        for(i=0;i<nodemap->num;i++){
-               printf("%s\n", inet_ntoa(nodemap->nodes[i].sin.sin_addr));
+               printf("%s\n", ctdb_addr_to_str(&nodemap->nodes[i].addr));
        }
 
        return 0;
@@ -1807,6 +2829,9 @@ static int control_reload_nodes_file(struct ctdb_context *ctdb, int argc, const
                DEBUG(DEBUG_ERR, ("ERROR: Failed to reload nodes file on node %u. You MUST fix that node manually!\n", mypnn));
        }
 
+       /* initiate a recovery */
+       control_recover(ctdb, argc, argv);
+
        return 0;
 }
 
@@ -1815,61 +2840,71 @@ static const struct {
        const char *name;
        int (*fn)(struct ctdb_context *, int, const char **);
        bool auto_all;
+       bool without_daemon; /* can be run without daemon running ? */
        const char *msg;
        const char *args;
 } ctdb_commands[] = {
 #ifdef CTDB_VERS
-       { "version",         control_version,           true,  "show version of ctdb" },
+       { "version",         control_version,           true,   false,  "show version of ctdb" },
 #endif
-       { "status",          control_status,            true,  "show node status" },
-       { "uptime",          control_uptime,            true,  "show node uptime" },
-       { "ping",            control_ping,              true,  "ping all nodes" },
-       { "getvar",          control_getvar,            true,  "get a tunable variable",               "<name>"},
-       { "setvar",          control_setvar,            true,  "set a tunable variable",               "<name> <value>"},
-       { "listvars",        control_listvars,          true,  "list tunable variables"},
-       { "statistics",      control_statistics,        false, "show statistics" },
-       { "statisticsreset", control_statistics_reset,  true,  "reset statistics"},
-       { "ip",              control_ip,                false,  "show which public ip's that ctdb manages" },
-       { "process-exists",  control_process_exists,    true,  "check if a process exists on a node",  "<pid>"},
-       { "getdbmap",        control_getdbmap,          true,  "show the database map" },
-       { "catdb",           control_catdb,             true,  "dump a database" ,                     "<dbname>"},
-       { "getmonmode",      control_getmonmode,        true,  "show monitoring mode" },
-       { "getcapabilities", control_getcapabilities,   true,  "show node capabilities" },
-       { "disablemonitor",      control_disable_monmode,        true,  "set monitoring mode to DISABLE" },
-       { "enablemonitor",      control_enable_monmode,        true,  "set monitoring mode to ACTIVE" },
-       { "setdebug",        control_setdebug,          true,  "set debug level",                      "<EMERG|ALERT|CRIT|ERR|WARNING|NOTICE|INFO|DEBUG>" },
-       { "getdebug",        control_getdebug,          true,  "get debug level" },
-       { "attach",          control_attach,            true,  "attach to a database",                 "<dbname>" },
-       { "dumpmemory",      control_dumpmemory,        true,  "dump memory map to stdout" },
-       { "rddumpmemory",    control_rddumpmemory,      true,  "dump memory map from the recovery daemon to stdout" },
-       { "getpid",          control_getpid,            true,  "get ctdbd process ID" },
-       { "disable",         control_disable,           true,  "disable a nodes public IP" },
-       { "enable",          control_enable,            true,  "enable a nodes public IP" },
-       { "ban",             control_ban,               true,  "ban a node from the cluster",          "<bantime|0>"},
-       { "unban",           control_unban,             true,  "unban a node from the cluster" },
-       { "shutdown",        control_shutdown,          true,  "shutdown ctdbd" },
-       { "recover",         control_recover,           true,  "force recovery" },
-       { "freeze",          control_freeze,            true,  "freeze all databases" },
-       { "thaw",            control_thaw,              true,  "thaw all databases" },
-       { "isnotrecmaster",  control_isnotrecmaster,    false,  "check if the local node is recmaster or not" },
-       { "killtcp",         kill_tcp,                  false, "kill a tcp connection.", "<srcip:port> <dstip:port>" },
-       { "gratiousarp",     control_gratious_arp,      false, "send a gratious arp", "<ip> <interface>" },
-       { "tickle",          tickle_tcp,                false, "send a tcp tickle ack", "<srcip:port> <dstip:port>" },
-       { "gettickles",      control_get_tickles,       false, "get the list of tickles registered for this ip", "<ip>" },
-
-       { "regsrvid",        regsrvid,                  false, "register a server id", "<pnn> <type> <id>" },
-       { "unregsrvid",      unregsrvid,                false, "unregister a server id", "<pnn> <type> <id>" },
-       { "chksrvid",        chksrvid,                  false, "check if a server id exists", "<pnn> <type> <id>" },
-       { "getsrvids",       getsrvids,                 false, "get a list of all server ids"},
-       { "vacuum",          ctdb_vacuum,               false, "vacuum the databases of empty records", "[max_records]"},
-       { "repack",          ctdb_repack,               false, "repack all databases", "[max_freelist]"},
-       { "listnodes",       control_listnodes,         false, "list all nodes in the cluster"},
-       { "reloadnodes",     control_reload_nodes_file,         false, "reload the nodes file and restart the transport on all nodes"},
-       { "getreclock",      control_getreclock,        false,  "get the path to the reclock file" },
-       { "moveip",          control_moveip,            false, "move/failover an ip address to another node", "<ip> <node>"},
-       { "addip",           control_addip,             true, "add a ip address to a node", "<ip/mask> <iface>"},
-       { "delip",           control_delip,             false, "delete an ip address from a node", "<ip>"},
-       { "eventscript",     control_eventscript,       true, "run the eventscript with the given parameters on a node", "<arguments>"},
+       { "status",          control_status,            true,   false,  "show node status" },
+       { "uptime",          control_uptime,            true,   false,  "show node uptime" },
+       { "ping",            control_ping,              true,   false,  "ping all nodes" },
+       { "getvar",          control_getvar,            true,   false,  "get a tunable variable",               "<name>"},
+       { "setvar",          control_setvar,            true,   false,  "set a tunable variable",               "<name> <value>"},
+       { "listvars",        control_listvars,          true,   false,  "list tunable variables"},
+       { "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" },
+       { "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" },
+       { "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" },
+       { "pnn",             control_pnn,               true,   false,  "show the pnn of the currnet node" },
+       { "lvs",             control_lvs,               true,   false,  "show lvs configuration" },
+       { "lvsmaster",       control_lvsmaster,         true,   false,  "show which node is the lvs master" },
+       { "disablemonitor",      control_disable_monmode,        true,  false,  "set monitoring mode to DISABLE" },
+       { "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" },
+       { "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" },
+       { "getpid",          control_getpid,            true,   false,  "get ctdbd process ID" },
+       { "disable",         control_disable,           true,   false,  "disable a nodes public IP" },
+       { "enable",          control_enable,            true,   false,  "enable a nodes public IP" },
+       { "ban",             control_ban,               true,   false,  "ban a node from the cluster",          "<bantime|0>"},
+       { "unban",           control_unban,             true,   false,  "unban a node from the cluster" },
+       { "shutdown",        control_shutdown,          true,   false,  "shutdown ctdbd" },
+       { "recover",         control_recover,           true,   false,  "force recovery" },
+       { "freeze",          control_freeze,            true,   false,  "freeze all databases" },
+       { "thaw",            control_thaw,              true,   false,  "thaw all databases" },
+       { "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>" },
+
+       { "regsrvid",        regsrvid,                  false,  false, "register a server id", "<pnn> <type> <id>" },
+       { "unregsrvid",      unregsrvid,                false,  false, "unregister a server id", "<pnn> <type> <id>" },
+       { "chksrvid",        chksrvid,                  false,  false, "check if a server id exists", "<pnn> <type> <id>" },
+       { "getsrvids",       getsrvids,                 false,  false, "get a list of all server ids"},
+       { "vacuum",          ctdb_vacuum,               false,  false, "vacuum the databases of empty records", "[max_records]"},
+       { "repack",          ctdb_repack,               false,  false, "repack all databases", "[max_freelist]"},
+       { "listnodes",       control_listnodes,         false,  false, "list all nodes in the cluster"},
+       { "reloadnodes",     control_reload_nodes_file,         false,  false, "reload the nodes file and restart the transport on all nodes"},
+       { "moveip",          control_moveip,            false,  false, "move/failover an ip address to another node", "<ip> <node>"},
+       { "addip",           control_addip,             true,   false, "add a ip address to a node", "<ip/mask> <iface>"},
+       { "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>"},
+       { "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"},
+       { "natgwlist",        control_natgwlist,    false,      false, "show the nodes belonging to this natgw configuration"},
+       { "xpnn",             control_xpnn,               true, true,  "find the pnn of the local node without talking to the daemon (unreliable)" },
 };
 
 /*
@@ -1898,7 +2933,7 @@ static void usage(void)
 static void ctdb_alarm(int sig)
 {
        printf("Maximum runtime exceeded - exiting\n");
-       _exit(0);
+       _exit(ERR_TIMEOUT);
 }
 
 /*
@@ -1954,6 +2989,13 @@ int main(int argc, const char *argv[])
                usage();
        }
 
+       if (options.maxruntime == 0) {
+               const char *ctdb_timeout;
+               ctdb_timeout = getenv("CTDB_TIMEOUT");
+               if (ctdb_timeout != NULL) {
+                       options.maxruntime = strtoul(ctdb_timeout, NULL, 0);
+               }
+       }
        if (options.maxruntime != 0) {
                signal(SIGALRM, ctdb_alarm);
                alarm(options.maxruntime);
@@ -1972,24 +3014,29 @@ int main(int argc, const char *argv[])
 
        ev = event_context_init(NULL);
 
-       /* initialise ctdb */
-       ctdb = ctdb_cmdline_client(ev);
-       if (ctdb == NULL) {
-               DEBUG(DEBUG_ERR, ("Failed to init ctdb\n"));
-               exit(1);
-       }
-
        for (i=0;i<ARRAY_SIZE(ctdb_commands);i++) {
                if (strcmp(control, ctdb_commands[i].name) == 0) {
                        int j;
 
-                       if (options.pnn == CTDB_CURRENT_NODE) {
-                               int pnn;
-                               pnn = ctdb_ctrl_getpnn(ctdb, TIMELIMIT(), options.pnn);         
-                               if (pnn == -1) {
-                                       return -1;
+                       if (ctdb_commands[i].without_daemon == false) {
+                               /* initialise ctdb */
+                               ctdb = ctdb_cmdline_client(ev);
+                               if (ctdb == NULL) {
+                                       DEBUG(DEBUG_ERR, ("Failed to init ctdb\n"));
+                                       exit(1);
+                               }
+
+                               /* verify the node exists */
+                               verify_node(ctdb);
+
+                               if (options.pnn == CTDB_CURRENT_NODE) {
+                                       int pnn;
+                                       pnn = ctdb_ctrl_getpnn(ctdb, TIMELIMIT(), options.pnn);         
+                                       if (pnn == -1) {
+                                               return -1;
+                                       }
+                                       options.pnn = pnn;
                                }
-                               options.pnn = pnn;
                        }
 
                        if (ctdb_commands[i].auto_all &&