add a command to write a record to a persistent database
[metze/ctdb/wip.git] / tools / ctdb.c
index e915e0099ddc3b9dee0ab72bff9ecc28acc175b4..b986f4e7ccb8111515f9c1ed72311074bc33f743 100644 (file)
@@ -44,6 +44,7 @@ static struct {
        int timelimit;
        uint32_t pnn;
        int machinereadable;
+       int verbose;
        int maxruntime;
 } options;
 
@@ -980,6 +981,80 @@ static int control_recmaster(struct ctdb_context *ctdb, int argc, const char **a
        return 0;
 }
 
+/*
+  add a tickle to a public address
+ */
+static int control_add_tickle(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       struct ctdb_tcp_connection t;
+       TDB_DATA data;
+       int ret;
+
+       if (argc < 2) {
+               usage();
+       }
+
+       if (parse_ip_port(argv[0], &t.src_addr) == 0) {
+               DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0]));
+               return -1;
+       }
+       if (parse_ip_port(argv[1], &t.dst_addr) == 0) {
+               DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[1]));
+               return -1;
+       }
+
+       data.dptr = (uint8_t *)&t;
+       data.dsize = sizeof(t);
+
+       /* tell all nodes about this tcp connection */
+       ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_TCP_ADD_DELAYED_UPDATE,
+                          0, data, ctdb, NULL, NULL, NULL, NULL);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,("Failed to add tickle\n"));
+               return -1;
+       }
+       
+       return 0;
+}
+
+
+/*
+  delete a tickle from a node
+ */
+static int control_del_tickle(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       struct ctdb_tcp_connection t;
+       TDB_DATA data;
+       int ret;
+
+       if (argc < 2) {
+               usage();
+       }
+
+       if (parse_ip_port(argv[0], &t.src_addr) == 0) {
+               DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0]));
+               return -1;
+       }
+       if (parse_ip_port(argv[1], &t.dst_addr) == 0) {
+               DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[1]));
+               return -1;
+       }
+
+       data.dptr = (uint8_t *)&t;
+       data.dsize = sizeof(t);
+
+       /* tell all nodes about this tcp connection */
+       ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_TCP_REMOVE,
+                          0, data, ctdb, NULL, NULL, NULL, NULL);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,("Failed to remove tickle\n"));
+               return -1;
+       }
+       
+       return 0;
+}
+
+
 /*
   get a list of all tickles for this pnn
  */
