int timelimit;
uint32_t pnn;
int machinereadable;
+ int verbose;
int maxruntime;
} options;
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
*/
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;
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);
}
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");
}
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);
}
}
+/*
+ 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)
{
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;
{ "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>" },
{ "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>" },
};
/*
"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++) {
{ "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
};
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) {