update how the NATGW configuration works.
[sahlberg/ctdb.git] / tools / ctdb.c
index 1fd61d9cba927f47d6042b21a9a04d903916e5ac..1f17059f52598d2d10d24a4bee122405e3f97c6b 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
  */
@@ -233,15 +302,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 +359,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;
@@ -408,14 +471,156 @@ 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);
+               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 recmode, recmaster;
-       int mypnn;
+       uint32_t recmaster;
 
        ret = ctdb_ctrl_getrecmaster(ctdb, ctdb, TIMELIMIT(), options.pnn, &recmaster);
        if (ret != 0) {
@@ -440,7 +645,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;
        }
@@ -512,7 +717,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;
        }
@@ -747,7 +952,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;
@@ -802,6 +1007,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
  */
@@ -818,11 +1093,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;
@@ -912,7 +1191,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;
        }
@@ -1155,6 +1434,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
  */
@@ -1164,10 +1486,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);
 
@@ -1182,7 +1522,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;
 }
 
@@ -1194,6 +1543,10 @@ static int control_unban(struct ctdb_context *ctdb, int argc, const char **argv)
 {
        int ret;
        TDB_DATA data;
+       uint32_t generation, next_generation;
+
+       /* record the current generation number */
+       generation = get_generation(ctdb);
 
        data.dptr = (uint8_t *)&options.pnn;
        data.dsize = sizeof(uint32_t);
@@ -1204,6 +1557,15 @@ static int control_unban(struct ctdb_context *ctdb, int argc, const char **argv)
                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;
 }
 
@@ -1230,6 +1592,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) {
@@ -1243,6 +1609,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;
 }
 
@@ -1485,6 +1860,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) {
@@ -1576,6 +1958,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);
@@ -1691,7 +2074,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);
 }
 
 /*
@@ -1724,7 +2113,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();
@@ -1733,7 +2122,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);
@@ -2152,6 +2541,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
  */
@@ -2285,6 +2718,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;
 }
 
@@ -2353,6 +2789,9 @@ static const struct {
        { "backupdb",        control_backupdb,          false, "backup the database into a file.", "<database> <file>"},
        { "restoredb",        control_restoredb,          false, "restore the database from a file.", "<file>"},
        { "recmaster",        control_recmaster,          false, "show the pnn for the recovery master."},
+       { "setflags",        control_setflags,            false, "set flags for a node in the nodemap.", "<node> <flags>"},
+       { "scriptstatus",        control_scriptstatus,    false, "show the status of the monitoring scripts"},
+       { "natgwlist",        control_natgwlist,    false, "show the nodes belonging to this natgw configuration"},
 };
 
 /*
@@ -2381,7 +2820,7 @@ static void usage(void)
 static void ctdb_alarm(int sig)
 {
        printf("Maximum runtime exceeded - exiting\n");
-       _exit(0);
+       _exit(ERR_TIMEOUT);
 }
 
 /*
@@ -2437,6 +2876,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);
@@ -2462,6 +2908,9 @@ int main(int argc, const char *argv[])
                exit(1);
        }
 
+       /* verify the node exists */
+       verify_node(ctdb);
+
        for (i=0;i<ARRAY_SIZE(ctdb_commands);i++) {
                if (strcmp(control, ctdb_commands[i].name) == 0) {
                        int j;