@@ -988,11 +1063,16 @@ static int control_get_tickles(struct ctdb_context *ctdb, int argc, const char *
        struct ctdb_control_tcp_tickle_list *list;
        ctdb_sock_addr addr;
        int i, ret;
+       unsigned port = 0;
 
        if (argc < 1) {
                usage();
        }
 
+       if (argc == 2) {
+               port = atoi(argv[1]);
+       }
+
        if (parse_ip(argv[0], NULL, 0, &addr) == 0) {
                DEBUG(DEBUG_ERR,("Wrongly formed ip address '%s'\n", argv[0]));
                return -1;
@@ -1004,11 +1084,25 @@ static int control_get_tickles(struct ctdb_context *ctdb, int argc, const char *
                return -1;
        }
 
-       printf("Tickles for ip:%s\n", ctdb_addr_to_str(&list->addr));
-       printf("Num tickles:%u\n", list->tickles.num);
-       for (i=0;i<list->tickles.num;i++) {
-               printf("SRC: %s:%u   ", ctdb_addr_to_str(&list->tickles.connections[i].src_addr), ntohs(list->tickles.connections[i].src_addr.ip.sin_port));
-               printf("DST: %s:%u\n", ctdb_addr_to_str(&list->tickles.connections[i].dst_addr), ntohs(list->tickles.connections[i].dst_addr.ip.sin_port));
+       if (options.machinereadable){
+               printf(":source ip:port:destination ip:port:\n");
+               for (i=0;i<list->tickles.num;i++) {
+                       if (port && port != ntohs(list->tickles.connections[i].dst_addr.ip.sin_port)) {
+                               continue;
+                       }
+                       printf(":%s:%u", ctdb_addr_to_str(&list->tickles.connections[i].src_addr), ntohs(list->tickles.connections[i].src_addr.ip.sin_port));
+                       printf(":%s:%u:\n", ctdb_addr_to_str(&list->tickles.connections[i].dst_addr), ntohs(list->tickles.connections[i].dst_addr.ip.sin_port));
+               }
+       } else {
+               printf("Tickles for ip:%s\n", ctdb_addr_to_str(&list->addr));
+               printf("Num tickles:%u\n", list->tickles.num);
+               for (i=0;i<list->tickles.num;i++) {
+                       if (port && port != ntohs(list->tickles.connections[i].dst_addr.ip.sin_port)) {
+                               continue;
+                       }
+                       printf("SRC: %s:%u   ", ctdb_addr_to_str(&list->tickles.connections[i].src_addr), ntohs(list->tickles.connections[i].src_addr.ip.sin_port));
+                       printf("DST: %s:%u\n", ctdb_addr_to_str(&list->tickles.connections[i].dst_addr), ntohs(list->tickles.connections[i].dst_addr.ip.sin_port));
+               }
        }
 
        talloc_free(list);
@@ -1727,7 +1821,11 @@ static int control_ip(struct ctdb_context *ctdb, int argc, const char **argv)
        }
 
        if (options.machinereadable){
-               printf(":Public IP:Node:ActiveInterface:AvailableInterfaces:ConfiguredInterfaces:\n");
+               printf(":Public IP:Node:");
+               if (options.verbose){
+                       printf("ActiveInterface:AvailableInterfaces:ConfiguredInterfaces:");
+               }
+               printf("\n");
        } else {
                if (options.pnn == CTDB_BROADCAST_ALL) {
                        printf("Public IPs on ALL nodes\n");
@@ -1787,19 +1885,29 @@ static int control_ip(struct ctdb_context *ctdb, int argc, const char **argv)
                }
 
                if (options.machinereadable){
-                       printf(":%s:%d:%s:%s:%s:\n",
-                              ctdb_addr_to_str(&ips->ips[ips->num-i].addr),
-                              ips->ips[ips->num-i].pnn,
-                              aciface?aciface:"",
-                              avifaces?avifaces:"",
-                              cifaces?cifaces:"");
+                       printf(":%s:%d:",
+                               ctdb_addr_to_str(&ips->ips[ips->num-i].addr),
+                               ips->ips[ips->num-i].pnn);
+                       if (options.verbose){
+                               printf("%s:%s:%s:",
+                                       aciface?aciface:"",
+                                       avifaces?avifaces:"",
+                                       cifaces?cifaces:"");
+                       }
+                       printf("\n");
                } else {
-                       printf("%s node[%d] active[%s] available[%s] configured[%s]\n",
-                              ctdb_addr_to_str(&ips->ips[ips->num-i].addr),
-                              ips->ips[ips->num-i].pnn,
-                              aciface?aciface:"",
-                              avifaces?avifaces:"",
-                              cifaces?cifaces:"");
+                       if (options.verbose) {
+                               printf("%s node[%d] active[%s] available[%s] configured[%s]\n",
+                                       ctdb_addr_to_str(&ips->ips[ips->num-i].addr),
+                                       ips->ips[ips->num-i].pnn,
+                                       aciface?aciface:"",
+                                       avifaces?avifaces:"",
+                                       cifaces?cifaces:"");
+                       } else {
+                               printf("%s %d\n",
+                                       ctdb_addr_to_str(&ips->ips[ips->num-i].addr),
+                                       ips->ips[ips->num-i].pnn);
+                       }
                }
                talloc_free(info);
        }
@@ -2748,6 +2856,172 @@ static int control_catdb(struct ctdb_context *ctdb, int argc, const char **argv)
 }
 
 
+/*
+  fetch a record from a persistent database
+ */
+static int control_pfetch(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       const char *db_name;
+       struct ctdb_db_context *ctdb_db;
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+       struct ctdb_transaction_handle *h;
+       TDB_DATA key, data;
+       int ret;
+
+       if (argc < 2) {
+               talloc_free(tmp_ctx);
+               usage();
+       }
+
+       db_name = argv[0];
+
+
+       if (db_exists(ctdb, db_name)) {
+               DEBUG(DEBUG_ERR,("Database '%s' does not exist\n", db_name));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       ctdb_db = ctdb_attach(ctdb, db_name, true, 0);
+
+       if (ctdb_db == NULL) {
+               DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       h = ctdb_transaction_start(ctdb_db, tmp_ctx);
+       if (h == NULL) {
+               DEBUG(DEBUG_ERR,("Failed to start transaction on database %s\n", db_name));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       key.dptr  = discard_const(argv[1]);
+       key.dsize = strlen(argv[1]);
+       ret = ctdb_transaction_fetch(h, tmp_ctx, key, &data);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,("Failed to fetch record\n"));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       if (data.dsize == 0 || data.dptr == NULL) {
+               DEBUG(DEBUG_ERR,("Record is empty\n"));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       fwrite(data.dptr, data.dsize, 1, stdout);
+
+       /* abort the transaction */
+       talloc_free(h);
+
+
+       talloc_free(tmp_ctx);
+       return 0;
+}
+
+/*
+  write a record to a persistent database
+ */
+static int control_pstore(struct ctdb_context *ctdb, int argc, const char **argv)
+{
+       const char *db_name;
+       struct ctdb_db_context *ctdb_db;
+       TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+       struct ctdb_transaction_handle *h;
+       struct stat st;
+       TDB_DATA key, data;
+       int fd, ret;
+
+       if (argc < 3) {
+               talloc_free(tmp_ctx);
+               usage();
+       }
+
+       fd = open(argv[2], O_RDONLY);
+       if (fd == -1) {
+               DEBUG(DEBUG_ERR,("Failed to open file containing record data : %s  %s\n", argv[2], strerror(errno)));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+       
+       ret = fstat(fd, &st);
+       if (ret == -1) {
+               DEBUG(DEBUG_ERR,("fstat of file %s failed: %s\n", argv[2], strerror(errno)));
+               close(fd);
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       if (!S_ISREG(st.st_mode)) {
+               DEBUG(DEBUG_ERR,("Not a regular file %s\n", argv[2]));
+               close(fd);
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       data.dsize = st.st_size;
+       if (data.dsize == 0) {
+               data.dptr  = NULL;
+       } else {
+               data.dptr = talloc_size(tmp_ctx, data.dsize);
+               if (data.dptr == NULL) {
+                       DEBUG(DEBUG_ERR,("Failed to talloc %d of memory to store record data\n", (int)data.dsize));
+                       close(fd);
+                       talloc_free(tmp_ctx);
+                       return -1;
+               }
+               ret = read(fd, data.dptr, data.dsize);
+               if (ret != data.dsize) {
+                       DEBUG(DEBUG_ERR,("Failed to read %d bytes of record data\n", (int)data.dsize));
+                       close(fd);
+                       talloc_free(tmp_ctx);
+                       return -1;
+               }
+       }
+       close(fd);
+
+
+       db_name = argv[0];
+
+       ctdb_db = ctdb_attach(ctdb, db_name, true, 0);
+
+       if (ctdb_db == NULL) {
+               DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       h = ctdb_transaction_start(ctdb_db, tmp_ctx);
+       if (h == NULL) {
+               DEBUG(DEBUG_ERR,("Failed to start transaction on database %s\n", db_name));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       key.dptr  = discard_const(argv[1]);
+       key.dsize = strlen(argv[1]);
+       ret = ctdb_transaction_store(h, key, data);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,("Failed to store record\n"));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+       ret = ctdb_transaction_commit(h);
+       if (ret != 0) {
+               DEBUG(DEBUG_ERR,("Failed to commit transaction\n"));
+               talloc_free(tmp_ctx);
+               return -1;
+       }
+
+
+       talloc_free(tmp_ctx);
+       return 0;
+}
+
 static void log_handler(struct ctdb_context *ctdb, uint64_t srvid, 
                             TDB_DATA data, void *private_data)
 {
@@ -3638,9 +3912,9 @@ static int control_restoredb(struct ctdb_context *ctdb, int argc, const char **a
                return -1;
        }
 
-       dbname = dbhdr.name;
+       dbname = discard_const(dbhdr.name);
        if (argc == 2) {
-               dbname = argv[1];
+               dbname = discard_const(argv[1]);
        }
 
        outdata.dsize = dbhdr.size;
@@ -4385,7 +4659,10 @@ static const struct {
        { "killtcp",         kill_tcp,                  false,  false, "kill a tcp connection.", "<srcip:port> <dstip:port>" },
        { "gratiousarp",     control_gratious_arp,      false,  false, "send a gratious arp", "<ip> <interface>" },
        { "tickle",          tickle_tcp,                false,  false, "send a tcp tickle ack", "<srcip:port> <dstip:port>" },
-       { "gettickles",      control_get_tickles,       false,  false, "get the list of tickles registered for this ip", "<ip>" },
+       { "gettickles",      control_get_tickles,       false,  false, "get the list of tickles registered for this ip", "<ip> [<port>]" },
+       { "addtickle",       control_add_tickle,        false,  false, "add a tickle for this ip", "<ip>:<port> <ip>:<port>" },
+
+       { "deltickle",       control_del_tickle,        false,  false, "delete a tickle from this ip", "<ip>:<port> <ip>:<port>" },
 
        { "regsrvid",        regsrvid,                  false,  false, "register a server id", "<pnn> <type> <id>" },
        { "unregsrvid",      unregsrvid,                false,  false, "unregister a server id", "<pnn> <type> <id>" },
@@ -4420,6 +4697,8 @@ static const struct {
        { "msglisten",        control_msglisten,        false,  false, "Listen on a srvid port for messages", "<msg srvid>"},
        { "msgsend",          control_msgsend,  false,  false, "Send a message to srvid", "<srvid> <message>"},
        { "sync",            control_ipreallocate,      true,   false,  "wait until ctdbd has synced all state changes" },
+       { "pfetch",          control_pfetch,            true,   false,  "fetch a record from a persistent database", "<db> <key>" },
+       { "pstore",          control_pstore,            true,   false,  "write a record to a persistent database", "<db> <key> <file containing record>" },
 };
 
 /*
@@ -4433,6 +4712,7 @@ static void usage(void)
 "Options:\n" \
 "   -n <node>          choose node number, or 'all' (defaults to local node)\n"
 "   -Y                 generate machinereadable output\n"
+"   -v                 generate verbose output\n"
 "   -t <timelimit>     set timelimit for control in seconds (default %u)\n", options.timelimit);
        printf("Controls:\n");
        for (i=0;i<ARRAY_SIZE(ctdb_commands);i++) {
@@ -4464,6 +4744,7 @@ int main(int argc, const char *argv[])
                { "timelimit", 't', POPT_ARG_INT, &options.timelimit, 0, "timelimit", "integer" },
                { "node",      'n', POPT_ARG_STRING, &nodestring, 0, "node", "integer|all" },
                { "machinereadable", 'Y', POPT_ARG_NONE, &options.machinereadable, 0, "enable machinereadable output", NULL },
+               { "verbose",    'v', POPT_ARG_NONE, &options.verbose, 0, "enable verbose output", NULL },
                { "maxruntime", 'T', POPT_ARG_INT, &options.maxruntime, 0, "die if runtime exceeds this limit (in seconds)", "integer" },
                POPT_TABLEEND
        };
@@ -4534,6 +4815,7 @@ int main(int argc, const char *argv[])
                DEBUG(DEBUG_ERR, ("Failed to initialize event system\n"));
                exit(1);
        }
+       tevent_loop_allow_nesting(ev);
 
        for (i=0;i<ARRAY_SIZE(ctdb_commands);i++) {
                if (strcmp(control, ctdb_commands[i].name) == 0) {