add missing checks on so far ignored return values
[samba.git] / ctdb / tools / ctdb.c
index 8aa934d3fc25067014948b6703994a3d88af447d..57e1a8dffe351e40694578649c50a7ce342ed00b 100644 (file)
 #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);
 
 static struct {
@@ -52,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
  */
@@ -140,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);
@@ -233,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));
@@ -296,7 +362,7 @@ static int control_pnn(struct ctdb_context *ctdb, int argc, const char **argv)
 {
        int mypnn;
 
-       mypnn = ctdb_ctrl_getpnn(ctdb, TIMELIMIT(), CTDB_CURRENT_NODE);
+       mypnn = ctdb_ctrl_getpnn(ctdb, TIMELIMIT(), options.pnn);
        if (mypnn == -1) {
                DEBUG(DEBUG_ERR, ("Unable to get pnn from local node."));
                return -1;
@@ -306,6 +372,81 @@ static int control_pnn(struct ctdb_context *ctdb, int argc, const char **argv)
        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
  */
@@ -408,6 +549,168 @@ 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
  */
@@ -421,7 +724,7 @@ static int control_get_tickles(struct ctdb_context *ctdb, int argc, const char *
                usage();
        }
 
-       if (parse_ip(argv[0], &addr) == 0) {
+       if (parse_ip(argv[0], NULL, 0, &addr) == 0) {
                DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0]));
                return -1;
        }
@@ -493,7 +796,7 @@ static int control_moveip(struct ctdb_context *ctdb, int argc, const char **argv
                usage();
        }
 
-       if (parse_ip(argv[0], &addr) == 0) {
+       if (parse_ip(argv[0], NULL, 0, &addr) == 0) {
                DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0]));
                return -1;
        }
@@ -728,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;
@@ -783,6 +1086,76 @@ 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
  */
@@ -799,11 +1172,15 @@ static int control_delip(struct ctdb_context *ctdb, int argc, const char **argv)
                usage();
        }
 
-       if (parse_ip(argv[0], &addr) == 0) {
+       if (parse_ip(argv[0], NULL, 0, &addr) == 0) {
                DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0]));
                return -1;
        }
 
+       if (options.pnn == CTDB_BROADCAST_ALL) {
+               return control_delip_all(ctdb, argc, argv, &addr);
+       }
+
        pub.addr  = addr;
        pub.mask  = 0;
        pub.len   = 0;
@@ -893,7 +1270,7 @@ static int control_gratious_arp(struct ctdb_context *ctdb, int argc, const char
                usage();
        }
 
-       if (!parse_ip(argv[0], &addr)) {
+       if (!parse_ip(argv[0], NULL, 0, &addr)) {
                DEBUG(DEBUG_ERR, ("Bad IP '%s'\n", argv[0]));
                return -1;
        }
@@ -1136,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
  */
@@ -1145,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);
 
@@ -1163,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;
 }
 
@@ -1175,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;
 }
 
@@ -1211,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) {
@@ -1224,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;
 }
 
@@ -1408,12 +1893,16 @@ static int control_lvsmaster(struct ctdb_context *ctdb, int argc, const char **a
                        }
                }
 
-               printf("Node %d is LVS master\n", i);
+               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 0;
+       return -1;
 }
 
 /*
@@ -1466,6 +1955,13 @@ static int control_catdb(struct ctdb_context *ctdb, int argc, const char **argv)
        }
 
        db_name = argv[0];
+
+
+       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) {
@@ -1557,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);
@@ -1672,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);
 }
 
 /*
@@ -1705,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();
@@ -1714,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);
@@ -1863,7 +2366,8 @@ static int control_backupdb(struct ctdb_context *ctdb, int argc, const char **ar
        struct db_file_header dbhdr;
        struct ctdb_db_context *ctdb_db;
        struct backup_data *bd;
-       int fh;
+       int fh = -1;
+       int status = -1;
 
        if (argc != 2) {
                DEBUG(DEBUG_ERR,("Invalid arguments\n"));
@@ -1896,6 +2400,7 @@ static int control_backupdb(struct ctdb_context *ctdb, int argc, const char **ar
        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;
        }
 
@@ -1948,16 +2453,30 @@ static int control_backupdb(struct ctdb_context *ctdb, int argc, const char **ar
        dbhdr.size = bd->len;
        if (strlen(argv[0]) >= MAX_DB_NAME) {
                DEBUG(DEBUG_ERR,("Too long dbname\n"));
-               talloc_free(tmp_ctx);
-               return -1;
+               goto done;
        }
        strncpy(discard_const(dbhdr.name), argv[0], MAX_DB_NAME);
-       write(fh, &dbhdr, sizeof(dbhdr));
-       write(fh, bd->records, bd->len);
+       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;
+       }
 
-       close(fh);
+       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 0;
+       return status;
 }
 
 /*
@@ -2133,6 +2652,50 @@ static int control_restoredb(struct ctdb_context *ctdb, int argc, const char **a
        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
  */
@@ -2266,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;
 }
 
@@ -2274,65 +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" },
-       { "pnn",             control_pnn,               true,  "show the pnn of the currnet node" },
-       { "lvs",             control_lvs,               true,  "show lvs configuration" },
-       { "lvsmaster",       control_lvsmaster,         true,  "show which node is the lvs master" },
-       { "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"},
-       { "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>"},
-       { "backupdb",        control_backupdb,          false, "backup the database into a file.", "<database> <file>"},
-       { "restoredb",        control_restoredb,          false, "restore the database from a file.", "<file>"},
+       { "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)" },
 };
 
 /*
@@ -2361,7 +2933,7 @@ static void usage(void)
 static void ctdb_alarm(int sig)
 {
        printf("Maximum runtime exceeded - exiting\n");
-       _exit(0);
+       _exit(ERR_TIMEOUT);
 }
 
 /*
@@ -2417,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);
@@ -2435,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 &&