ctdb-client: Move ctdb_ctrl_modflags() to ctdb tool
[Samba.git] / ctdb / tools / ctdb.c
blob55477bf477ccd16eb21cc0b7a49f2fbf23dab139
1 /*
2 CTDB control tool
4 Copyright (C) Amitay Isaacs 2015
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>.
20 #include "replace.h"
21 #include "system/network.h"
22 #include "system/filesys.h"
23 #include "system/time.h"
24 #include "system/wait.h"
25 #include "system/dir.h"
27 #include <ctype.h>
28 #include <popt.h>
29 #include <talloc.h>
30 #include <tevent.h>
31 #include <tdb.h>
33 #include "ctdb_version.h"
34 #include "lib/util/debug.h"
35 #include "lib/util/samba_util.h"
36 #include "lib/util/sys_rw.h"
38 #include "common/db_hash.h"
39 #include "common/logging.h"
40 #include "protocol/protocol.h"
41 #include "protocol/protocol_api.h"
42 #include "common/system.h"
43 #include "client/client.h"
45 #define TIMEOUT() timeval_current_ofs(options.timelimit, 0)
47 #define SRVID_CTDB_TOOL (CTDB_SRVID_TOOL_RANGE | 0x0001000000000000LL)
48 #define SRVID_CTDB_PUSHDB (CTDB_SRVID_TOOL_RANGE | 0x0002000000000000LL)
50 static struct {
51 const char *socket;
52 const char *debuglevelstr;
53 int timelimit;
54 int pnn;
55 int machinereadable;
56 const char *sep;
57 int machineparsable;
58 int verbose;
59 int maxruntime;
60 int printemptyrecords;
61 int printdatasize;
62 int printlmaster;
63 int printhash;
64 int printrecordflags;
65 } options;
67 static poptContext pc;
69 struct ctdb_context {
70 struct tevent_context *ev;
71 struct ctdb_client_context *client;
72 struct ctdb_node_map *nodemap;
73 uint32_t pnn, cmd_pnn;
74 uint64_t srvid;
77 static void usage(const char *command);
80 * Utility Functions
83 static double timeval_delta(struct timeval *tv2, struct timeval *tv)
85 return (tv2->tv_sec - tv->tv_sec) +
86 (tv2->tv_usec - tv->tv_usec) * 1.0e-6;
89 static struct ctdb_node_and_flags *get_node_by_pnn(
90 struct ctdb_node_map *nodemap,
91 uint32_t pnn)
93 int i;
95 for (i=0; i<nodemap->num; i++) {
96 if (nodemap->node[i].pnn == pnn) {
97 return &nodemap->node[i];
100 return NULL;
103 static const char *pretty_print_flags(TALLOC_CTX *mem_ctx, uint32_t flags)
105 static const struct {
106 uint32_t flag;
107 const char *name;
108 } flag_names[] = {
109 { NODE_FLAGS_DISCONNECTED, "DISCONNECTED" },
110 { NODE_FLAGS_PERMANENTLY_DISABLED, "DISABLED" },
111 { NODE_FLAGS_BANNED, "BANNED" },
112 { NODE_FLAGS_UNHEALTHY, "UNHEALTHY" },
113 { NODE_FLAGS_DELETED, "DELETED" },
114 { NODE_FLAGS_STOPPED, "STOPPED" },
115 { NODE_FLAGS_INACTIVE, "INACTIVE" },
117 char *flags_str = NULL;
118 int i;
120 for (i=0; i<ARRAY_SIZE(flag_names); i++) {
121 if (flags & flag_names[i].flag) {
122 if (flags_str == NULL) {
123 flags_str = talloc_asprintf(mem_ctx,
124 "%s", flag_names[i].name);
125 } else {
126 flags_str = talloc_asprintf_append(flags_str,
127 "|%s", flag_names[i].name);
129 if (flags_str == NULL) {
130 return "OUT-OF-MEMORY";
134 if (flags_str == NULL) {
135 return "OK";
138 return flags_str;
141 static uint64_t next_srvid(struct ctdb_context *ctdb)
143 ctdb->srvid += 1;
144 return ctdb->srvid;
148 * Get consistent nodemap information.
150 * If nodemap is already cached, use that. If not get it.
151 * If the current node is BANNED, then get nodemap from "better" node.
153 static struct ctdb_node_map *get_nodemap(struct ctdb_context *ctdb, bool force)
155 TALLOC_CTX *tmp_ctx;
156 struct ctdb_node_map *nodemap;
157 struct ctdb_node_and_flags *node;
158 uint32_t current_node;
159 int ret;
161 if (force) {
162 TALLOC_FREE(ctdb->nodemap);
165 if (ctdb->nodemap != NULL) {
166 return ctdb->nodemap;
169 tmp_ctx = talloc_new(ctdb);
170 if (tmp_ctx == NULL) {
171 return false;
174 current_node = ctdb->pnn;
175 again:
176 ret = ctdb_ctrl_get_nodemap(tmp_ctx, ctdb->ev, ctdb->client,
177 current_node, TIMEOUT(), &nodemap);
178 if (ret != 0) {
179 fprintf(stderr, "Failed to get nodemap from node %u\n",
180 current_node);
181 goto failed;
184 node = get_node_by_pnn(nodemap, current_node);
185 if (node->flags & NODE_FLAGS_BANNED) {
186 /* Pick next node */
187 do {
188 current_node = (current_node + 1) % nodemap->num;
189 node = get_node_by_pnn(nodemap, current_node);
190 if (! (node->flags &
191 (NODE_FLAGS_DELETED|NODE_FLAGS_DISCONNECTED))) {
192 break;
194 } while (current_node != ctdb->pnn);
196 if (current_node == ctdb->pnn) {
197 /* Tried all nodes in the cluster */
198 fprintf(stderr, "Warning: All nodes are banned.\n");
199 goto failed;
202 goto again;
205 ctdb->nodemap = talloc_steal(ctdb, nodemap);
206 return nodemap;
208 failed:
209 talloc_free(tmp_ctx);
210 return NULL;
213 static bool verify_pnn(struct ctdb_context *ctdb, int pnn)
215 struct ctdb_node_map *nodemap;
216 bool found;
217 int i;
219 if (pnn == -1) {
220 return false;
223 nodemap = get_nodemap(ctdb, false);
224 if (nodemap == NULL) {
225 return false;
228 found = false;
229 for (i=0; i<nodemap->num; i++) {
230 if (nodemap->node[i].pnn == pnn) {
231 found = true;
232 break;
235 if (! found) {
236 fprintf(stderr, "Node %u does not exist\n", pnn);
237 return false;
240 if (nodemap->node[i].flags &
241 (NODE_FLAGS_DISCONNECTED|NODE_FLAGS_DELETED)) {
242 fprintf(stderr, "Node %u has status %s\n", pnn,
243 pretty_print_flags(ctdb, nodemap->node[i].flags));
244 return false;
247 return true;
250 static struct ctdb_node_map *talloc_nodemap(TALLOC_CTX *mem_ctx,
251 struct ctdb_node_map *nodemap)
253 struct ctdb_node_map *nodemap2;
255 nodemap2 = talloc_zero(mem_ctx, struct ctdb_node_map);
256 if (nodemap2 == NULL) {
257 return NULL;
260 nodemap2->node = talloc_array(nodemap2, struct ctdb_node_and_flags,
261 nodemap->num);
262 if (nodemap2->node == NULL) {
263 talloc_free(nodemap2);
264 return NULL;
267 return nodemap2;
271 * Get the number and the list of matching nodes
273 * nodestring := NULL | all | pnn,[pnn,...]
275 * If nodestring is NULL, use the current node.
277 static bool parse_nodestring(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
278 const char *nodestring,
279 struct ctdb_node_map **out)
281 struct ctdb_node_map *nodemap, *nodemap2;
282 struct ctdb_node_and_flags *node;
283 int i;
285 nodemap = get_nodemap(ctdb, false);
286 if (nodemap == NULL) {
287 return false;
290 nodemap2 = talloc_nodemap(mem_ctx, nodemap);
291 if (nodemap2 == NULL) {
292 return false;
295 if (nodestring == NULL) {
296 for (i=0; i<nodemap->num; i++) {
297 if (nodemap->node[i].pnn == ctdb->cmd_pnn) {
298 nodemap2->node[0] = nodemap->node[i];
299 break;
302 nodemap2->num = 1;
304 goto done;
307 if (strcmp(nodestring, "all") == 0) {
308 for (i=0; i<nodemap->num; i++) {
309 nodemap2->node[i] = nodemap->node[i];
311 nodemap2->num = nodemap->num;
313 goto done;
314 } else {
315 char *ns, *tok;
317 ns = talloc_strdup(mem_ctx, nodestring);
318 if (ns == NULL) {
319 return false;
322 tok = strtok(ns, ",");
323 while (tok != NULL) {
324 uint32_t pnn;
325 char *endptr;
327 pnn = (uint32_t)strtoul(tok, &endptr, 0);
328 if (pnn == 0 && tok == endptr) {
329 fprintf(stderr, "Invalid node %s\n", tok);
330 return false;
333 node = get_node_by_pnn(nodemap, pnn);
334 if (node == NULL) {
335 fprintf(stderr, "Node %u does not exist\n",
336 pnn);
337 return false;
340 nodemap2->node[nodemap2->num] = *node;
341 nodemap2->num += 1;
343 tok = strtok(NULL, ",");
347 done:
348 *out = nodemap2;
349 return true;
352 /* Compare IP address */
353 static bool ctdb_same_ip(ctdb_sock_addr *ip1, ctdb_sock_addr *ip2)
355 bool ret = false;
357 if (ip1->sa.sa_family != ip2->sa.sa_family) {
358 return false;
361 switch (ip1->sa.sa_family) {
362 case AF_INET:
363 ret = (memcmp(&ip1->ip.sin_addr, &ip2->ip.sin_addr,
364 sizeof(struct in_addr)) == 0);
365 break;
367 case AF_INET6:
368 ret = (memcmp(&ip1->ip6.sin6_addr, &ip2->ip6.sin6_addr,
369 sizeof(struct in6_addr)) == 0);
370 break;
373 return ret;
376 /* Append a node to a node map with given address and flags */
377 static bool node_map_add(struct ctdb_node_map *nodemap,
378 const char *nstr, uint32_t flags)
380 ctdb_sock_addr addr;
381 uint32_t num;
382 struct ctdb_node_and_flags *n;
384 if (! parse_ip(nstr, NULL, 0, &addr)) {
385 fprintf(stderr, "Invalid IP address %s\n", nstr);
386 return false;
389 num = nodemap->num;
390 nodemap->node = talloc_realloc(nodemap, nodemap->node,
391 struct ctdb_node_and_flags, num+1);
392 if (nodemap->node == NULL) {
393 return false;
396 n = &nodemap->node[num];
397 n->addr = addr;
398 n->pnn = num;
399 n->flags = flags;
401 nodemap->num = num+1;
402 return true;
405 /* Read a nodes file into a node map */
406 static struct ctdb_node_map *ctdb_read_nodes_file(TALLOC_CTX *mem_ctx,
407 const char *nlist)
409 char **lines;
410 int nlines;
411 int i;
412 struct ctdb_node_map *nodemap;
414 nodemap = talloc_zero(mem_ctx, struct ctdb_node_map);
415 if (nodemap == NULL) {
416 return NULL;
419 lines = file_lines_load(nlist, &nlines, 0, mem_ctx);
420 if (lines == NULL) {
421 return NULL;
424 while (nlines > 0 && strcmp(lines[nlines-1], "") == 0) {
425 nlines--;
428 for (i=0; i<nlines; i++) {
429 char *node;
430 uint32_t flags;
431 size_t len;
433 node = lines[i];
434 /* strip leading spaces */
435 while((*node == ' ') || (*node == '\t')) {
436 node++;
439 len = strlen(node);
441 /* strip trailing spaces */
442 while ((len > 1) &&
443 ((node[len-1] == ' ') || (node[len-1] == '\t')))
445 node[len-1] = '\0';
446 len--;
449 if (len == 0) {
450 continue;
452 if (*node == '#') {
453 /* A "deleted" node is a node that is
454 commented out in the nodes file. This is
455 used instead of removing a line, which
456 would cause subsequent nodes to change
457 their PNN. */
458 flags = NODE_FLAGS_DELETED;
459 node = discard_const("0.0.0.0");
460 } else {
461 flags = 0;
463 if (! node_map_add(nodemap, node, flags)) {
464 talloc_free(lines);
465 TALLOC_FREE(nodemap);
466 return NULL;
470 talloc_free(lines);
471 return nodemap;
474 static struct ctdb_node_map *read_nodes_file(TALLOC_CTX *mem_ctx, uint32_t pnn)
476 struct ctdb_node_map *nodemap;
477 char *nodepath;
478 const char *nodes_list = NULL;
480 if (pnn != CTDB_UNKNOWN_PNN) {
481 nodepath = talloc_asprintf(mem_ctx, "CTDB_NODES_%u", pnn);
482 if (nodepath != NULL) {
483 nodes_list = getenv(nodepath);
486 if (nodes_list == NULL) {
487 nodes_list = getenv("CTDB_NODES");
489 if (nodes_list == NULL) {
490 const char *basedir = getenv("CTDB_BASE");
491 if (basedir == NULL) {
492 basedir = CTDB_ETCDIR;
494 nodes_list = talloc_asprintf(mem_ctx, "%s/nodes", basedir);
495 if (nodes_list == NULL) {
496 fprintf(stderr, "Memory allocation error\n");
497 return NULL;
501 nodemap = ctdb_read_nodes_file(mem_ctx, nodes_list);
502 if (nodemap == NULL) {
503 fprintf(stderr, "Failed to read nodes file \"%s\"\n",
504 nodes_list);
505 return NULL;
508 return nodemap;
511 static struct ctdb_dbid *db_find(TALLOC_CTX *mem_ctx,
512 struct ctdb_context *ctdb,
513 struct ctdb_dbid_map *dbmap,
514 const char *db_name)
516 struct ctdb_dbid *db = NULL;
517 const char *name;
518 int ret, i;
520 for (i=0; i<dbmap->num; i++) {
521 ret = ctdb_ctrl_get_dbname(mem_ctx, ctdb->ev, ctdb->client,
522 ctdb->pnn, TIMEOUT(),
523 dbmap->dbs[i].db_id, &name);
524 if (ret != 0) {
525 return false;
528 if (strcmp(db_name, name) == 0) {
529 talloc_free(discard_const(name));
530 db = &dbmap->dbs[i];
531 break;
535 return db;
538 static bool db_exists(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
539 const char *db_arg, uint32_t *db_id,
540 const char **db_name, uint8_t *db_flags)
542 struct ctdb_dbid_map *dbmap;
543 struct ctdb_dbid *db = NULL;
544 uint32_t id = 0;
545 const char *name = NULL;
546 int ret, i;
548 ret = ctdb_ctrl_get_dbmap(mem_ctx, ctdb->ev, ctdb->client,
549 ctdb->pnn, TIMEOUT(), &dbmap);
550 if (ret != 0) {
551 return false;
554 if (strncmp(db_arg, "0x", 2) == 0) {
555 id = strtoul(db_arg, NULL, 0);
556 for (i=0; i<dbmap->num; i++) {
557 if (id == dbmap->dbs[i].db_id) {
558 db = &dbmap->dbs[i];
559 break;
562 } else {
563 name = db_arg;
564 db = db_find(mem_ctx, ctdb, dbmap, name);
567 if (db == NULL) {
568 fprintf(stderr, "No database matching '%s' found\n", db_arg);
569 return false;
572 if (name == NULL) {
573 ret = ctdb_ctrl_get_dbname(mem_ctx, ctdb->ev, ctdb->client,
574 ctdb->pnn, TIMEOUT(), id, &name);
575 if (ret != 0) {
576 return false;
580 if (db_id != NULL) {
581 *db_id = db->db_id;
583 if (db_name != NULL) {
584 *db_name = talloc_strdup(mem_ctx, name);
586 if (db_flags != NULL) {
587 *db_flags = db->flags;
589 return true;
592 static int h2i(char h)
594 if (h >= 'a' && h <= 'f') {
595 return h - 'a' + 10;
597 if (h >= 'A' && h <= 'F') {
598 return h - 'f' + 10;
600 return h - '0';
603 static int hex_to_data(const char *str, size_t len, TALLOC_CTX *mem_ctx,
604 TDB_DATA *out)
606 int i;
607 TDB_DATA data;
609 if (len & 0x01) {
610 fprintf(stderr, "Key (%s) contains odd number of hex digits\n",
611 str);
612 return EINVAL;
615 data.dsize = len / 2;
616 data.dptr = talloc_size(mem_ctx, data.dsize);
617 if (data.dptr == NULL) {
618 return ENOMEM;
621 for (i=0; i<data.dsize; i++) {
622 data.dptr[i] = h2i(str[i*2]) << 4 | h2i(str[i*2+1]);
625 *out = data;
626 return 0;
629 static int str_to_data(const char *str, size_t len, TALLOC_CTX *mem_ctx,
630 TDB_DATA *out)
632 TDB_DATA data;
633 int ret = 0;
635 if (strncmp(str, "0x", 2) == 0) {
636 ret = hex_to_data(str+2, len-2, mem_ctx, &data);
637 } else {
638 data.dptr = talloc_memdup(mem_ctx, str, len);
639 if (data.dptr == NULL) {
640 return ENOMEM;
642 data.dsize = len;
645 *out = data;
646 return ret;
649 static int run_helper(TALLOC_CTX *mem_ctx, const char *command,
650 const char *path, int argc, const char **argv)
652 pid_t pid;
653 int save_errno, status, ret;
654 const char **new_argv;
655 int i;
657 new_argv = talloc_array(mem_ctx, const char *, argc + 2);
658 if (new_argv == NULL) {
659 return ENOMEM;
662 new_argv[0] = path;
663 for (i=0; i<argc; i++) {
664 new_argv[i+1] = argv[i];
666 new_argv[argc+1] = NULL;
668 pid = fork();
669 if (pid < 0) {
670 save_errno = errno;
671 talloc_free(new_argv);
672 fprintf(stderr, "Failed to fork %s (%s) - %s\n",
673 command, path, strerror(save_errno));
674 return save_errno;
677 if (pid == 0) {
678 ret = execv(path, discard_const(new_argv));
679 if (ret == -1) {
680 _exit(64+errno);
682 /* Should not happen */
683 _exit(64+ENOEXEC);
686 talloc_free(new_argv);
688 ret = waitpid(pid, &status, 0);
689 if (ret == -1) {
690 save_errno = errno;
691 fprintf(stderr, "waitpid() failed for %s - %s\n",
692 command, strerror(save_errno));
693 return save_errno;
696 if (WIFEXITED(status)) {
697 int pstatus = WEXITSTATUS(status);
698 if (WIFSIGNALED(status)) {
699 fprintf(stderr, "%s terminated with signal %d\n",
700 command, WTERMSIG(status));
701 ret = EINTR;
702 } else if (pstatus >= 64 && pstatus < 255) {
703 fprintf(stderr, "%s failed with error %d\n",
704 command, pstatus-64);
705 ret = pstatus - 64;
706 } else {
707 ret = pstatus;
709 return ret;
710 } else if (WIFSIGNALED(status)) {
711 fprintf(stderr, "%s terminated with signal %d\n",
712 command, WTERMSIG(status));
713 return EINTR;
716 return 0;
720 * Command Functions
723 static int control_version(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
724 int argc, const char **argv)
726 printf("%s\n", CTDB_VERSION_STRING);
727 return 0;
730 static bool partially_online(TALLOC_CTX *mem_ctx,
731 struct ctdb_context *ctdb,
732 struct ctdb_node_and_flags *node)
734 struct ctdb_iface_list *iface_list;
735 int ret, i;
736 bool status = false;
738 if (node->flags != 0) {
739 return false;
742 ret = ctdb_ctrl_get_ifaces(mem_ctx, ctdb->ev, ctdb->client,
743 node->pnn, TIMEOUT(), &iface_list);
744 if (ret != 0) {
745 return false;
748 status = false;
749 for (i=0; i < iface_list->num; i++) {
750 if (iface_list->iface[i].link_state == 0) {
751 status = true;
752 break;
756 return status;
759 static void print_nodemap_machine(TALLOC_CTX *mem_ctx,
760 struct ctdb_context *ctdb,
761 struct ctdb_node_map *nodemap,
762 uint32_t mypnn)
764 struct ctdb_node_and_flags *node;
765 int i;
767 printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
768 options.sep,
769 "Node", options.sep,
770 "IP", options.sep,
771 "Disconnected", options.sep,
772 "Banned", options.sep,
773 "Disabled", options.sep,
774 "Unhealthy", options.sep,
775 "Stopped", options.sep,
776 "Inactive", options.sep,
777 "PartiallyOnline", options.sep,
778 "ThisNode", options.sep);
780 for (i=0; i<nodemap->num; i++) {
781 node = &nodemap->node[i];
782 if (node->flags & NODE_FLAGS_DELETED) {
783 continue;
786 printf("%s%u%s%s%s%d%s%d%s%d%s%d%s%d%s%d%s%d%s%c%s\n",
787 options.sep,
788 node->pnn, options.sep,
789 ctdb_sock_addr_to_string(mem_ctx, &node->addr),
790 options.sep,
791 !! (node->flags & NODE_FLAGS_DISCONNECTED), options.sep,
792 !! (node->flags & NODE_FLAGS_BANNED), options.sep,
793 !! (node->flags & NODE_FLAGS_PERMANENTLY_DISABLED),
794 options.sep,
795 !! (node->flags & NODE_FLAGS_UNHEALTHY), options.sep,
796 !! (node->flags & NODE_FLAGS_STOPPED), options.sep,
797 !! (node->flags & NODE_FLAGS_INACTIVE), options.sep,
798 partially_online(mem_ctx, ctdb, node), options.sep,
799 (node->pnn == mypnn)?'Y':'N', options.sep);
804 static void print_nodemap(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
805 struct ctdb_node_map *nodemap, uint32_t mypnn,
806 bool print_header)
808 struct ctdb_node_and_flags *node;
809 int num_deleted_nodes = 0;
810 int i;
812 for (i=0; i<nodemap->num; i++) {
813 if (nodemap->node[i].flags & NODE_FLAGS_DELETED) {
814 num_deleted_nodes++;
818 if (print_header) {
819 if (num_deleted_nodes == 0) {
820 printf("Number of nodes:%d\n", nodemap->num);
821 } else {
822 printf("Number of nodes:%d "
823 "(including %d deleted nodes)\n",
824 nodemap->num, num_deleted_nodes);
828 for (i=0; i<nodemap->num; i++) {
829 node = &nodemap->node[i];
830 if (node->flags & NODE_FLAGS_DELETED) {
831 continue;
834 printf("pnn:%u %-16s %s%s\n",
835 node->pnn,
836 ctdb_sock_addr_to_string(mem_ctx, &node->addr),
837 partially_online(mem_ctx, ctdb, node) ?
838 "PARTIALLYONLINE" :
839 pretty_print_flags(mem_ctx, node->flags),
840 node->pnn == mypnn ? " (THIS NODE)" : "");
844 static void print_status(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
845 struct ctdb_node_map *nodemap, uint32_t mypnn,
846 struct ctdb_vnn_map *vnnmap, int recmode,
847 uint32_t recmaster)
849 int i;
851 print_nodemap(mem_ctx, ctdb, nodemap, mypnn, true);
853 if (vnnmap->generation == INVALID_GENERATION) {
854 printf("Generation:INVALID\n");
855 } else {
856 printf("Generation:%u\n", vnnmap->generation);
858 printf("Size:%d\n", vnnmap->size);
859 for (i=0; i<vnnmap->size; i++) {
860 printf("hash:%d lmaster:%d\n", i, vnnmap->map[i]);
863 printf("Recovery mode:%s (%d)\n",
864 recmode == CTDB_RECOVERY_NORMAL ? "NORMAL" : "RECOVERY",
865 recmode);
866 printf("Recovery master:%d\n", recmaster);
869 static int control_status(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
870 int argc, const char **argv)
872 struct ctdb_node_map *nodemap;
873 struct ctdb_vnn_map *vnnmap;
874 int recmode;
875 uint32_t recmaster;
876 int ret;
878 if (argc != 0) {
879 usage("status");
882 nodemap = get_nodemap(ctdb, false);
883 if (nodemap == NULL) {
884 return 1;
887 if (options.machinereadable == 1) {
888 print_nodemap_machine(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn);
889 return 0;
892 ret = ctdb_ctrl_getvnnmap(mem_ctx, ctdb->ev, ctdb->client,
893 ctdb->cmd_pnn, TIMEOUT(), &vnnmap);
894 if (ret != 0) {
895 return ret;
898 ret = ctdb_ctrl_get_recmode(mem_ctx, ctdb->ev, ctdb->client,
899 ctdb->cmd_pnn, TIMEOUT(), &recmode);
900 if (ret != 0) {
901 return ret;
904 ret = ctdb_ctrl_get_recmaster(mem_ctx, ctdb->ev, ctdb->client,
905 ctdb->cmd_pnn, TIMEOUT(), &recmaster);
906 if (ret != 0) {
907 return ret;
910 print_status(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn, vnnmap,
911 recmode, recmaster);
912 return 0;
915 static int control_uptime(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
916 int argc, const char **argv)
918 struct ctdb_uptime *uptime;
919 int ret, tmp, days, hours, minutes, seconds;
921 ret = ctdb_ctrl_uptime(mem_ctx, ctdb->ev, ctdb->client,
922 ctdb->cmd_pnn, TIMEOUT(), &uptime);
923 if (ret != 0) {
924 return ret;
927 printf("Current time of node %-4u : %s",
928 ctdb->cmd_pnn, ctime(&uptime->current_time.tv_sec));
930 tmp = uptime->current_time.tv_sec - uptime->ctdbd_start_time.tv_sec;
931 seconds = tmp % 60; tmp /= 60;
932 minutes = tmp % 60; tmp /= 60;
933 hours = tmp % 24; tmp /= 24;
934 days = tmp;
936 printf("Ctdbd start time : (%03d %02d:%02d:%02d) %s",
937 days, hours, minutes, seconds,
938 ctime(&uptime->ctdbd_start_time.tv_sec));
940 tmp = uptime->current_time.tv_sec - uptime->last_recovery_finished.tv_sec;
941 seconds = tmp % 60; tmp /= 60;
942 minutes = tmp % 60; tmp /= 60;
943 hours = tmp % 24; tmp /= 24;
944 days = tmp;
946 printf("Time of last recovery/failover: (%03d %02d:%02d:%02d) %s",
947 days, hours, minutes, seconds,
948 ctime(&uptime->last_recovery_finished.tv_sec));
950 printf("Duration of last recovery/failover: %lf seconds\n",
951 timeval_delta(&uptime->last_recovery_finished,
952 &uptime->last_recovery_started));
954 return 0;
957 static int control_ping(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
958 int argc, const char **argv)
960 struct timeval tv;
961 int ret, num_clients;
963 tv = timeval_current();
964 ret = ctdb_ctrl_ping(mem_ctx, ctdb->ev, ctdb->client,
965 ctdb->cmd_pnn, TIMEOUT(), &num_clients);
966 if (ret != 0) {
967 return ret;
970 printf("response from %u time=%.6f sec (%d clients)\n",
971 ctdb->cmd_pnn, timeval_elapsed(&tv), num_clients);
972 return 0;
975 const char *runstate_to_string(enum ctdb_runstate runstate);
976 enum ctdb_runstate runstate_from_string(const char *runstate_str);
978 static int control_runstate(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
979 int argc, const char **argv)
981 enum ctdb_runstate runstate;
982 bool found;
983 int ret, i;
985 ret = ctdb_ctrl_get_runstate(mem_ctx, ctdb->ev, ctdb->client,
986 ctdb->cmd_pnn, TIMEOUT(), &runstate);
987 if (ret != 0) {
988 return ret;
991 found = true;
992 for (i=0; i<argc; i++) {
993 enum ctdb_runstate t;
995 found = false;
996 t = ctdb_runstate_from_string(argv[i]);
997 if (t == CTDB_RUNSTATE_UNKNOWN) {
998 printf("Invalid run state (%s)\n", argv[i]);
999 return 1;
1002 if (t == runstate) {
1003 found = true;
1004 break;
1008 if (! found) {
1009 printf("CTDB not in required run state (got %s)\n",
1010 ctdb_runstate_to_string(runstate));
1011 return 1;
1014 printf("%s\n", ctdb_runstate_to_string(runstate));
1015 return 0;
1018 static int control_getvar(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1019 int argc, const char **argv)
1021 struct ctdb_var_list *tun_var_list;
1022 uint32_t value;
1023 int ret, i;
1024 bool found;
1026 if (argc != 1) {
1027 usage("getvar");
1030 ret = ctdb_ctrl_list_tunables(mem_ctx, ctdb->ev, ctdb->client,
1031 ctdb->cmd_pnn, TIMEOUT(), &tun_var_list);
1032 if (ret != 0) {
1033 fprintf(stderr,
1034 "Failed to get list of variables from node %u\n",
1035 ctdb->cmd_pnn);
1036 return ret;
1039 found = false;
1040 for (i=0; i<tun_var_list->count; i++) {
1041 if (strcasecmp(tun_var_list->var[i], argv[0]) == 0) {
1042 found = true;
1043 break;
1047 if (! found) {
1048 printf("No such tunable %s\n", argv[0]);
1049 return 1;
1052 ret = ctdb_ctrl_get_tunable(mem_ctx, ctdb->ev, ctdb->client,
1053 ctdb->cmd_pnn, TIMEOUT(), argv[0], &value);
1054 if (ret != 0) {
1055 return ret;
1058 printf("%-26s = %u\n", argv[0], value);
1059 return 0;
1062 static int control_setvar(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1063 int argc, const char **argv)
1065 struct ctdb_var_list *tun_var_list;
1066 struct ctdb_tunable tunable;
1067 int ret, i;
1068 bool found;
1070 if (argc != 2) {
1071 usage("setvar");
1074 ret = ctdb_ctrl_list_tunables(mem_ctx, ctdb->ev, ctdb->client,
1075 ctdb->cmd_pnn, TIMEOUT(), &tun_var_list);
1076 if (ret != 0) {
1077 fprintf(stderr,
1078 "Failed to get list of variables from node %u\n",
1079 ctdb->cmd_pnn);
1080 return ret;
1083 found = false;
1084 for (i=0; i<tun_var_list->count; i++) {
1085 if (strcasecmp(tun_var_list->var[i], argv[0]) == 0) {
1086 found = true;
1087 break;
1091 if (! found) {
1092 printf("No such tunable %s\n", argv[0]);
1093 return 1;
1096 tunable.name = argv[0];
1097 tunable.value = strtoul(argv[1], NULL, 0);
1099 ret = ctdb_ctrl_set_tunable(mem_ctx, ctdb->ev, ctdb->client,
1100 ctdb->cmd_pnn, TIMEOUT(), &tunable);
1101 if (ret != 0) {
1102 if (ret == 1) {
1103 fprintf(stderr,
1104 "Setting obsolete tunable variable '%s'\n",
1105 tunable.name);
1106 return 0;
1110 return ret;
1113 static int control_listvars(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1114 int argc, const char **argv)
1116 struct ctdb_var_list *tun_var_list;
1117 int ret, i;
1119 if (argc != 0) {
1120 usage("listvars");
1123 ret = ctdb_ctrl_list_tunables(mem_ctx, ctdb->ev, ctdb->client,
1124 ctdb->cmd_pnn, TIMEOUT(), &tun_var_list);
1125 if (ret != 0) {
1126 return ret;
1129 for (i=0; i<tun_var_list->count; i++) {
1130 control_getvar(mem_ctx, ctdb, 1, &tun_var_list->var[i]);
1133 return 0;
1136 const struct {
1137 const char *name;
1138 uint32_t offset;
1139 } stats_fields[] = {
1140 #define STATISTICS_FIELD(n) { #n, offsetof(struct ctdb_statistics, n) }
1141 STATISTICS_FIELD(num_clients),
1142 STATISTICS_FIELD(frozen),
1143 STATISTICS_FIELD(recovering),
1144 STATISTICS_FIELD(num_recoveries),
1145 STATISTICS_FIELD(client_packets_sent),
1146 STATISTICS_FIELD(client_packets_recv),
1147 STATISTICS_FIELD(node_packets_sent),
1148 STATISTICS_FIELD(node_packets_recv),
1149 STATISTICS_FIELD(keepalive_packets_sent),
1150 STATISTICS_FIELD(keepalive_packets_recv),
1151 STATISTICS_FIELD(node.req_call),
1152 STATISTICS_FIELD(node.reply_call),
1153 STATISTICS_FIELD(node.req_dmaster),
1154 STATISTICS_FIELD(node.reply_dmaster),
1155 STATISTICS_FIELD(node.reply_error),
1156 STATISTICS_FIELD(node.req_message),
1157 STATISTICS_FIELD(node.req_control),
1158 STATISTICS_FIELD(node.reply_control),
1159 STATISTICS_FIELD(client.req_call),
1160 STATISTICS_FIELD(client.req_message),
1161 STATISTICS_FIELD(client.req_control),
1162 STATISTICS_FIELD(timeouts.call),
1163 STATISTICS_FIELD(timeouts.control),
1164 STATISTICS_FIELD(timeouts.traverse),
1165 STATISTICS_FIELD(locks.num_calls),
1166 STATISTICS_FIELD(locks.num_current),
1167 STATISTICS_FIELD(locks.num_pending),
1168 STATISTICS_FIELD(locks.num_failed),
1169 STATISTICS_FIELD(total_calls),
1170 STATISTICS_FIELD(pending_calls),
1171 STATISTICS_FIELD(childwrite_calls),
1172 STATISTICS_FIELD(pending_childwrite_calls),
1173 STATISTICS_FIELD(memory_used),
1174 STATISTICS_FIELD(max_hop_count),
1175 STATISTICS_FIELD(total_ro_delegations),
1176 STATISTICS_FIELD(total_ro_revokes),
1179 #define LATENCY_AVG(v) ((v).num ? (v).total / (v).num : 0.0 )
1181 static void print_statistics_machine(struct ctdb_statistics *s,
1182 bool show_header)
1184 int i;
1186 if (show_header) {
1187 printf("CTDB version%s", options.sep);
1188 printf("Current time of statistics%s", options.sep);
1189 printf("Statistics collected since%s", options.sep);
1190 for (i=0; i<ARRAY_SIZE(stats_fields); i++) {
1191 printf("%s%s", stats_fields[i].name, options.sep);
1193 printf("num_reclock_ctdbd_latency%s", options.sep);
1194 printf("min_reclock_ctdbd_latency%s", options.sep);
1195 printf("avg_reclock_ctdbd_latency%s", options.sep);
1196 printf("max_reclock_ctdbd_latency%s", options.sep);
1198 printf("num_reclock_recd_latency%s", options.sep);
1199 printf("min_reclock_recd_latency%s", options.sep);
1200 printf("avg_reclock_recd_latency%s", options.sep);
1201 printf("max_reclock_recd_latency%s", options.sep);
1203 printf("num_call_latency%s", options.sep);
1204 printf("min_call_latency%s", options.sep);
1205 printf("avg_call_latency%s", options.sep);
1206 printf("max_call_latency%s", options.sep);
1208 printf("num_lockwait_latency%s", options.sep);
1209 printf("min_lockwait_latency%s", options.sep);
1210 printf("avg_lockwait_latency%s", options.sep);
1211 printf("max_lockwait_latency%s", options.sep);
1213 printf("num_childwrite_latency%s", options.sep);
1214 printf("min_childwrite_latency%s", options.sep);
1215 printf("avg_childwrite_latency%s", options.sep);
1216 printf("max_childwrite_latency%s", options.sep);
1217 printf("\n");
1220 printf("%u%s", CTDB_PROTOCOL, options.sep);
1221 printf("%u%s", (uint32_t)s->statistics_current_time.tv_sec, options.sep);
1222 printf("%u%s", (uint32_t)s->statistics_start_time.tv_sec, options.sep);
1223 for (i=0;i<ARRAY_SIZE(stats_fields);i++) {
1224 printf("%u%s",
1225 *(uint32_t *)(stats_fields[i].offset+(uint8_t *)s),
1226 options.sep);
1228 printf("%u%s", s->reclock.ctdbd.num, options.sep);
1229 printf("%.6f%s", s->reclock.ctdbd.min, options.sep);
1230 printf("%.6f%s", LATENCY_AVG(s->reclock.ctdbd), options.sep);
1231 printf("%.6f%s", s->reclock.ctdbd.max, options.sep);
1233 printf("%u%s", s->reclock.recd.num, options.sep);
1234 printf("%.6f%s", s->reclock.recd.min, options.sep);
1235 printf("%.6f%s", LATENCY_AVG(s->reclock.recd), options.sep);
1236 printf("%.6f%s", s->reclock.recd.max, options.sep);
1238 printf("%d%s", s->call_latency.num, options.sep);
1239 printf("%.6f%s", s->call_latency.min, options.sep);
1240 printf("%.6f%s", LATENCY_AVG(s->call_latency), options.sep);
1241 printf("%.6f%s", s->call_latency.max, options.sep);
1243 printf("%d%s", s->childwrite_latency.num, options.sep);
1244 printf("%.6f%s", s->childwrite_latency.min, options.sep);
1245 printf("%.6f%s", LATENCY_AVG(s->childwrite_latency), options.sep);
1246 printf("%.6f%s", s->childwrite_latency.max, options.sep);
1247 printf("\n");
1250 static void print_statistics(struct ctdb_statistics *s)
1252 int tmp, days, hours, minutes, seconds;
1253 int i;
1254 const char *prefix = NULL;
1255 int preflen = 0;
1257 tmp = s->statistics_current_time.tv_sec -
1258 s->statistics_start_time.tv_sec;
1259 seconds = tmp % 60; tmp /= 60;
1260 minutes = tmp % 60; tmp /= 60;
1261 hours = tmp % 24; tmp /= 24;
1262 days = tmp;
1264 printf("CTDB version %u\n", CTDB_PROTOCOL);
1265 printf("Current time of statistics : %s",
1266 ctime(&s->statistics_current_time.tv_sec));
1267 printf("Statistics collected since : (%03d %02d:%02d:%02d) %s",
1268 days, hours, minutes, seconds,
1269 ctime(&s->statistics_start_time.tv_sec));
1271 for (i=0; i<ARRAY_SIZE(stats_fields); i++) {
1272 if (strchr(stats_fields[i].name, '.') != NULL) {
1273 preflen = strcspn(stats_fields[i].name, ".") + 1;
1274 if (! prefix ||
1275 strncmp(prefix, stats_fields[i].name, preflen) != 0) {
1276 prefix = stats_fields[i].name;
1277 printf(" %*.*s\n", preflen-1, preflen-1,
1278 stats_fields[i].name);
1280 } else {
1281 preflen = 0;
1283 printf(" %*s%-22s%*s%10u\n", preflen ? 4 : 0, "",
1284 stats_fields[i].name+preflen, preflen ? 0 : 4, "",
1285 *(uint32_t *)(stats_fields[i].offset+(uint8_t *)s));
1288 printf(" hop_count_buckets:");
1289 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
1290 printf(" %d", s->hop_count_bucket[i]);
1292 printf("\n");
1293 printf(" lock_buckets:");
1294 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
1295 printf(" %d", s->locks.buckets[i]);
1297 printf("\n");
1298 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1299 "locks_latency MIN/AVG/MAX",
1300 s->locks.latency.min, LATENCY_AVG(s->locks.latency),
1301 s->locks.latency.max, s->locks.latency.num);
1303 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1304 "reclock_ctdbd MIN/AVG/MAX",
1305 s->reclock.ctdbd.min, LATENCY_AVG(s->reclock.ctdbd),
1306 s->reclock.ctdbd.max, s->reclock.ctdbd.num);
1308 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1309 "reclock_recd MIN/AVG/MAX",
1310 s->reclock.recd.min, LATENCY_AVG(s->reclock.recd),
1311 s->reclock.recd.max, s->reclock.recd.num);
1313 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1314 "call_latency MIN/AVG/MAX",
1315 s->call_latency.min, LATENCY_AVG(s->call_latency),
1316 s->call_latency.max, s->call_latency.num);
1318 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1319 "childwrite_latency MIN/AVG/MAX",
1320 s->childwrite_latency.min,
1321 LATENCY_AVG(s->childwrite_latency),
1322 s->childwrite_latency.max, s->childwrite_latency.num);
1325 static int control_statistics(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1326 int argc, const char **argv)
1328 struct ctdb_statistics *stats;
1329 int ret;
1331 if (argc != 0) {
1332 usage("statistics");
1335 ret = ctdb_ctrl_statistics(mem_ctx, ctdb->ev, ctdb->client,
1336 ctdb->cmd_pnn, TIMEOUT(), &stats);
1337 if (ret != 0) {
1338 return ret;
1341 if (options.machinereadable) {
1342 print_statistics_machine(stats, true);
1343 } else {
1344 print_statistics(stats);
1347 return 0;
1350 static int control_statistics_reset(TALLOC_CTX *mem_ctx,
1351 struct ctdb_context *ctdb,
1352 int argc, const char **argv)
1354 int ret;
1356 if (argc != 0) {
1357 usage("statisticsreset");
1360 ret = ctdb_ctrl_statistics_reset(mem_ctx, ctdb->ev, ctdb->client,
1361 ctdb->cmd_pnn, TIMEOUT());
1362 if (ret != 0) {
1363 return ret;
1366 return 0;
1369 static int control_stats(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1370 int argc, const char **argv)
1372 struct ctdb_statistics_list *slist;
1373 int ret, count = 0, i;
1374 bool show_header = true;
1376 if (argc > 1) {
1377 usage("stats");
1380 if (argc == 1) {
1381 count = atoi(argv[0]);
1384 ret = ctdb_ctrl_get_stat_history(mem_ctx, ctdb->ev, ctdb->client,
1385 ctdb->cmd_pnn, TIMEOUT(), &slist);
1386 if (ret != 0) {
1387 return ret;
1390 for (i=0; i<slist->num; i++) {
1391 if (slist->stats[i].statistics_start_time.tv_sec == 0) {
1392 continue;
1394 if (options.machinereadable == 1) {
1395 print_statistics_machine(&slist->stats[i],
1396 show_header);
1397 show_header = false;
1398 } else {
1399 print_statistics(&slist->stats[i]);
1401 if (count > 0 && i == count) {
1402 break;
1406 return 0;
1409 static int ctdb_public_ip_cmp(const void *a, const void *b)
1411 const struct ctdb_public_ip *ip_a = a;
1412 const struct ctdb_public_ip *ip_b = b;
1414 return ctdb_sock_addr_cmp(&ip_a->addr, &ip_b->addr);
1417 static void print_ip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1418 struct ctdb_public_ip_list *ips,
1419 struct ctdb_public_ip_info **ipinfo,
1420 bool all_nodes)
1422 int i, j;
1423 char *conf, *avail, *active;
1425 if (options.machinereadable == 1) {
1426 printf("%s%s%s%s%s", options.sep,
1427 "Public IP", options.sep,
1428 "Node", options.sep);
1429 if (options.verbose == 1) {
1430 printf("%s%s%s%s%s%s\n",
1431 "ActiveInterfaces", options.sep,
1432 "AvailableInterfaces", options.sep,
1433 "ConfiguredInterfaces", options.sep);
1434 } else {
1435 printf("\n");
1437 } else {
1438 if (all_nodes) {
1439 printf("Public IPs on ALL nodes\n");
1440 } else {
1441 printf("Public IPs on node %u\n", ctdb->cmd_pnn);
1445 for (i = 0; i < ips->num; i++) {
1447 if (options.machinereadable == 1) {
1448 printf("%s%s%s%d%s", options.sep,
1449 ctdb_sock_addr_to_string(
1450 mem_ctx, &ips->ip[i].addr),
1451 options.sep,
1452 (int)ips->ip[i].pnn, options.sep);
1453 } else {
1454 printf("%s", ctdb_sock_addr_to_string(
1455 mem_ctx, &ips->ip[i].addr));
1458 if (options.verbose == 0) {
1459 if (options.machinereadable == 1) {
1460 printf("\n");
1461 } else {
1462 printf(" %d\n", (int)ips->ip[i].pnn);
1464 continue;
1467 conf = NULL;
1468 avail = NULL;
1469 active = NULL;
1471 if (ipinfo[i] == NULL) {
1472 goto skip_ipinfo;
1475 for (j=0; j<ipinfo[i]->ifaces->num; j++) {
1476 struct ctdb_iface *iface;
1478 iface = &ipinfo[i]->ifaces->iface[j];
1479 if (conf == NULL) {
1480 conf = talloc_strdup(mem_ctx, iface->name);
1481 } else {
1482 conf = talloc_asprintf_append(
1483 conf, ",%s", iface->name);
1486 if (ipinfo[i]->active_idx == j) {
1487 active = iface->name;
1490 if (iface->link_state == 0) {
1491 continue;
1494 if (avail == NULL) {
1495 avail = talloc_strdup(mem_ctx, iface->name);
1496 } else {
1497 avail = talloc_asprintf_append(
1498 avail, ",%s", iface->name);
1502 skip_ipinfo:
1504 if (options.machinereadable == 1) {
1505 printf("%s%s%s%s%s%s\n",
1506 active ? active : "", options.sep,
1507 avail ? avail : "", options.sep,
1508 conf ? conf : "", options.sep);
1509 } else {
1510 printf(" node[%d] active[%s] available[%s]"
1511 " configured[%s]\n",
1512 (int)ips->ip[i].pnn, active ? active : "",
1513 avail ? avail : "", conf ? conf : "");
1518 static int collect_ips(uint8_t *keybuf, size_t keylen, uint8_t *databuf,
1519 size_t datalen, void *private_data)
1521 struct ctdb_public_ip_list *ips = talloc_get_type_abort(
1522 private_data, struct ctdb_public_ip_list);
1523 struct ctdb_public_ip *ip;
1525 ip = (struct ctdb_public_ip *)databuf;
1526 ips->ip[ips->num] = *ip;
1527 ips->num += 1;
1529 return 0;
1532 static int get_all_public_ips(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx,
1533 struct ctdb_public_ip_list **out)
1535 struct ctdb_node_map *nodemap;
1536 struct ctdb_public_ip_list *ips;
1537 struct db_hash_context *ipdb;
1538 uint32_t *pnn_list;
1539 int ret, count, i, j;
1541 nodemap = get_nodemap(ctdb, false);
1542 if (nodemap == NULL) {
1543 return 1;
1546 ret = db_hash_init(mem_ctx, "ips", 101, DB_HASH_COMPLEX, &ipdb);
1547 if (ret != 0) {
1548 goto failed;
1551 count = list_of_active_nodes(nodemap, CTDB_UNKNOWN_PNN, mem_ctx,
1552 &pnn_list);
1553 if (count <= 0) {
1554 goto failed;
1557 for (i=0; i<count; i++) {
1558 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
1559 pnn_list[i], TIMEOUT(),
1560 false, &ips);
1561 if (ret != 0) {
1562 goto failed;
1565 for (j=0; j<ips->num; j++) {
1566 struct ctdb_public_ip ip;
1568 ip.pnn = ips->ip[j].pnn;
1569 ip.addr = ips->ip[j].addr;
1571 if (pnn_list[i] == ip.pnn) {
1572 /* Node claims IP is hosted on it, so
1573 * save that information
1575 ret = db_hash_add(ipdb, (uint8_t *)&ip.addr,
1576 sizeof(ip.addr),
1577 (uint8_t *)&ip, sizeof(ip));
1578 if (ret != 0) {
1579 goto failed;
1581 } else {
1582 /* Node thinks IP is hosted elsewhere,
1583 * so overwrite with CTDB_UNKNOWN_PNN
1584 * if there's no existing entry
1586 ret = db_hash_exists(ipdb, (uint8_t *)&ip.addr,
1587 sizeof(ip.addr));
1588 if (ret == ENOENT) {
1589 ip.pnn = CTDB_UNKNOWN_PNN;
1590 ret = db_hash_add(ipdb,
1591 (uint8_t *)&ip.addr,
1592 sizeof(ip.addr),
1593 (uint8_t *)&ip,
1594 sizeof(ip));
1595 if (ret != 0) {
1596 goto failed;
1602 TALLOC_FREE(ips);
1605 talloc_free(pnn_list);
1607 ret = db_hash_traverse(ipdb, NULL, NULL, &count);
1608 if (ret != 0) {
1609 goto failed;
1612 ips = talloc_zero(mem_ctx, struct ctdb_public_ip_list);
1613 if (ips == NULL) {
1614 goto failed;
1617 ips->ip = talloc_array(ips, struct ctdb_public_ip, count);
1618 if (ips->ip == NULL) {
1619 goto failed;
1622 ret = db_hash_traverse(ipdb, collect_ips, ips, &count);
1623 if (ret != 0) {
1624 goto failed;
1627 if (count != ips->num) {
1628 goto failed;
1631 talloc_free(ipdb);
1633 *out = ips;
1634 return 0;
1636 failed:
1637 talloc_free(ipdb);
1638 return 1;
1641 static int control_ip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1642 int argc, const char **argv)
1644 struct ctdb_public_ip_list *ips;
1645 struct ctdb_public_ip_info **ipinfo;
1646 int ret, i;
1647 bool do_all = false;
1649 if (argc > 1) {
1650 usage("ip");
1653 if (argc == 1) {
1654 if (strcmp(argv[0], "all") == 0) {
1655 do_all = true;
1656 } else {
1657 usage("ip");
1661 if (do_all) {
1662 ret = get_all_public_ips(ctdb, mem_ctx, &ips);
1663 } else {
1664 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
1665 ctdb->cmd_pnn, TIMEOUT(),
1666 false, &ips);
1668 if (ret != 0) {
1669 return ret;
1672 qsort(ips->ip, ips->num, sizeof(struct ctdb_public_ip),
1673 ctdb_public_ip_cmp);
1675 ipinfo = talloc_array(mem_ctx, struct ctdb_public_ip_info *, ips->num);
1676 if (ipinfo == NULL) {
1677 return 1;
1680 for (i=0; i<ips->num; i++) {
1681 uint32_t pnn;
1682 if (do_all) {
1683 pnn = ips->ip[i].pnn;
1684 } else {
1685 pnn = ctdb->cmd_pnn;
1687 if (pnn == CTDB_UNKNOWN_PNN) {
1688 ipinfo[i] = NULL;
1689 continue;
1691 ret = ctdb_ctrl_get_public_ip_info(mem_ctx, ctdb->ev,
1692 ctdb->client, pnn,
1693 TIMEOUT(), &ips->ip[i].addr,
1694 &ipinfo[i]);
1695 if (ret != 0) {
1696 return ret;
1700 print_ip(mem_ctx, ctdb, ips, ipinfo, do_all);
1701 return 0;
1704 static int control_ipinfo(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1705 int argc, const char **argv)
1707 struct ctdb_public_ip_info *ipinfo;
1708 ctdb_sock_addr addr;
1709 int ret, i;
1711 if (argc != 1) {
1712 usage("ipinfo");
1715 if (! parse_ip(argv[0], NULL, 0, &addr)) {
1716 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
1717 return 1;
1720 ret = ctdb_ctrl_get_public_ip_info(mem_ctx, ctdb->ev, ctdb->client,
1721 ctdb->cmd_pnn, TIMEOUT(), &addr,
1722 &ipinfo);
1723 if (ret != 0) {
1724 if (ret == -1) {
1725 printf("Node %u does not know about IP %s\n",
1726 ctdb->cmd_pnn, argv[0]);
1728 return ret;
1731 printf("Public IP[%s] info on node %u\n",
1732 ctdb_sock_addr_to_string(mem_ctx, &ipinfo->ip.addr),
1733 ctdb->cmd_pnn);
1735 printf("IP:%s\nCurrentNode:%u\nNumInterfaces:%u\n",
1736 ctdb_sock_addr_to_string(mem_ctx, &ipinfo->ip.addr),
1737 ipinfo->ip.pnn, ipinfo->ifaces->num);
1739 for (i=0; i<ipinfo->ifaces->num; i++) {
1740 struct ctdb_iface *iface;
1742 iface = &ipinfo->ifaces->iface[i];
1743 iface->name[CTDB_IFACE_SIZE] = '\0';
1744 printf("Interface[%u]: Name:%s Link:%s References:%u%s\n",
1745 i+1, iface->name,
1746 iface->link_state == 0 ? "down" : "up",
1747 iface->references,
1748 (i == ipinfo->active_idx) ? " (active)" : "");
1751 return 0;
1754 static int control_ifaces(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1755 int argc, const char **argv)
1757 struct ctdb_iface_list *ifaces;
1758 int ret, i;
1760 if (argc != 0) {
1761 usage("ifaces");
1764 ret = ctdb_ctrl_get_ifaces(mem_ctx, ctdb->ev, ctdb->client,
1765 ctdb->cmd_pnn, TIMEOUT(), &ifaces);
1766 if (ret != 0) {
1767 return ret;
1770 if (ifaces->num == 0) {
1771 printf("No interfaces configured on node %u\n",
1772 ctdb->cmd_pnn);
1773 return 0;
1776 if (options.machinereadable) {
1777 printf("%s%s%s%s%s%s%s\n", options.sep,
1778 "Name", options.sep,
1779 "LinkStatus", options.sep,
1780 "References", options.sep);
1781 } else {
1782 printf("Interfaces on node %u\n", ctdb->cmd_pnn);
1785 for (i=0; i<ifaces->num; i++) {
1786 if (options.machinereadable) {
1787 printf("%s%s%s%u%s%u%s\n", options.sep,
1788 ifaces->iface[i].name, options.sep,
1789 ifaces->iface[i].link_state, options.sep,
1790 ifaces->iface[i].references, options.sep);
1791 } else {
1792 printf("name:%s link:%s references:%u\n",
1793 ifaces->iface[i].name,
1794 ifaces->iface[i].link_state ? "up" : "down",
1795 ifaces->iface[i].references);
1799 return 0;
1802 static int control_setifacelink(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1803 int argc, const char **argv)
1805 struct ctdb_iface_list *ifaces;
1806 struct ctdb_iface *iface;
1807 int ret, i;
1809 if (argc != 2) {
1810 usage("setifacelink");
1813 if (strlen(argv[0]) > CTDB_IFACE_SIZE) {
1814 fprintf(stderr, "Interface name '%s' too long\n", argv[0]);
1815 return 1;
1818 ret = ctdb_ctrl_get_ifaces(mem_ctx, ctdb->ev, ctdb->client,
1819 ctdb->cmd_pnn, TIMEOUT(), &ifaces);
1820 if (ret != 0) {
1821 fprintf(stderr,
1822 "Failed to get interface information from node %u\n",
1823 ctdb->cmd_pnn);
1824 return ret;
1827 iface = NULL;
1828 for (i=0; i<ifaces->num; i++) {
1829 if (strcmp(ifaces->iface[i].name, argv[0]) == 0) {
1830 iface = &ifaces->iface[i];
1831 break;
1835 if (iface == NULL) {
1836 printf("Interface %s not configured on node %u\n",
1837 argv[0], ctdb->cmd_pnn);
1838 return 1;
1841 if (strcmp(argv[1], "up") == 0) {
1842 iface->link_state = 1;
1843 } else if (strcmp(argv[1], "down") == 0) {
1844 iface->link_state = 0;
1845 } else {
1846 usage("setifacelink");
1847 return 1;
1850 iface->references = 0;
1852 ret = ctdb_ctrl_set_iface_link_state(mem_ctx, ctdb->ev, ctdb->client,
1853 ctdb->cmd_pnn, TIMEOUT(), iface);
1854 if (ret != 0) {
1855 return ret;
1858 return 0;
1861 static int control_process_exists(TALLOC_CTX *mem_ctx,
1862 struct ctdb_context *ctdb,
1863 int argc, const char **argv)
1865 pid_t pid;
1866 int ret, status;
1868 if (argc != 1) {
1869 usage("process-exists");
1872 pid = atoi(argv[0]);
1873 ret = ctdb_ctrl_process_exists(mem_ctx, ctdb->ev, ctdb->client,
1874 ctdb->cmd_pnn, TIMEOUT(), pid, &status);
1875 if (ret != 0) {
1876 return ret;
1879 if (status == 0) {
1880 printf("PID %u exists\n", pid);
1881 } else {
1882 printf("PID %u does not exist\n", pid);
1884 return status;
1887 static int control_getdbmap(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1888 int argc, const char **argv)
1890 struct ctdb_dbid_map *dbmap;
1891 int ret, i;
1893 if (argc != 0) {
1894 usage("getdbmap");
1897 ret = ctdb_ctrl_get_dbmap(mem_ctx, ctdb->ev, ctdb->client,
1898 ctdb->cmd_pnn, TIMEOUT(), &dbmap);
1899 if (ret != 0) {
1900 return ret;
1903 if (options.machinereadable == 1) {
1904 printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
1905 options.sep,
1906 "ID", options.sep,
1907 "Name", options.sep,
1908 "Path", options.sep,
1909 "Persistent", options.sep,
1910 "Sticky", options.sep,
1911 "Unhealthy", options.sep,
1912 "Readonly", options.sep);
1913 } else {
1914 printf("Number of databases:%d\n", dbmap->num);
1917 for (i=0; i<dbmap->num; i++) {
1918 const char *name;
1919 const char *path;
1920 const char *health;
1921 bool persistent;
1922 bool readonly;
1923 bool sticky;
1924 uint32_t db_id;
1926 db_id = dbmap->dbs[i].db_id;
1928 ret = ctdb_ctrl_get_dbname(mem_ctx, ctdb->ev, ctdb->client,
1929 ctdb->cmd_pnn, TIMEOUT(), db_id,
1930 &name);
1931 if (ret != 0) {
1932 return ret;
1935 ret = ctdb_ctrl_getdbpath(mem_ctx, ctdb->ev, ctdb->client,
1936 ctdb->cmd_pnn, TIMEOUT(), db_id,
1937 &path);
1938 if (ret != 0) {
1939 return ret;
1942 ret = ctdb_ctrl_db_get_health(mem_ctx, ctdb->ev, ctdb->client,
1943 ctdb->cmd_pnn, TIMEOUT(), db_id,
1944 &health);
1945 if (ret != 0) {
1946 return ret;
1949 persistent = dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT;
1950 readonly = dbmap->dbs[i].flags & CTDB_DB_FLAGS_READONLY;
1951 sticky = dbmap->dbs[i].flags & CTDB_DB_FLAGS_STICKY;
1953 if (options.machinereadable == 1) {
1954 printf("%s0x%08X%s%s%s%s%s%d%s%d%s%d%s%d%s\n",
1955 options.sep,
1956 db_id, options.sep,
1957 name, options.sep,
1958 path, options.sep,
1959 !! (persistent), options.sep,
1960 !! (sticky), options.sep,
1961 !! (health), options.sep,
1962 !! (readonly), options.sep);
1963 } else {
1964 printf("dbid:0x%08x name:%s path:%s%s%s%s%s\n",
1965 db_id, name, path,
1966 persistent ? " PERSISTENT" : "",
1967 sticky ? " STICKY" : "",
1968 readonly ? " READONLY" : "",
1969 health ? " UNHEALTHY" : "");
1972 talloc_free(discard_const(name));
1973 talloc_free(discard_const(path));
1974 talloc_free(discard_const(health));
1977 return 0;
1980 static int control_getdbstatus(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1981 int argc, const char **argv)
1983 uint32_t db_id;
1984 const char *db_name, *db_path, *db_health;
1985 uint8_t db_flags;
1986 int ret;
1988 if (argc != 1) {
1989 usage("getdbstatus");
1992 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
1993 return 1;
1996 ret = ctdb_ctrl_getdbpath(mem_ctx, ctdb->ev, ctdb->client,
1997 ctdb->cmd_pnn, TIMEOUT(), db_id,
1998 &db_path);
1999 if (ret != 0) {
2000 return ret;
2003 ret = ctdb_ctrl_db_get_health(mem_ctx, ctdb->ev, ctdb->client,
2004 ctdb->cmd_pnn, TIMEOUT(), db_id,
2005 &db_health);
2006 if (ret != 0) {
2007 return ret;
2010 printf("dbid: 0x%08x\nname: %s\npath: %s\n", db_id, db_name, db_path);
2011 printf("PERSISTENT: %s\nSTICKY: %s\nREADONLY: %s\nHEALTH: %s\n",
2012 (db_flags & CTDB_DB_FLAGS_PERSISTENT ? "yes" : "no"),
2013 (db_flags & CTDB_DB_FLAGS_STICKY ? "yes" : "no"),
2014 (db_flags & CTDB_DB_FLAGS_READONLY ? "yes" : "no"),
2015 (db_health ? db_health : "OK"));
2016 return 0;
2019 struct dump_record_state {
2020 uint32_t count;
2023 #define ISASCII(x) (isprint(x) && ! strchr("\"\\", (x)))
2025 static void dump_tdb_data(const char *name, TDB_DATA val)
2027 int i;
2029 fprintf(stdout, "%s(%zu) = \"", name, val.dsize);
2030 for (i=0; i<val.dsize; i++) {
2031 if (ISASCII(val.dptr[i])) {
2032 fprintf(stdout, "%c", val.dptr[i]);
2033 } else {
2034 fprintf(stdout, "\\%02X", val.dptr[i]);
2037 fprintf(stdout, "\"\n");
2040 static void dump_ltdb_header(struct ctdb_ltdb_header *header)
2042 fprintf(stdout, "dmaster: %u\n", header->dmaster);
2043 fprintf(stdout, "rsn: %" PRIu64 "\n", header->rsn);
2044 fprintf(stdout, "flags: 0x%08x", header->flags);
2045 if (header->flags & CTDB_REC_FLAG_MIGRATED_WITH_DATA) {
2046 fprintf(stdout, " MIGRATED_WITH_DATA");
2048 if (header->flags & CTDB_REC_FLAG_VACUUM_MIGRATED) {
2049 fprintf(stdout, " VACUUM_MIGRATED");
2051 if (header->flags & CTDB_REC_FLAG_AUTOMATIC) {
2052 fprintf(stdout, " AUTOMATIC");
2054 if (header->flags & CTDB_REC_RO_HAVE_DELEGATIONS) {
2055 fprintf(stdout, " RO_HAVE_DELEGATIONS");
2057 if (header->flags & CTDB_REC_RO_HAVE_READONLY) {
2058 fprintf(stdout, " RO_HAVE_READONLY");
2060 if (header->flags & CTDB_REC_RO_REVOKING_READONLY) {
2061 fprintf(stdout, " RO_REVOKING_READONLY");
2063 if (header->flags & CTDB_REC_RO_REVOKE_COMPLETE) {
2064 fprintf(stdout, " RO_REVOKE_COMPLETE");
2066 fprintf(stdout, "\n");
2070 static int dump_record(uint32_t reqid, struct ctdb_ltdb_header *header,
2071 TDB_DATA key, TDB_DATA data, void *private_data)
2073 struct dump_record_state *state =
2074 (struct dump_record_state *)private_data;
2076 state->count += 1;
2078 dump_tdb_data("key", key);
2079 dump_ltdb_header(header);
2080 dump_tdb_data("data", data);
2081 fprintf(stdout, "\n");
2083 return 0;
2086 static int control_catdb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2087 int argc, const char **argv)
2089 struct ctdb_db_context *db;
2090 const char *db_name;
2091 uint32_t db_id;
2092 uint8_t db_flags;
2093 struct dump_record_state state;
2094 int ret;
2096 if (argc != 1) {
2097 usage("catdb");
2100 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
2101 return 1;
2104 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
2105 db_flags, &db);
2106 if (ret != 0) {
2107 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
2108 return ret;
2111 state.count = 0;
2113 ret = ctdb_db_traverse(mem_ctx, ctdb->ev, ctdb->client, db,
2114 ctdb->cmd_pnn, TIMEOUT(),
2115 dump_record, &state);
2117 printf("Dumped %u records\n", state.count);
2119 return ret;
2122 static int control_cattdb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2123 int argc, const char **argv)
2125 struct ctdb_db_context *db;
2126 const char *db_name;
2127 uint32_t db_id;
2128 uint8_t db_flags;
2129 struct dump_record_state state;
2130 int ret;
2132 if (argc != 1) {
2133 usage("catdb");
2136 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
2137 return 1;
2140 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
2141 db_flags, &db);
2142 if (ret != 0) {
2143 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
2144 return ret;
2147 state.count = 0;
2148 ret = ctdb_db_traverse_local(db, true, true, dump_record, &state);
2150 printf("Dumped %u record(s)\n", state.count);
2152 return ret;
2155 static int control_getmonmode(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2156 int argc, const char **argv)
2158 int mode, ret;
2160 if (argc != 0) {
2161 usage("getmonmode");
2164 ret = ctdb_ctrl_get_monmode(mem_ctx, ctdb->ev, ctdb->client,
2165 ctdb->cmd_pnn, TIMEOUT(), &mode);
2166 if (ret != 0) {
2167 return ret;
2170 printf("%s\n",
2171 (mode == CTDB_MONITORING_ENABLED) ? "ENABLED" : "DISABLED");
2172 return 0;
2175 static int control_getcapabilities(TALLOC_CTX *mem_ctx,
2176 struct ctdb_context *ctdb,
2177 int argc, const char **argv)
2179 uint32_t caps;
2180 int ret;
2182 if (argc != 0) {
2183 usage("getcapabilities");
2186 ret = ctdb_ctrl_get_capabilities(mem_ctx, ctdb->ev, ctdb->client,
2187 ctdb->cmd_pnn, TIMEOUT(), &caps);
2188 if (ret != 0) {
2189 return ret;
2192 if (options.machinereadable == 1) {
2193 printf("%s%s%s%s%s\n",
2194 options.sep,
2195 "RECMASTER", options.sep,
2196 "LMASTER", options.sep);
2197 printf("%s%d%s%d%s\n", options.sep,
2198 !! (caps & CTDB_CAP_RECMASTER), options.sep,
2199 !! (caps & CTDB_CAP_LMASTER), options.sep);
2200 } else {
2201 printf("RECMASTER: %s\n",
2202 (caps & CTDB_CAP_RECMASTER) ? "YES" : "NO");
2203 printf("LMASTER: %s\n",
2204 (caps & CTDB_CAP_LMASTER) ? "YES" : "NO");
2207 return 0;
2210 static int control_pnn(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2211 int argc, const char **argv)
2213 printf("%u\n", ctdb_client_pnn(ctdb->client));
2214 return 0;
2217 static int control_lvs(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2218 int argc, const char **argv)
2220 char *t, *lvs_helper = NULL;
2222 if (argc != 1) {
2223 usage("lvs");
2226 t = getenv("CTDB_LVS_HELPER");
2227 if (t != NULL) {
2228 lvs_helper = talloc_strdup(mem_ctx, t);
2229 } else {
2230 lvs_helper = talloc_asprintf(mem_ctx, "%s/ctdb_lvs",
2231 CTDB_HELPER_BINDIR);
2234 if (lvs_helper == NULL) {
2235 fprintf(stderr, "Unable to set LVS helper\n");
2236 return 1;
2239 return run_helper(mem_ctx, "LVS helper", lvs_helper, argc, argv);
2242 static int control_disable_monitor(TALLOC_CTX *mem_ctx,
2243 struct ctdb_context *ctdb,
2244 int argc, const char **argv)
2246 int ret;
2248 if (argc != 0) {
2249 usage("disablemonitor");
2252 ret = ctdb_ctrl_disable_monitor(mem_ctx, ctdb->ev, ctdb->client,
2253 ctdb->cmd_pnn, TIMEOUT());
2254 if (ret != 0) {
2255 return ret;
2258 return 0;
2261 static int control_enable_monitor(TALLOC_CTX *mem_ctx,
2262 struct ctdb_context *ctdb,
2263 int argc, const char **argv)
2265 int ret;
2267 if (argc != 0) {
2268 usage("enablemonitor");
2271 ret = ctdb_ctrl_enable_monitor(mem_ctx, ctdb->ev, ctdb->client,
2272 ctdb->cmd_pnn, TIMEOUT());
2273 if (ret != 0) {
2274 return ret;
2277 return 0;
2280 static int control_setdebug(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2281 int argc, const char **argv)
2283 int log_level;
2284 int ret;
2285 bool found;
2287 if (argc != 1) {
2288 usage("setdebug");
2291 found = debug_level_parse(argv[0], &log_level);
2292 if (! found) {
2293 fprintf(stderr,
2294 "Invalid debug level '%s'. Valid levels are:\n",
2295 argv[0]);
2296 fprintf(stderr, "\tERROR | WARNING | NOTICE | INFO | DEBUG\n");
2297 return 1;
2300 ret = ctdb_ctrl_setdebug(mem_ctx, ctdb->ev, ctdb->client,
2301 ctdb->cmd_pnn, TIMEOUT(), log_level);
2302 if (ret != 0) {
2303 return ret;
2306 return 0;
2309 static int control_getdebug(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2310 int argc, const char **argv)
2312 int loglevel;
2313 const char *log_str;
2314 int ret;
2316 if (argc != 0) {
2317 usage("getdebug");
2320 ret = ctdb_ctrl_getdebug(mem_ctx, ctdb->ev, ctdb->client,
2321 ctdb->cmd_pnn, TIMEOUT(), &loglevel);
2322 if (ret != 0) {
2323 return ret;
2326 log_str = debug_level_to_string(loglevel);
2327 printf("%s\n", log_str);
2329 return 0;
2332 static int control_attach(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2333 int argc, const char **argv)
2335 const char *db_name;
2336 uint8_t db_flags = 0;
2337 int ret;
2339 if (argc < 1 || argc > 2) {
2340 usage("attach");
2343 db_name = argv[0];
2344 if (argc == 2) {
2345 if (strcmp(argv[1], "persistent") == 0) {
2346 db_flags = CTDB_DB_FLAGS_PERSISTENT;
2347 } else if (strcmp(argv[1], "readonly") == 0) {
2348 db_flags = CTDB_DB_FLAGS_READONLY;
2349 } else if (strcmp(argv[1], "sticky") == 0) {
2350 db_flags = CTDB_DB_FLAGS_STICKY;
2351 } else {
2352 usage("attach");
2356 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
2357 db_flags, NULL);
2358 if (ret != 0) {
2359 return ret;
2362 return 0;
2365 static int control_detach(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2366 int argc, const char **argv)
2368 const char *db_name;
2369 uint32_t db_id;
2370 uint8_t db_flags;
2371 struct ctdb_node_map *nodemap;
2372 int recmode;
2373 int ret, ret2, i;
2375 if (argc < 1) {
2376 usage("detach");
2379 ret = ctdb_ctrl_get_recmode(mem_ctx, ctdb->ev, ctdb->client,
2380 ctdb->cmd_pnn, TIMEOUT(), &recmode);
2381 if (ret != 0) {
2382 return ret;
2385 if (recmode == CTDB_RECOVERY_ACTIVE) {
2386 fprintf(stderr, "Database cannot be detached"
2387 " when recovery is active\n");
2388 return 1;
2391 nodemap = get_nodemap(ctdb, false);
2392 if (nodemap == NULL) {
2393 return 1;
2396 for (i=0; i<nodemap->num; i++) {
2397 uint32_t value;
2399 if (nodemap->node[i].flags & NODE_FLAGS_DISCONNECTED) {
2400 continue;
2402 if (nodemap->node[i].flags & NODE_FLAGS_DELETED) {
2403 continue;
2405 if (nodemap->node[i].flags & NODE_FLAGS_INACTIVE) {
2406 fprintf(stderr, "Database cannot be detached on"
2407 " inactive (stopped or banned) node %u\n",
2408 nodemap->node[i].pnn);
2409 return 1;
2412 ret = ctdb_ctrl_get_tunable(mem_ctx, ctdb->ev, ctdb->client,
2413 nodemap->node[i].pnn, TIMEOUT(),
2414 "AllowClientDBAttach", &value);
2415 if (ret != 0) {
2416 fprintf(stderr,
2417 "Unable to get tunable AllowClientDBAttach"
2418 " from node %u\n", nodemap->node[i].pnn);
2419 return ret;
2422 if (value == 1) {
2423 fprintf(stderr,
2424 "Database access is still active on node %u."
2425 " Set AllowclientDBAttach=0 on all nodes.\n",
2426 nodemap->node[i].pnn);
2427 return 1;
2431 ret2 = 0;
2432 for (i=0; i<argc; i++) {
2433 if (! db_exists(mem_ctx, ctdb, argv[i], &db_id, &db_name,
2434 &db_flags)) {
2435 continue;
2438 if (db_flags & CTDB_DB_FLAGS_PERSISTENT) {
2439 fprintf(stderr,
2440 "Persistent database %s cannot be detached\n",
2441 argv[0]);
2442 return 1;
2445 ret = ctdb_detach(ctdb->ev, ctdb->client, TIMEOUT(), db_id);
2446 if (ret != 0) {
2447 fprintf(stderr, "Database %s detach failed\n", db_name);
2448 ret2 = ret;
2452 return ret2;
2455 static int control_dumpmemory(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2456 int argc, const char **argv)
2458 const char *mem_str;
2459 ssize_t n;
2460 int ret;
2462 ret = ctdb_ctrl_dump_memory(mem_ctx, ctdb->ev, ctdb->client,
2463 ctdb->cmd_pnn, TIMEOUT(), &mem_str);
2464 if (ret != 0) {
2465 return ret;
2468 n = write(1, mem_str, strlen(mem_str)+1);
2469 if (n < 0 || n != strlen(mem_str)+1) {
2470 fprintf(stderr, "Failed to write talloc summary\n");
2471 return 1;
2474 return 0;
2477 static void dump_memory(uint64_t srvid, TDB_DATA data, void *private_data)
2479 bool *done = (bool *)private_data;
2480 ssize_t n;
2482 n = write(1, data.dptr, data.dsize);
2483 if (n < 0 || n != data.dsize) {
2484 fprintf(stderr, "Failed to write talloc summary\n");
2487 *done = true;
2490 static int control_rddumpmemory(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2491 int argc, const char **argv)
2493 struct ctdb_srvid_message msg = { 0 };
2494 int ret;
2495 bool done = false;
2497 msg.pnn = ctdb->pnn;
2498 msg.srvid = next_srvid(ctdb);
2500 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
2501 msg.srvid, dump_memory, &done);
2502 if (ret != 0) {
2503 return ret;
2506 ret = ctdb_message_mem_dump(mem_ctx, ctdb->ev, ctdb->client,
2507 ctdb->cmd_pnn, &msg);
2508 if (ret != 0) {
2509 return ret;
2512 ctdb_client_wait(ctdb->ev, &done);
2513 return 0;
2516 static int control_getpid(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2517 int argc, const char **argv)
2519 pid_t pid;
2520 int ret;
2522 ret = ctdb_ctrl_get_pid(mem_ctx, ctdb->ev, ctdb->client,
2523 ctdb->cmd_pnn, TIMEOUT(), &pid);
2524 if (ret != 0) {
2525 return ret;
2528 printf("%u\n", pid);
2529 return 0;
2532 static int check_flags(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2533 const char *desc, uint32_t flag, bool set_flag)
2535 struct ctdb_node_map *nodemap;
2536 bool flag_is_set;
2538 nodemap = get_nodemap(ctdb, false);
2539 if (nodemap == NULL) {
2540 return 1;
2543 flag_is_set = nodemap->node[ctdb->cmd_pnn].flags & flag;
2544 if (set_flag == flag_is_set) {
2545 if (set_flag) {
2546 fprintf(stderr, "Node %u is already %s\n",
2547 ctdb->cmd_pnn, desc);
2548 } else {
2549 fprintf(stderr, "Node %u is not %s\n",
2550 ctdb->cmd_pnn, desc);
2552 return 0;
2555 return 1;
2558 static void wait_for_flags(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2559 uint32_t flag, bool set_flag)
2561 struct ctdb_node_map *nodemap;
2562 bool flag_is_set;
2564 while (1) {
2565 nodemap = get_nodemap(ctdb, true);
2566 if (nodemap == NULL) {
2567 fprintf(stderr,
2568 "Failed to get nodemap, trying again\n");
2569 sleep(1);
2570 continue;
2573 flag_is_set = nodemap->node[ctdb->cmd_pnn].flags & flag;
2574 if (flag_is_set == set_flag) {
2575 break;
2578 sleep(1);
2582 static int ctdb_ctrl_modflags(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
2583 struct ctdb_client_context *client,
2584 uint32_t destnode, struct timeval timeout,
2585 uint32_t set, uint32_t clear)
2587 struct ctdb_node_map *nodemap;
2588 struct ctdb_node_flag_change flag_change;
2589 struct ctdb_req_control request;
2590 uint32_t *pnn_list;
2591 int ret, count;
2593 ret = ctdb_ctrl_get_nodemap(mem_ctx, ev, client, destnode,
2594 tevent_timeval_zero(), &nodemap);
2595 if (ret != 0) {
2596 return ret;
2599 flag_change.pnn = destnode;
2600 flag_change.old_flags = nodemap->node[destnode].flags;
2601 flag_change.new_flags = flag_change.old_flags | set;
2602 flag_change.new_flags &= ~clear;
2604 count = list_of_connected_nodes(nodemap, -1, mem_ctx, &pnn_list);
2605 if (count == -1) {
2606 return ENOMEM;
2609 ctdb_req_control_modify_flags(&request, &flag_change);
2610 ret = ctdb_client_control_multi(mem_ctx, ev, client, pnn_list, count,
2611 tevent_timeval_zero(), &request,
2612 NULL, NULL);
2613 return ret;
2616 struct ipreallocate_state {
2617 int status;
2618 bool done;
2621 static void ipreallocate_handler(uint64_t srvid, TDB_DATA data,
2622 void *private_data)
2624 struct ipreallocate_state *state =
2625 (struct ipreallocate_state *)private_data;
2627 if (data.dsize != sizeof(int)) {
2628 /* Ignore packet */
2629 return;
2632 state->status = *(int *)data.dptr;
2633 state->done = true;
2636 static int ipreallocate(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb)
2638 struct ctdb_srvid_message msg = { 0 };
2639 struct ipreallocate_state state;
2640 int ret;
2642 msg.pnn = ctdb->pnn;
2643 msg.srvid = next_srvid(ctdb);
2645 state.done = false;
2646 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
2647 msg.srvid,
2648 ipreallocate_handler, &state);
2649 if (ret != 0) {
2650 return ret;
2653 while (true) {
2654 ret = ctdb_message_takeover_run(mem_ctx, ctdb->ev,
2655 ctdb->client,
2656 CTDB_BROADCAST_CONNECTED,
2657 &msg);
2658 if (ret != 0) {
2659 goto fail;
2662 ret = ctdb_client_wait_timeout(ctdb->ev, &state.done,
2663 TIMEOUT());
2664 if (ret != 0) {
2665 continue;
2668 if (state.status >= 0) {
2669 ret = 0;
2670 } else {
2671 ret = state.status;
2673 break;
2676 fail:
2677 ctdb_client_remove_message_handler(ctdb->ev, ctdb->client,
2678 msg.srvid, &state);
2679 return ret;
2682 static int control_disable(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2683 int argc, const char **argv)
2685 int ret;
2687 if (argc != 0) {
2688 usage("disable");
2691 ret = check_flags(mem_ctx, ctdb, "disabled",
2692 NODE_FLAGS_PERMANENTLY_DISABLED, true);
2693 if (ret == 0) {
2694 return 0;
2697 ret = ctdb_ctrl_modflags(mem_ctx, ctdb->ev, ctdb->client,
2698 ctdb->cmd_pnn, TIMEOUT(),
2699 NODE_FLAGS_PERMANENTLY_DISABLED, 0);
2700 if (ret != 0) {
2701 fprintf(stderr,
2702 "Failed to set DISABLED flag on node %u\n",
2703 ctdb->cmd_pnn);
2704 return ret;
2707 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_PERMANENTLY_DISABLED, true);
2708 return ipreallocate(mem_ctx, ctdb);
2711 static int control_enable(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2712 int argc, const char **argv)
2714 int ret;
2716 if (argc != 0) {
2717 usage("enable");
2720 ret = check_flags(mem_ctx, ctdb, "disabled",
2721 NODE_FLAGS_PERMANENTLY_DISABLED, false);
2722 if (ret == 0) {
2723 return 0;
2726 ret = ctdb_ctrl_modflags(mem_ctx, ctdb->ev, ctdb->client,
2727 ctdb->cmd_pnn, TIMEOUT(),
2728 0, NODE_FLAGS_PERMANENTLY_DISABLED);
2729 if (ret != 0) {
2730 fprintf(stderr, "Failed to reset DISABLED flag on node %u\n",
2731 ctdb->cmd_pnn);
2732 return ret;
2735 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_PERMANENTLY_DISABLED, false);
2736 return ipreallocate(mem_ctx, ctdb);
2739 static int control_stop(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2740 int argc, const char **argv)
2742 int ret;
2744 if (argc != 0) {
2745 usage("stop");
2748 ret = check_flags(mem_ctx, ctdb, "stopped",
2749 NODE_FLAGS_STOPPED, true);
2750 if (ret == 0) {
2751 return 0;
2754 ret = ctdb_ctrl_stop_node(mem_ctx, ctdb->ev, ctdb->client,
2755 ctdb->cmd_pnn, TIMEOUT());
2756 if (ret != 0) {
2757 fprintf(stderr, "Failed to stop node %u\n", ctdb->cmd_pnn);
2758 return ret;
2761 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_STOPPED, true);
2762 return ipreallocate(mem_ctx, ctdb);
2765 static int control_continue(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2766 int argc, const char **argv)
2768 int ret;
2770 if (argc != 0) {
2771 usage("continue");
2774 ret = check_flags(mem_ctx, ctdb, "stopped",
2775 NODE_FLAGS_STOPPED, false);
2776 if (ret == 0) {
2777 return 0;
2780 ret = ctdb_ctrl_continue_node(mem_ctx, ctdb->ev, ctdb->client,
2781 ctdb->cmd_pnn, TIMEOUT());
2782 if (ret != 0) {
2783 fprintf(stderr, "Failed to continue stopped node %u\n",
2784 ctdb->cmd_pnn);
2785 return ret;
2788 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_STOPPED, false);
2789 return ipreallocate(mem_ctx, ctdb);
2792 static int control_ban(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2793 int argc, const char **argv)
2795 struct ctdb_ban_state ban_state;
2796 int ret;
2798 if (argc != 1) {
2799 usage("ban");
2802 ret = check_flags(mem_ctx, ctdb, "banned",
2803 NODE_FLAGS_BANNED, true);
2804 if (ret == 0) {
2805 return 0;
2808 ban_state.pnn = ctdb->cmd_pnn;
2809 ban_state.time = strtoul(argv[0], NULL, 0);
2811 if (ban_state.time == 0) {
2812 fprintf(stderr, "Ban time cannot be zero\n");
2813 return EINVAL;
2816 ret = ctdb_ctrl_set_ban_state(mem_ctx, ctdb->ev, ctdb->client,
2817 ctdb->cmd_pnn, TIMEOUT(), &ban_state);
2818 if (ret != 0) {
2819 fprintf(stderr, "Failed to ban node %u\n", ctdb->cmd_pnn);
2820 return ret;
2823 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_BANNED, true);
2824 return ipreallocate(mem_ctx, ctdb);
2828 static int control_unban(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2829 int argc, const char **argv)
2831 struct ctdb_ban_state ban_state;
2832 int ret;
2834 if (argc != 0) {
2835 usage("unban");
2838 ret = check_flags(mem_ctx, ctdb, "banned",
2839 NODE_FLAGS_BANNED, false);
2840 if (ret == 0) {
2841 return 0;
2844 ban_state.pnn = ctdb->cmd_pnn;
2845 ban_state.time = 0;
2847 ret = ctdb_ctrl_set_ban_state(mem_ctx, ctdb->ev, ctdb->client,
2848 ctdb->cmd_pnn, TIMEOUT(), &ban_state);
2849 if (ret != 0) {
2850 fprintf(stderr, "Failed to unban node %u\n", ctdb->cmd_pnn);
2851 return ret;
2854 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_BANNED, false);
2855 return ipreallocate(mem_ctx, ctdb);
2859 static int control_shutdown(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2860 int argc, const char **argv)
2862 int ret;
2864 if (argc != 0) {
2865 usage("shutdown");
2868 ret = ctdb_ctrl_shutdown(mem_ctx, ctdb->ev, ctdb->client,
2869 ctdb->cmd_pnn, TIMEOUT());
2870 if (ret != 0) {
2871 fprintf(stderr, "Unable to shutdown node %u\n", ctdb->cmd_pnn);
2872 return ret;
2875 return 0;
2878 static int get_generation(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2879 uint32_t *generation)
2881 uint32_t recmaster;
2882 int recmode;
2883 struct ctdb_vnn_map *vnnmap;
2884 int ret;
2886 again:
2887 ret = ctdb_ctrl_get_recmaster(mem_ctx, ctdb->ev, ctdb->client,
2888 ctdb->cmd_pnn, TIMEOUT(), &recmaster);
2889 if (ret != 0) {
2890 fprintf(stderr, "Failed to find recovery master\n");
2891 return ret;
2894 ret = ctdb_ctrl_get_recmode(mem_ctx, ctdb->ev, ctdb->client,
2895 recmaster, TIMEOUT(), &recmode);
2896 if (ret != 0) {
2897 fprintf(stderr, "Failed to get recovery mode from node %u\n",
2898 recmaster);
2899 return ret;
2902 if (recmode == CTDB_RECOVERY_ACTIVE) {
2903 sleep(1);
2904 goto again;
2907 ret = ctdb_ctrl_getvnnmap(mem_ctx, ctdb->ev, ctdb->client,
2908 recmaster, TIMEOUT(), &vnnmap);
2909 if (ret != 0) {
2910 fprintf(stderr, "Failed to get generation from node %u\n",
2911 recmaster);
2912 return ret;
2915 if (vnnmap->generation == INVALID_GENERATION) {
2916 talloc_free(vnnmap);
2917 sleep(1);
2918 goto again;
2921 *generation = vnnmap->generation;
2922 talloc_free(vnnmap);
2923 return 0;
2927 static int control_recover(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2928 int argc, const char **argv)
2930 uint32_t generation, next_generation;
2931 int ret;
2933 if (argc != 0) {
2934 usage("recover");
2937 ret = get_generation(mem_ctx, ctdb, &generation);
2938 if (ret != 0) {
2939 return ret;
2942 ret = ctdb_ctrl_set_recmode(mem_ctx, ctdb->ev, ctdb->client,
2943 ctdb->cmd_pnn, TIMEOUT(),
2944 CTDB_RECOVERY_ACTIVE);
2945 if (ret != 0) {
2946 fprintf(stderr, "Failed to set recovery mode active\n");
2947 return ret;
2950 while (1) {
2951 ret = get_generation(mem_ctx, ctdb, &next_generation);
2952 if (ret != 0) {
2953 fprintf(stderr,
2954 "Failed to confirm end of recovery\n");
2955 return ret;
2958 if (next_generation != generation) {
2959 break;
2962 sleep (1);
2965 return 0;
2968 static int control_ipreallocate(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2969 int argc, const char **argv)
2971 if (argc != 0) {
2972 usage("ipreallocate");
2975 return ipreallocate(mem_ctx, ctdb);
2978 static int control_isnotrecmaster(TALLOC_CTX *mem_ctx,
2979 struct ctdb_context *ctdb,
2980 int argc, const char **argv)
2982 uint32_t recmaster;
2983 int ret;
2985 if (argc != 0) {
2986 usage("isnotrecmaster");
2989 ret = ctdb_ctrl_get_recmaster(mem_ctx, ctdb->ev, ctdb->client,
2990 ctdb->pnn, TIMEOUT(), &recmaster);
2991 if (ret != 0) {
2992 fprintf(stderr, "Failed to get recmaster\n");
2993 return ret;
2996 if (recmaster != ctdb->pnn) {
2997 printf("this node is not the recmaster\n");
2998 return 1;
3001 printf("this node is the recmaster\n");
3002 return 0;
3005 static int control_gratarp(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3006 int argc, const char **argv)
3008 struct ctdb_addr_info addr_info;
3009 int ret;
3011 if (argc != 2) {
3012 usage("gratarp");
3015 if (! parse_ip(argv[0], NULL, 0, &addr_info.addr)) {
3016 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3017 return 1;
3019 addr_info.iface = argv[1];
3021 ret = ctdb_ctrl_send_gratuitous_arp(mem_ctx, ctdb->ev, ctdb->client,
3022 ctdb->cmd_pnn, TIMEOUT(),
3023 &addr_info);
3024 if (ret != 0) {
3025 fprintf(stderr, "Unable to send gratuitous arp from node %u\n",
3026 ctdb->cmd_pnn);
3027 return ret;
3030 return 0;
3033 static int control_tickle(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3034 int argc, const char **argv)
3036 ctdb_sock_addr src, dst;
3037 int ret;
3039 if (argc != 0 && argc != 2) {
3040 usage("tickle");
3043 if (argc == 0) {
3044 struct ctdb_connection *clist;
3045 int count;
3046 int i, num_failed;
3048 ret = ctdb_parse_connections(stdin, mem_ctx, &count, &clist);
3049 if (ret != 0) {
3050 return ret;
3053 num_failed = 0;
3054 for (i=0; i<count; i++) {
3055 ret = ctdb_sys_send_tcp(&clist[i].src,
3056 &clist[i].dst,
3057 0, 0, 0);
3058 if (ret != 0) {
3059 num_failed += 1;
3063 TALLOC_FREE(clist);
3065 if (num_failed > 0) {
3066 fprintf(stderr, "Failed to send %d tickles\n",
3067 num_failed);
3068 return 1;
3071 return 0;
3075 if (! parse_ip_port(argv[0], &src)) {
3076 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3077 return 1;
3080 if (! parse_ip_port(argv[1], &dst)) {
3081 fprintf(stderr, "Invalid IP address %s\n", argv[1]);
3082 return 1;
3085 ret = ctdb_sys_send_tcp(&src, &dst, 0, 0, 0);
3086 if (ret != 0) {
3087 fprintf(stderr, "Failed to send tickle ack\n");
3088 return ret;
3091 return 0;
3094 static int control_gettickles(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3095 int argc, const char **argv)
3097 ctdb_sock_addr addr;
3098 struct ctdb_tickle_list *tickles;
3099 unsigned port = 0;
3100 int ret, i;
3102 if (argc < 1 || argc > 2) {
3103 usage("gettickles");
3106 if (argc == 2) {
3107 port = strtoul(argv[1], NULL, 10);
3110 if (! parse_ip(argv[0], NULL, port, &addr)) {
3111 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3112 return 1;
3115 ret = ctdb_ctrl_get_tcp_tickle_list(mem_ctx, ctdb->ev, ctdb->client,
3116 ctdb->cmd_pnn, TIMEOUT(), &addr,
3117 &tickles);
3118 if (ret != 0) {
3119 fprintf(stderr, "Failed to get list of connections\n");
3120 return ret;
3123 if (options.machinereadable) {
3124 printf("%s%s%s%s%s%s%s%s%s\n",
3125 options.sep,
3126 "Source IP", options.sep,
3127 "Port", options.sep,
3128 "Destiation IP", options.sep,
3129 "Port", options.sep);
3130 for (i=0; i<tickles->num; i++) {
3131 printf("%s%s%s%u%s%s%s%u%s\n", options.sep,
3132 ctdb_sock_addr_to_string(
3133 mem_ctx, &tickles->conn[i].src),
3134 options.sep,
3135 ntohs(tickles->conn[i].src.ip.sin_port),
3136 options.sep,
3137 ctdb_sock_addr_to_string(
3138 mem_ctx, &tickles->conn[i].dst),
3139 options.sep,
3140 ntohs(tickles->conn[i].dst.ip.sin_port),
3141 options.sep);
3143 } else {
3144 printf("Connections for IP: %s\n",
3145 ctdb_sock_addr_to_string(mem_ctx, &tickles->addr));
3146 printf("Num connections: %u\n", tickles->num);
3147 for (i=0; i<tickles->num; i++) {
3148 printf("SRC: %s:%u DST: %s:%u\n",
3149 ctdb_sock_addr_to_string(
3150 mem_ctx, &tickles->conn[i].src),
3151 ntohs(tickles->conn[i].src.ip.sin_port),
3152 ctdb_sock_addr_to_string(
3153 mem_ctx, &tickles->conn[i].dst),
3154 ntohs(tickles->conn[i].dst.ip.sin_port));
3158 talloc_free(tickles);
3159 return 0;
3162 typedef void (*clist_request_func)(struct ctdb_req_control *request,
3163 struct ctdb_connection *conn);
3165 typedef int (*clist_reply_func)(struct ctdb_reply_control *reply);
3167 struct process_clist_state {
3168 struct ctdb_connection *clist;
3169 int count;
3170 int num_failed, num_total;
3171 clist_reply_func reply_func;
3174 static void process_clist_done(struct tevent_req *subreq);
3176 static struct tevent_req *process_clist_send(
3177 TALLOC_CTX *mem_ctx,
3178 struct ctdb_context *ctdb,
3179 struct ctdb_connection *clist,
3180 int count,
3181 clist_request_func request_func,
3182 clist_reply_func reply_func)
3184 struct tevent_req *req, *subreq;
3185 struct process_clist_state *state;
3186 struct ctdb_req_control request;
3187 int i;
3189 req = tevent_req_create(mem_ctx, &state, struct process_clist_state);
3190 if (req == NULL) {
3191 return NULL;
3194 state->clist = clist;
3195 state->count = count;
3196 state->reply_func = reply_func;
3198 for (i=0; i<count; i++) {
3199 request_func(&request, &clist[i]);
3200 subreq = ctdb_client_control_send(state, ctdb->ev,
3201 ctdb->client, ctdb->cmd_pnn,
3202 TIMEOUT(), &request);
3203 if (tevent_req_nomem(subreq, req)) {
3204 return tevent_req_post(req, ctdb->ev);
3206 tevent_req_set_callback(subreq, process_clist_done, req);
3209 return req;
3212 static void process_clist_done(struct tevent_req *subreq)
3214 struct tevent_req *req = tevent_req_callback_data(
3215 subreq, struct tevent_req);
3216 struct process_clist_state *state = tevent_req_data(
3217 req, struct process_clist_state);
3218 struct ctdb_reply_control *reply;
3219 int ret;
3220 bool status;
3222 status = ctdb_client_control_recv(subreq, NULL, state, &reply);
3223 TALLOC_FREE(subreq);
3224 if (! status) {
3225 state->num_failed += 1;
3226 goto done;
3229 ret = state->reply_func(reply);
3230 if (ret != 0) {
3231 state->num_failed += 1;
3232 goto done;
3235 done:
3236 state->num_total += 1;
3237 if (state->num_total == state->count) {
3238 tevent_req_done(req);
3242 static int process_clist_recv(struct tevent_req *req)
3244 struct process_clist_state *state = tevent_req_data(
3245 req, struct process_clist_state);
3247 return state->num_failed;
3250 static int control_addtickle(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3251 int argc, const char **argv)
3253 struct ctdb_connection conn;
3254 int ret;
3256 if (argc != 0 && argc != 2) {
3257 usage("addtickle");
3260 if (argc == 0) {
3261 struct ctdb_connection *clist;
3262 struct tevent_req *req;
3263 int count;
3265 ret = ctdb_parse_connections(stdin, mem_ctx, &count, &clist);
3266 if (ret != 0) {
3267 return ret;
3269 if (count == 0) {
3270 return 0;
3273 req = process_clist_send(mem_ctx, ctdb, clist, count,
3274 ctdb_req_control_tcp_add_delayed_update,
3275 ctdb_reply_control_tcp_add_delayed_update);
3276 if (req == NULL) {
3277 talloc_free(clist);
3278 return ENOMEM;
3281 tevent_req_poll(req, ctdb->ev);
3282 talloc_free(clist);
3284 ret = process_clist_recv(req);
3285 if (ret != 0) {
3286 fprintf(stderr, "Failed to add %d tickles\n", ret);
3287 return 1;
3290 return 0;
3293 if (! parse_ip_port(argv[0], &conn.src)) {
3294 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3295 return 1;
3297 if (! parse_ip_port(argv[1], &conn.dst)) {
3298 fprintf(stderr, "Invalid IP address %s\n", argv[1]);
3299 return 1;
3302 ret = ctdb_ctrl_tcp_add_delayed_update(mem_ctx, ctdb->ev,
3303 ctdb->client, ctdb->cmd_pnn,
3304 TIMEOUT(), &conn);
3305 if (ret != 0) {
3306 fprintf(stderr, "Failed to register connection\n");
3307 return ret;
3310 return 0;
3313 static int control_deltickle(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3314 int argc, const char **argv)
3316 struct ctdb_connection conn;
3317 int ret;
3319 if (argc != 0 && argc != 2) {
3320 usage("deltickle");
3323 if (argc == 0) {
3324 struct ctdb_connection *clist;
3325 struct tevent_req *req;
3326 int count;
3328 ret = ctdb_parse_connections(stdin, mem_ctx, &count, &clist);
3329 if (ret != 0) {
3330 return ret;
3332 if (count == 0) {
3333 return 0;
3336 req = process_clist_send(mem_ctx, ctdb, clist, count,
3337 ctdb_req_control_tcp_remove,
3338 ctdb_reply_control_tcp_remove);
3339 if (req == NULL) {
3340 talloc_free(clist);
3341 return ENOMEM;
3344 tevent_req_poll(req, ctdb->ev);
3345 talloc_free(clist);
3347 ret = process_clist_recv(req);
3348 if (ret != 0) {
3349 fprintf(stderr, "Failed to remove %d tickles\n", ret);
3350 return 1;
3353 return 0;
3356 if (! parse_ip_port(argv[0], &conn.src)) {
3357 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3358 return 1;
3360 if (! parse_ip_port(argv[1], &conn.dst)) {
3361 fprintf(stderr, "Invalid IP address %s\n", argv[1]);
3362 return 1;
3365 ret = ctdb_ctrl_tcp_remove(mem_ctx, ctdb->ev, ctdb->client,
3366 ctdb->cmd_pnn, TIMEOUT(), &conn);
3367 if (ret != 0) {
3368 fprintf(stderr, "Failed to unregister connection\n");
3369 return ret;
3372 return 0;
3375 static int control_check_srvids(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3376 int argc, const char **argv)
3378 uint64_t *srvid;
3379 uint8_t *result;
3380 int ret, i;
3382 if (argc == 0) {
3383 usage("check_srvids");
3386 srvid = talloc_array(mem_ctx, uint64_t, argc);
3387 if (srvid == NULL) {
3388 fprintf(stderr, "Memory allocation error\n");
3389 return 1;
3392 for (i=0; i<argc; i++) {
3393 srvid[i] = strtoull(argv[i], NULL, 0);
3396 ret = ctdb_ctrl_check_srvids(mem_ctx, ctdb->ev, ctdb->client,
3397 ctdb->cmd_pnn, TIMEOUT(), srvid, argc,
3398 &result);
3399 if (ret != 0) {
3400 fprintf(stderr, "Failed to check srvids on node %u\n",
3401 ctdb->cmd_pnn);
3402 return ret;
3405 for (i=0; i<argc; i++) {
3406 printf("SRVID 0x%" PRIx64 " %s\n", srvid[i],
3407 (result[i] ? "exists" : "does not exist"));
3410 return 0;
3413 static int control_listnodes(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3414 int argc, const char **argv)
3416 struct ctdb_node_map *nodemap;
3417 int i;
3419 if (argc != 0) {
3420 usage("listnodes");
3423 nodemap = read_nodes_file(mem_ctx, CTDB_UNKNOWN_PNN);
3424 if (nodemap == NULL) {
3425 return 1;
3428 for (i=0; i<nodemap->num; i++) {
3429 if (nodemap->node[i].flags & NODE_FLAGS_DELETED) {
3430 continue;
3433 if (options.machinereadable) {
3434 printf("%s%u%s%s%s\n", options.sep,
3435 nodemap->node[i].pnn, options.sep,
3436 ctdb_sock_addr_to_string(
3437 mem_ctx, &nodemap->node[i].addr),
3438 options.sep);
3439 } else {
3440 printf("%s\n",
3441 ctdb_sock_addr_to_string(
3442 mem_ctx, &nodemap->node[i].addr));
3446 return 0;
3449 static bool nodemap_identical(struct ctdb_node_map *nodemap1,
3450 struct ctdb_node_map *nodemap2)
3452 int i;
3454 if (nodemap1->num != nodemap2->num) {
3455 return false;
3458 for (i=0; i<nodemap1->num; i++) {
3459 struct ctdb_node_and_flags *n1, *n2;
3461 n1 = &nodemap1->node[i];
3462 n2 = &nodemap2->node[i];
3464 if ((n1->pnn != n2->pnn) ||
3465 (n1->flags != n2->flags) ||
3466 ! ctdb_sock_addr_same_ip(&n1->addr, &n2->addr)) {
3467 return false;
3471 return true;
3474 static int check_node_file_changes(TALLOC_CTX *mem_ctx,
3475 struct ctdb_node_map *nm,
3476 struct ctdb_node_map *fnm,
3477 bool *reload)
3479 int i;
3480 bool check_failed = false;
3482 *reload = false;
3484 for (i=0; i<nm->num; i++) {
3485 if (i >= fnm->num) {
3486 fprintf(stderr,
3487 "Node %u (%s) missing from nodes file\n",
3488 nm->node[i].pnn,
3489 ctdb_sock_addr_to_string(
3490 mem_ctx, &nm->node[i].addr));
3491 check_failed = true;
3492 continue;
3494 if (nm->node[i].flags & NODE_FLAGS_DELETED &&
3495 fnm->node[i].flags & NODE_FLAGS_DELETED) {
3496 /* Node remains deleted */
3497 continue;
3500 if (! (nm->node[i].flags & NODE_FLAGS_DELETED) &&
3501 ! (fnm->node[i].flags & NODE_FLAGS_DELETED)) {
3502 /* Node not newly nor previously deleted */
3503 if (! ctdb_same_ip(&nm->node[i].addr,
3504 &fnm->node[i].addr)) {
3505 fprintf(stderr,
3506 "Node %u has changed IP address"
3507 " (was %s, now %s)\n",
3508 nm->node[i].pnn,
3509 ctdb_sock_addr_to_string(
3510 mem_ctx, &nm->node[i].addr),
3511 ctdb_sock_addr_to_string(
3512 mem_ctx, &fnm->node[i].addr));
3513 check_failed = true;
3514 } else {
3515 if (nm->node[i].flags & NODE_FLAGS_DISCONNECTED) {
3516 fprintf(stderr,
3517 "WARNING: Node %u is disconnected."
3518 " You MUST fix this node manually!\n",
3519 nm->node[i].pnn);
3522 continue;
3525 if (fnm->node[i].flags & NODE_FLAGS_DELETED) {
3526 /* Node is being deleted */
3527 printf("Node %u is DELETED\n", nm->node[i].pnn);
3528 *reload = true;
3529 if (! (nm->node[i].flags & NODE_FLAGS_DISCONNECTED)) {
3530 fprintf(stderr,
3531 "ERROR: Node %u is still connected\n",
3532 nm->node[i].pnn);
3533 check_failed = true;
3535 continue;
3538 if (nm->node[i].flags & NODE_FLAGS_DELETED) {
3539 /* Node was previously deleted */
3540 printf("Node %u is UNDELETED\n", nm->node[i].pnn);
3541 *reload = true;
3545 if (check_failed) {
3546 fprintf(stderr,
3547 "ERROR: Nodes will not be reloaded due to previous error\n");
3548 return 1;
3551 /* Leftover nodes in file are NEW */
3552 for (; i < fnm->num; i++) {
3553 printf("Node %u is NEW\n", fnm->node[i].pnn);
3554 *reload = true;
3557 return 0;
3560 struct disable_recoveries_state {
3561 uint32_t *pnn_list;
3562 int node_count;
3563 bool *reply;
3564 int status;
3565 bool done;
3568 static void disable_recoveries_handler(uint64_t srvid, TDB_DATA data,
3569 void *private_data)
3571 struct disable_recoveries_state *state =
3572 (struct disable_recoveries_state *)private_data;
3573 int ret, i;
3575 if (data.dsize != sizeof(int)) {
3576 /* Ignore packet */
3577 return;
3580 /* ret will be a PNN (i.e. >=0) on success, or negative on error */
3581 ret = *(int *)data.dptr;
3582 if (ret < 0) {
3583 state->status = ret;
3584 state->done = true;
3585 return;
3587 for (i=0; i<state->node_count; i++) {
3588 if (state->pnn_list[i] == ret) {
3589 state->reply[i] = true;
3590 break;
3594 state->done = true;
3595 for (i=0; i<state->node_count; i++) {
3596 if (! state->reply[i]) {
3597 state->done = false;
3598 break;
3603 static int disable_recoveries(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3604 uint32_t timeout, uint32_t *pnn_list, int count)
3606 struct ctdb_disable_message disable = { 0 };
3607 struct disable_recoveries_state state;
3608 int ret, i;
3610 disable.pnn = ctdb->pnn;
3611 disable.srvid = next_srvid(ctdb);
3612 disable.timeout = timeout;
3614 state.pnn_list = pnn_list;
3615 state.node_count = count;
3616 state.done = false;
3617 state.status = 0;
3618 state.reply = talloc_zero_array(mem_ctx, bool, count);
3619 if (state.reply == NULL) {
3620 return ENOMEM;
3623 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
3624 disable.srvid,
3625 disable_recoveries_handler,
3626 &state);
3627 if (ret != 0) {
3628 return ret;
3631 for (i=0; i<count; i++) {
3632 ret = ctdb_message_disable_recoveries(mem_ctx, ctdb->ev,
3633 ctdb->client,
3634 pnn_list[i],
3635 &disable);
3636 if (ret != 0) {
3637 goto fail;
3641 ret = ctdb_client_wait_timeout(ctdb->ev, &state.done, TIMEOUT());
3642 if (ret == ETIME) {
3643 fprintf(stderr, "Timed out waiting to disable recoveries\n");
3644 } else {
3645 ret = (state.status >= 0 ? 0 : 1);
3648 fail:
3649 ctdb_client_remove_message_handler(ctdb->ev, ctdb->client,
3650 disable.srvid, &state);
3651 return ret;
3654 static int control_reloadnodes(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3655 int argc, const char **argv)
3657 struct ctdb_node_map *nodemap = NULL;
3658 struct ctdb_node_map *file_nodemap;
3659 struct ctdb_node_map *remote_nodemap;
3660 struct ctdb_req_control request;
3661 struct ctdb_reply_control **reply;
3662 bool reload;
3663 int ret, i;
3664 uint32_t *pnn_list;
3665 int count;
3667 nodemap = get_nodemap(ctdb, false);
3668 if (nodemap == NULL) {
3669 return 1;
3672 file_nodemap = read_nodes_file(mem_ctx, ctdb->pnn);
3673 if (file_nodemap == NULL) {
3674 return 1;
3677 for (i=0; i<nodemap->num; i++) {
3678 if (nodemap->node[i].flags & NODE_FLAGS_DISCONNECTED) {
3679 continue;
3682 ret = ctdb_ctrl_get_nodes_file(mem_ctx, ctdb->ev, ctdb->client,
3683 nodemap->node[i].pnn, TIMEOUT(),
3684 &remote_nodemap);
3685 if (ret != 0) {
3686 fprintf(stderr,
3687 "ERROR: Failed to get nodes file from node %u\n",
3688 nodemap->node[i].pnn);
3689 return ret;
3692 if (! nodemap_identical(file_nodemap, remote_nodemap)) {
3693 fprintf(stderr,
3694 "ERROR: Nodes file on node %u differs"
3695 " from current node (%u)\n",
3696 nodemap->node[i].pnn, ctdb->pnn);
3697 return 1;
3701 ret = check_node_file_changes(mem_ctx, nodemap, file_nodemap, &reload);
3702 if (ret != 0) {
3703 return ret;
3706 if (! reload) {
3707 fprintf(stderr, "No change in nodes file,"
3708 " skipping unnecessary reload\n");
3709 return 0;
3712 count = list_of_connected_nodes(nodemap, CTDB_UNKNOWN_PNN,
3713 mem_ctx, &pnn_list);
3714 if (count <= 0) {
3715 fprintf(stderr, "Memory allocation error\n");
3716 return 1;
3719 ret = disable_recoveries(mem_ctx, ctdb, 2*options.timelimit,
3720 pnn_list, count);
3721 if (ret != 0) {
3722 fprintf(stderr, "Failed to disable recoveries\n");
3723 return ret;
3726 ctdb_req_control_reload_nodes_file(&request);
3727 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
3728 pnn_list, count, TIMEOUT(),
3729 &request, NULL, &reply);
3730 if (ret != 0) {
3731 bool failed = false;
3733 for (i=0; i<count; i++) {
3734 ret = ctdb_reply_control_reload_nodes_file(reply[i]);
3735 if (ret != 0) {
3736 fprintf(stderr,
3737 "Node %u failed to reload nodes\n",
3738 pnn_list[i]);
3739 failed = true;
3742 if (failed) {
3743 fprintf(stderr,
3744 "You MUST fix failed nodes manually!\n");
3748 ret = disable_recoveries(mem_ctx, ctdb, 0, pnn_list, count);
3749 if (ret != 0) {
3750 fprintf(stderr, "Failed to enable recoveries\n");
3751 return ret;
3754 return 0;
3757 static int moveip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3758 ctdb_sock_addr *addr, uint32_t pnn)
3760 struct ctdb_public_ip_list *pubip_list;
3761 struct ctdb_public_ip pubip;
3762 struct ctdb_node_map *nodemap;
3763 struct ctdb_req_control request;
3764 uint32_t *pnn_list;
3765 int ret, i, count;
3767 ret = ctdb_message_disable_ip_check(mem_ctx, ctdb->ev, ctdb->client,
3768 CTDB_BROADCAST_CONNECTED,
3769 2*options.timelimit);
3770 if (ret != 0) {
3771 fprintf(stderr, "Failed to disable IP check\n");
3772 return ret;
3775 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
3776 pnn, TIMEOUT(), false, &pubip_list);
3777 if (ret != 0) {
3778 fprintf(stderr, "Failed to get Public IPs from node %u\n",
3779 pnn);
3780 return ret;
3783 for (i=0; i<pubip_list->num; i++) {
3784 if (ctdb_same_ip(addr, &pubip_list->ip[i].addr)) {
3785 break;
3789 if (i == pubip_list->num) {
3790 fprintf(stderr, "Node %u CANNOT host IP address %s\n",
3791 pnn, ctdb_sock_addr_to_string(mem_ctx, addr));
3792 return 1;
3795 nodemap = get_nodemap(ctdb, false);
3796 if (nodemap == NULL) {
3797 return 1;
3800 count = list_of_active_nodes(nodemap, pnn, mem_ctx, &pnn_list);
3801 if (count <= 0) {
3802 fprintf(stderr, "Memory allocation error\n");
3803 return 1;
3806 pubip.pnn = pnn;
3807 pubip.addr = *addr;
3808 ctdb_req_control_release_ip(&request, &pubip);
3810 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
3811 pnn_list, count, TIMEOUT(),
3812 &request, NULL, NULL);
3813 if (ret != 0) {
3814 fprintf(stderr, "Failed to release IP on nodes\n");
3815 return ret;
3818 ret = ctdb_ctrl_takeover_ip(mem_ctx, ctdb->ev, ctdb->client,
3819 pnn, TIMEOUT(), &pubip);
3820 if (ret != 0) {
3821 fprintf(stderr, "Failed to takeover IP on node %u\n", pnn);
3822 return ret;
3825 return 0;
3828 static int control_moveip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3829 int argc, const char **argv)
3831 ctdb_sock_addr addr;
3832 uint32_t pnn;
3833 int ret, retries = 0;
3835 if (argc != 2) {
3836 usage("moveip");
3839 if (! parse_ip(argv[0], NULL, 0, &addr)) {
3840 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3841 return 1;
3844 pnn = strtoul(argv[1], NULL, 10);
3845 if (pnn == CTDB_UNKNOWN_PNN) {
3846 fprintf(stderr, "Invalid PNN %s\n", argv[1]);
3847 return 1;
3850 while (retries < 5) {
3851 ret = moveip(mem_ctx, ctdb, &addr, pnn);
3852 if (ret == 0) {
3853 break;
3856 sleep(3);
3857 retries++;
3860 if (ret != 0) {
3861 fprintf(stderr, "Failed to move IP %s to node %u\n",
3862 argv[0], pnn);
3863 return ret;
3866 return 0;
3869 static int rebalancenode(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3870 uint32_t pnn)
3872 int ret;
3874 ret = ctdb_message_rebalance_node(mem_ctx, ctdb->ev, ctdb->client,
3875 CTDB_BROADCAST_CONNECTED, pnn);
3876 if (ret != 0) {
3877 fprintf(stderr,
3878 "Failed to ask recovery master to distribute IPs\n");
3879 return ret;
3882 return 0;
3885 static int control_addip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3886 int argc, const char **argv)
3888 ctdb_sock_addr addr;
3889 struct ctdb_public_ip_list *pubip_list;
3890 struct ctdb_addr_info addr_info;
3891 unsigned int mask;
3892 int ret, i, retries = 0;
3894 if (argc != 2) {
3895 usage("addip");
3898 if (! parse_ip_mask(argv[0], argv[1], &addr, &mask)) {
3899 fprintf(stderr, "Invalid IP/Mask %s\n", argv[0]);
3900 return 1;
3903 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
3904 ctdb->cmd_pnn, TIMEOUT(),
3905 false, &pubip_list);
3906 if (ret != 0) {
3907 fprintf(stderr, "Failed to get Public IPs from node %u\n",
3908 ctdb->cmd_pnn);
3909 return 1;
3912 for (i=0; i<pubip_list->num; i++) {
3913 if (ctdb_same_ip(&addr, &pubip_list->ip[i].addr)) {
3914 fprintf(stderr, "Node already knows about IP %s\n",
3915 ctdb_sock_addr_to_string(mem_ctx, &addr));
3916 return 0;
3920 addr_info.addr = addr;
3921 addr_info.mask = mask;
3922 addr_info.iface = argv[1];
3924 while (retries < 5) {
3925 ret = ctdb_ctrl_add_public_ip(mem_ctx, ctdb->ev, ctdb->client,
3926 ctdb->cmd_pnn, TIMEOUT(),
3927 &addr_info);
3928 if (ret == 0) {
3929 break;
3932 sleep(3);
3933 retries++;
3936 if (ret != 0) {
3937 fprintf(stderr, "Failed to add public IP to node %u."
3938 " Giving up\n", ctdb->cmd_pnn);
3939 return ret;
3942 ret = rebalancenode(mem_ctx, ctdb, ctdb->cmd_pnn);
3943 if (ret != 0) {
3944 return ret;
3947 return 0;
3950 static int control_delip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3951 int argc, const char **argv)
3953 ctdb_sock_addr addr;
3954 struct ctdb_public_ip_list *pubip_list;
3955 struct ctdb_addr_info addr_info;
3956 int ret, i;
3958 if (argc != 1) {
3959 usage("delip");
3962 if (! parse_ip(argv[0], NULL, 0, &addr)) {
3963 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3964 return 1;
3967 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
3968 ctdb->cmd_pnn, TIMEOUT(),
3969 false, &pubip_list);
3970 if (ret != 0) {
3971 fprintf(stderr, "Failed to get Public IPs from node %u\n",
3972 ctdb->cmd_pnn);
3973 return 1;
3976 for (i=0; i<pubip_list->num; i++) {
3977 if (ctdb_same_ip(&addr, &pubip_list->ip[i].addr)) {
3978 break;
3982 if (i == pubip_list->num) {
3983 fprintf(stderr, "Node does not know about IP address %s\n",
3984 ctdb_sock_addr_to_string(mem_ctx, &addr));
3985 return 0;
3988 addr_info.addr = addr;
3989 addr_info.mask = 0;
3990 addr_info.iface = NULL;
3992 ret = ctdb_ctrl_del_public_ip(mem_ctx, ctdb->ev, ctdb->client,
3993 ctdb->cmd_pnn, TIMEOUT(), &addr_info);
3994 if (ret != 0) {
3995 fprintf(stderr, "Failed to delete public IP from node %u\n",
3996 ctdb->cmd_pnn);
3997 return ret;
4000 return 0;
4003 #define DB_VERSION 3
4004 #define MAX_DB_NAME 64
4005 #define MAX_REC_BUFFER_SIZE (100*1000)
4007 struct db_header {
4008 unsigned long version;
4009 time_t timestamp;
4010 unsigned long flags;
4011 unsigned long nbuf;
4012 unsigned long nrec;
4013 char name[MAX_DB_NAME];
4016 struct backup_state {
4017 TALLOC_CTX *mem_ctx;
4018 struct ctdb_rec_buffer *recbuf;
4019 uint32_t db_id;
4020 int fd;
4021 unsigned int nbuf, nrec;
4024 static int backup_handler(uint32_t reqid, struct ctdb_ltdb_header *header,
4025 TDB_DATA key, TDB_DATA data, void *private_data)
4027 struct backup_state *state = (struct backup_state *)private_data;
4028 size_t len;
4029 int ret;
4031 if (state->recbuf == NULL) {
4032 state->recbuf = ctdb_rec_buffer_init(state->mem_ctx,
4033 state->db_id);
4034 if (state->recbuf == NULL) {
4035 return ENOMEM;
4039 ret = ctdb_rec_buffer_add(state->recbuf, state->recbuf, reqid,
4040 header, key, data);
4041 if (ret != 0) {
4042 return ret;
4045 len = ctdb_rec_buffer_len(state->recbuf);
4046 if (len < MAX_REC_BUFFER_SIZE) {
4047 return 0;
4050 ret = ctdb_rec_buffer_write(state->recbuf, state->fd);
4051 if (ret != 0) {
4052 fprintf(stderr, "Failed to write records to backup file\n");
4053 return ret;
4056 state->nbuf += 1;
4057 state->nrec += state->recbuf->count;
4058 TALLOC_FREE(state->recbuf);
4060 return 0;
4063 static int control_backupdb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4064 int argc, const char **argv)
4066 const char *db_name;
4067 struct ctdb_db_context *db;
4068 uint32_t db_id;
4069 uint8_t db_flags;
4070 struct backup_state state;
4071 struct db_header db_hdr;
4072 int fd, ret;
4074 if (argc != 2) {
4075 usage("backupdb");
4078 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
4079 return 1;
4082 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4083 db_flags, &db);
4084 if (ret != 0) {
4085 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4086 return ret;
4089 fd = open(argv[1], O_RDWR|O_CREAT, 0600);
4090 if (fd == -1) {
4091 ret = errno;
4092 fprintf(stderr, "Failed to open file %s for writing\n",
4093 argv[1]);
4094 return ret;
4097 /* Write empty header first */
4098 ZERO_STRUCT(db_hdr);
4099 ret = write(fd, &db_hdr, sizeof(struct db_header));
4100 if (ret == -1) {
4101 ret = errno;
4102 close(fd);
4103 fprintf(stderr, "Failed to write header to file %s\n", argv[1]);
4104 return ret;
4107 state.mem_ctx = mem_ctx;
4108 state.recbuf = NULL;
4109 state.fd = fd;
4110 state.nbuf = 0;
4111 state.nrec = 0;
4113 ret = ctdb_db_traverse_local(db, true, false, backup_handler, &state);
4114 if (ret != 0) {
4115 fprintf(stderr, "Failed to collect records from DB %s\n",
4116 db_name);
4117 close(fd);
4118 return ret;
4121 if (state.recbuf != NULL) {
4122 ret = ctdb_rec_buffer_write(state.recbuf, state.fd);
4123 if (ret != 0) {
4124 fprintf(stderr,
4125 "Failed to write records to backup file\n");
4126 close(fd);
4127 return ret;
4130 state.nbuf += 1;
4131 state.nrec += state.recbuf->count;
4132 TALLOC_FREE(state.recbuf);
4135 db_hdr.version = DB_VERSION;
4136 db_hdr.timestamp = time(NULL);
4137 db_hdr.flags = db_flags;
4138 db_hdr.nbuf = state.nbuf;
4139 db_hdr.nrec = state.nrec;
4140 strncpy(db_hdr.name, db_name, MAX_DB_NAME-1);
4142 lseek(fd, 0, SEEK_SET);
4143 ret = write(fd, &db_hdr, sizeof(struct db_header));
4144 if (ret == -1) {
4145 ret = errno;
4146 close(fd);
4147 fprintf(stderr, "Failed to write header to file %s\n", argv[1]);
4148 return ret;
4151 close(fd);
4152 printf("Database backed up to %s\n", argv[1]);
4153 return 0;
4156 static int control_restoredb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4157 int argc, const char **argv)
4159 const char *db_name = NULL;
4160 struct ctdb_db_context *db;
4161 struct db_header db_hdr;
4162 struct ctdb_node_map *nodemap;
4163 struct ctdb_req_control request;
4164 struct ctdb_reply_control **reply;
4165 struct ctdb_transdb wipedb;
4166 struct ctdb_pulldb_ext pulldb;
4167 struct ctdb_rec_buffer *recbuf;
4168 uint32_t generation;
4169 uint32_t *pnn_list;
4170 char timebuf[128];
4171 int fd, i;
4172 int count, ret;
4174 if (argc < 1 || argc > 2) {
4175 usage("restoredb");
4178 fd = open(argv[0], O_RDONLY, 0600);
4179 if (fd == -1) {
4180 ret = errno;
4181 fprintf(stderr, "Failed to open file %s for reading\n",
4182 argv[0]);
4183 return ret;
4186 if (argc == 2) {
4187 db_name = argv[1];
4190 ret = read(fd, &db_hdr, sizeof(struct db_header));
4191 if (ret == -1) {
4192 ret = errno;
4193 close(fd);
4194 fprintf(stderr, "Failed to read db header from file %s\n",
4195 argv[0]);
4196 return ret;
4198 db_hdr.name[sizeof(db_hdr.name)-1] = '\0';
4200 if (db_hdr.version != DB_VERSION) {
4201 fprintf(stderr,
4202 "Wrong version of backup file, expected %u, got %lu\n",
4203 DB_VERSION, db_hdr.version);
4204 close(fd);
4205 return EINVAL;
4208 if (db_name == NULL) {
4209 db_name = db_hdr.name;
4212 strftime(timebuf, sizeof(timebuf)-1, "%Y/%m/%d %H:%M:%S",
4213 localtime(&db_hdr.timestamp));
4214 printf("Restoring database %s from backup @ %s\n", db_name, timebuf);
4216 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4217 db_hdr.flags, &db);
4218 if (ret != 0) {
4219 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4220 close(fd);
4221 return ret;
4224 nodemap = get_nodemap(ctdb, false);
4225 if (nodemap == NULL) {
4226 fprintf(stderr, "Failed to get nodemap\n");
4227 close(fd);
4228 return ENOMEM;
4231 ret = get_generation(mem_ctx, ctdb, &generation);
4232 if (ret != 0) {
4233 fprintf(stderr, "Failed to get current generation\n");
4234 close(fd);
4235 return ret;
4238 count = list_of_active_nodes(nodemap, CTDB_UNKNOWN_PNN, mem_ctx,
4239 &pnn_list);
4240 if (count <= 0) {
4241 close(fd);
4242 return ENOMEM;
4245 wipedb.db_id = ctdb_db_id(db);
4246 wipedb.tid = generation;
4248 ctdb_req_control_db_freeze(&request, wipedb.db_id);
4249 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4250 ctdb->client, pnn_list, count,
4251 TIMEOUT(), &request, NULL, NULL);
4252 if (ret != 0) {
4253 goto failed;
4257 ctdb_req_control_db_transaction_start(&request, &wipedb);
4258 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4259 pnn_list, count, TIMEOUT(),
4260 &request, NULL, NULL);
4261 if (ret != 0) {
4262 goto failed;
4265 ctdb_req_control_wipe_database(&request, &wipedb);
4266 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4267 pnn_list, count, TIMEOUT(),
4268 &request, NULL, NULL);
4269 if (ret != 0) {
4270 goto failed;
4273 pulldb.db_id = ctdb_db_id(db);
4274 pulldb.lmaster = 0;
4275 pulldb.srvid = SRVID_CTDB_PUSHDB;
4277 ctdb_req_control_db_push_start(&request, &pulldb);
4278 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4279 pnn_list, count, TIMEOUT(),
4280 &request, NULL, NULL);
4281 if (ret != 0) {
4282 goto failed;
4285 for (i=0; i<db_hdr.nbuf; i++) {
4286 struct ctdb_req_message message;
4287 TDB_DATA data;
4289 ret = ctdb_rec_buffer_read(fd, mem_ctx, &recbuf);
4290 if (ret != 0) {
4291 goto failed;
4294 data.dsize = ctdb_rec_buffer_len(recbuf);
4295 data.dptr = talloc_size(mem_ctx, data.dsize);
4296 if (data.dptr == NULL) {
4297 goto failed;
4300 ctdb_rec_buffer_push(recbuf, data.dptr);
4302 message.srvid = pulldb.srvid;
4303 message.data.data = data;
4305 ret = ctdb_client_message_multi(mem_ctx, ctdb->ev,
4306 ctdb->client,
4307 pnn_list, count,
4308 &message, NULL);
4309 if (ret != 0) {
4310 goto failed;
4313 talloc_free(recbuf);
4314 talloc_free(data.dptr);
4317 ctdb_req_control_db_push_confirm(&request, pulldb.db_id);
4318 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4319 pnn_list, count, TIMEOUT(),
4320 &request, NULL, &reply);
4321 if (ret != 0) {
4322 goto failed;
4325 for (i=0; i<count; i++) {
4326 uint32_t num_records;
4328 ret = ctdb_reply_control_db_push_confirm(reply[i],
4329 &num_records);
4330 if (ret != 0) {
4331 fprintf(stderr, "Invalid response from node %u\n",
4332 pnn_list[i]);
4333 goto failed;
4336 if (num_records != db_hdr.nrec) {
4337 fprintf(stderr, "Node %u received %u of %lu records\n",
4338 pnn_list[i], num_records, db_hdr.nrec);
4339 goto failed;
4343 ctdb_req_control_db_set_healthy(&request, wipedb.db_id);
4344 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4345 pnn_list, count, TIMEOUT(),
4346 &request, NULL, NULL);
4347 if (ret != 0) {
4348 goto failed;
4351 ctdb_req_control_db_transaction_commit(&request, &wipedb);
4352 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4353 pnn_list, count, TIMEOUT(),
4354 &request, NULL, NULL);
4355 if (ret != 0) {
4356 goto failed;
4359 ctdb_req_control_db_thaw(&request, wipedb.db_id);
4360 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4361 ctdb->client, pnn_list, count,
4362 TIMEOUT(), &request, NULL, NULL);
4363 if (ret != 0) {
4364 goto failed;
4367 printf("Database %s restored\n", db_name);
4368 close(fd);
4369 return 0;
4372 failed:
4373 close(fd);
4374 ctdb_ctrl_set_recmode(mem_ctx, ctdb->ev, ctdb->client,
4375 ctdb->pnn, TIMEOUT(), CTDB_RECOVERY_ACTIVE);
4376 return ret;
4379 struct dumpdbbackup_state {
4380 ctdb_rec_parser_func_t parser;
4381 struct dump_record_state sub_state;
4384 static int dumpdbbackup_handler(uint32_t reqid,
4385 struct ctdb_ltdb_header *header,
4386 TDB_DATA key, TDB_DATA data,
4387 void *private_data)
4389 struct dumpdbbackup_state *state =
4390 (struct dumpdbbackup_state *)private_data;
4391 struct ctdb_ltdb_header hdr;
4392 int ret;
4394 ret = ctdb_ltdb_header_extract(&data, &hdr);
4395 if (ret != 0) {
4396 return ret;
4399 return state->parser(reqid, &hdr, key, data, &state->sub_state);
4402 static int control_dumpdbbackup(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4403 int argc, const char **argv)
4405 struct db_header db_hdr;
4406 char timebuf[128];
4407 struct dumpdbbackup_state state;
4408 int fd, ret, i;
4410 if (argc != 1) {
4411 usage("dumpbackup");
4414 fd = open(argv[0], O_RDONLY, 0600);
4415 if (fd == -1) {
4416 ret = errno;
4417 fprintf(stderr, "Failed to open file %s for reading\n",
4418 argv[0]);
4419 return ret;
4422 ret = read(fd, &db_hdr, sizeof(struct db_header));
4423 if (ret == -1) {
4424 ret = errno;
4425 close(fd);
4426 fprintf(stderr, "Failed to read db header from file %s\n",
4427 argv[0]);
4428 return ret;
4430 db_hdr.name[sizeof(db_hdr.name)-1] = '\0';
4432 if (db_hdr.version != DB_VERSION) {
4433 fprintf(stderr,
4434 "Wrong version of backup file, expected %u, got %lu\n",
4435 DB_VERSION, db_hdr.version);
4436 close(fd);
4437 return EINVAL;
4440 strftime(timebuf, sizeof(timebuf)-1, "%Y/%m/%d %H:%M:%S",
4441 localtime(&db_hdr.timestamp));
4442 printf("Dumping database %s from backup @ %s\n",
4443 db_hdr.name, timebuf);
4445 state.parser = dump_record;
4446 state.sub_state.count = 0;
4448 for (i=0; i<db_hdr.nbuf; i++) {
4449 struct ctdb_rec_buffer *recbuf;
4451 ret = ctdb_rec_buffer_read(fd, mem_ctx, &recbuf);
4452 if (ret != 0) {
4453 fprintf(stderr, "Failed to read records\n");
4454 close(fd);
4455 return ret;
4458 ret = ctdb_rec_buffer_traverse(recbuf, dumpdbbackup_handler,
4459 &state);
4460 if (ret != 0) {
4461 fprintf(stderr, "Failed to dump records\n");
4462 close(fd);
4463 return ret;
4467 close(fd);
4468 printf("Dumped %u record(s)\n", state.sub_state.count);
4469 return 0;
4472 static int control_wipedb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4473 int argc, const char **argv)
4475 const char *db_name;
4476 struct ctdb_db_context *db;
4477 uint32_t db_id;
4478 uint8_t db_flags;
4479 struct ctdb_node_map *nodemap;
4480 struct ctdb_req_control request;
4481 struct ctdb_transdb wipedb;
4482 uint32_t generation;
4483 uint32_t *pnn_list;
4484 int count, ret;
4486 if (argc != 1) {
4487 usage("wipedb");
4490 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
4491 return 1;
4494 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4495 db_flags, &db);
4496 if (ret != 0) {
4497 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4498 return ret;
4501 nodemap = get_nodemap(ctdb, false);
4502 if (nodemap == NULL) {
4503 fprintf(stderr, "Failed to get nodemap\n");
4504 return ENOMEM;
4507 ret = get_generation(mem_ctx, ctdb, &generation);
4508 if (ret != 0) {
4509 fprintf(stderr, "Failed to get current generation\n");
4510 return ret;
4513 count = list_of_active_nodes(nodemap, CTDB_UNKNOWN_PNN, mem_ctx,
4514 &pnn_list);
4515 if (count <= 0) {
4516 return ENOMEM;
4519 ctdb_req_control_db_freeze(&request, db_id);
4520 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4521 ctdb->client, pnn_list, count,
4522 TIMEOUT(), &request, NULL, NULL);
4523 if (ret != 0) {
4524 goto failed;
4527 wipedb.db_id = db_id;
4528 wipedb.tid = generation;
4530 ctdb_req_control_db_transaction_start(&request, &wipedb);
4531 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4532 pnn_list, count, TIMEOUT(),
4533 &request, NULL, NULL);
4534 if (ret != 0) {
4535 goto failed;
4538 ctdb_req_control_wipe_database(&request, &wipedb);
4539 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4540 pnn_list, count, TIMEOUT(),
4541 &request, NULL, NULL);
4542 if (ret != 0) {
4543 goto failed;
4546 ctdb_req_control_db_set_healthy(&request, db_id);
4547 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4548 pnn_list, count, TIMEOUT(),
4549 &request, NULL, NULL);
4550 if (ret != 0) {
4551 goto failed;
4554 ctdb_req_control_db_transaction_commit(&request, &wipedb);
4555 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4556 pnn_list, count, TIMEOUT(),
4557 &request, NULL, NULL);
4558 if (ret != 0) {
4559 goto failed;
4562 ctdb_req_control_db_thaw(&request, db_id);
4563 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4564 ctdb->client, pnn_list, count,
4565 TIMEOUT(), &request, NULL, NULL);
4566 if (ret != 0) {
4567 goto failed;
4570 printf("Database %s wiped\n", db_name);
4571 return 0;
4574 failed:
4575 ctdb_ctrl_set_recmode(mem_ctx, ctdb->ev, ctdb->client,
4576 ctdb->pnn, TIMEOUT(), CTDB_RECOVERY_ACTIVE);
4577 return ret;
4580 static int control_recmaster(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4581 int argc, const char **argv)
4583 uint32_t recmaster;
4584 int ret;
4586 ret = ctdb_ctrl_get_recmaster(mem_ctx, ctdb->ev, ctdb->client,
4587 ctdb->cmd_pnn, TIMEOUT(), &recmaster);
4588 if (ret != 0) {
4589 return ret;
4592 printf("%u\n", recmaster);
4593 return 0;
4596 static int control_event(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4597 int argc, const char **argv)
4599 char *t, *event_helper = NULL;
4600 char *eventd_socket = NULL;
4601 const char **new_argv;
4602 int i;
4604 t = getenv("CTDB_EVENT_HELPER");
4605 if (t != NULL) {
4606 event_helper = talloc_strdup(mem_ctx, t);
4607 } else {
4608 event_helper = talloc_asprintf(mem_ctx, "%s/ctdb_event",
4609 CTDB_HELPER_BINDIR);
4612 if (event_helper == NULL) {
4613 fprintf(stderr, "Unable to set event daemon helper\n");
4614 return 1;
4617 t = getenv("CTDB_SOCKET");
4618 if (t != NULL) {
4619 eventd_socket = talloc_asprintf(mem_ctx, "%s/eventd.sock",
4620 dirname(t));
4621 } else {
4622 eventd_socket = talloc_asprintf(mem_ctx, "%s/eventd.sock",
4623 CTDB_RUNDIR);
4626 if (eventd_socket == NULL) {
4627 fprintf(stderr, "Unable to set event daemon socket\n");
4628 return 1;
4631 new_argv = talloc_array(mem_ctx, const char *, argc + 1);
4632 if (new_argv == NULL) {
4633 fprintf(stderr, "Memory allocation error\n");
4634 return 1;
4637 new_argv[0] = eventd_socket;
4638 for (i=0; i<argc; i++) {
4639 new_argv[i+1] = argv[i];
4642 return run_helper(mem_ctx, "event daemon helper", event_helper,
4643 argc+1, new_argv);
4646 static int control_scriptstatus(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4647 int argc, const char **argv)
4649 const char *new_argv[3];
4651 if (argc > 1) {
4652 usage("scriptstatus");
4655 new_argv[0] = "status";
4656 new_argv[1] = (argc == 0) ? "monitor" : argv[0];
4657 new_argv[2] = NULL;
4659 (void) control_event(mem_ctx, ctdb, 2, new_argv);
4660 return 0;
4663 static int control_natgw(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4664 int argc, const char **argv)
4666 char *t, *natgw_helper = NULL;
4668 if (argc != 1) {
4669 usage("natgw");
4672 t = getenv("CTDB_NATGW_HELPER");
4673 if (t != NULL) {
4674 natgw_helper = talloc_strdup(mem_ctx, t);
4675 } else {
4676 natgw_helper = talloc_asprintf(mem_ctx, "%s/ctdb_natgw",
4677 CTDB_HELPER_BINDIR);
4680 if (natgw_helper == NULL) {
4681 fprintf(stderr, "Unable to set NAT gateway helper\n");
4682 return 1;
4685 return run_helper(mem_ctx, "NAT gateway helper", natgw_helper,
4686 argc, argv);
4689 static int control_natgwlist(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4690 int argc, const char **argv)
4692 char *t, *natgw_helper = NULL;
4693 const char *cmd_argv[] = { "natgwlist", NULL };
4695 if (argc != 0) {
4696 usage("natgwlist");
4699 t = getenv("CTDB_NATGW_HELPER");
4700 if (t != NULL) {
4701 natgw_helper = talloc_strdup(mem_ctx, t);
4702 } else {
4703 natgw_helper = talloc_asprintf(mem_ctx, "%s/ctdb_natgw",
4704 CTDB_HELPER_BINDIR);
4707 if (natgw_helper == NULL) {
4708 fprintf(stderr, "Unable to set NAT gateway helper\n");
4709 return 1;
4712 return run_helper(mem_ctx, "NAT gateway helper", natgw_helper,
4713 1, cmd_argv);
4717 * Find the PNN of the current node
4718 * discover the pnn by loading the nodes file and try to bind
4719 * to all addresses one at a time until the ip address is found.
4721 static bool find_node_xpnn(TALLOC_CTX *mem_ctx, uint32_t *pnn)
4723 struct ctdb_node_map *nodemap;
4724 int i;
4726 nodemap = read_nodes_file(mem_ctx, CTDB_UNKNOWN_PNN);
4727 if (nodemap == NULL) {
4728 return false;
4731 for (i=0; i<nodemap->num; i++) {
4732 if (nodemap->node[i].flags & NODE_FLAGS_DELETED) {
4733 continue;
4735 if (ctdb_sys_have_ip(&nodemap->node[i].addr)) {
4736 if (pnn != NULL) {
4737 *pnn = nodemap->node[i].pnn;
4739 talloc_free(nodemap);
4740 return true;
4744 fprintf(stderr, "Failed to detect PNN of the current node.\n");
4745 talloc_free(nodemap);
4746 return false;
4749 static int control_getreclock(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4750 int argc, const char **argv)
4752 const char *reclock;
4753 int ret;
4755 if (argc != 0) {
4756 usage("getreclock");
4759 ret = ctdb_ctrl_get_reclock_file(mem_ctx, ctdb->ev, ctdb->client,
4760 ctdb->cmd_pnn, TIMEOUT(), &reclock);
4761 if (ret != 0) {
4762 return ret;
4765 if (reclock != NULL) {
4766 printf("%s\n", reclock);
4769 return 0;
4772 static int control_setlmasterrole(TALLOC_CTX *mem_ctx,
4773 struct ctdb_context *ctdb,
4774 int argc, const char **argv)
4776 uint32_t lmasterrole = 0;
4777 int ret;
4779 if (argc != 1) {
4780 usage("setlmasterrole");
4783 if (strcmp(argv[0], "on") == 0) {
4784 lmasterrole = 1;
4785 } else if (strcmp(argv[0], "off") == 0) {
4786 lmasterrole = 0;
4787 } else {
4788 usage("setlmasterrole");
4791 ret = ctdb_ctrl_set_lmasterrole(mem_ctx, ctdb->ev, ctdb->client,
4792 ctdb->cmd_pnn, TIMEOUT(), lmasterrole);
4793 if (ret != 0) {
4794 return ret;
4797 return 0;
4800 static int control_setrecmasterrole(TALLOC_CTX *mem_ctx,
4801 struct ctdb_context *ctdb,
4802 int argc, const char **argv)
4804 uint32_t recmasterrole = 0;
4805 int ret;
4807 if (argc != 1) {
4808 usage("setrecmasterrole");
4811 if (strcmp(argv[0], "on") == 0) {
4812 recmasterrole = 1;
4813 } else if (strcmp(argv[0], "off") == 0) {
4814 recmasterrole = 0;
4815 } else {
4816 usage("setrecmasterrole");
4819 ret = ctdb_ctrl_set_recmasterrole(mem_ctx, ctdb->ev, ctdb->client,
4820 ctdb->cmd_pnn, TIMEOUT(),
4821 recmasterrole);
4822 if (ret != 0) {
4823 return ret;
4826 return 0;
4829 static int control_setdbreadonly(TALLOC_CTX *mem_ctx,
4830 struct ctdb_context *ctdb,
4831 int argc, const char **argv)
4833 uint32_t db_id;
4834 uint8_t db_flags;
4835 int ret;
4837 if (argc != 1) {
4838 usage("setdbreadonly");
4841 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, NULL, &db_flags)) {
4842 return 1;
4845 if (db_flags & CTDB_DB_FLAGS_PERSISTENT) {
4846 fprintf(stderr, "Cannot set READONLY on persistent DB\n");
4847 return 1;
4850 ret = ctdb_ctrl_set_db_readonly(mem_ctx, ctdb->ev, ctdb->client,
4851 ctdb->cmd_pnn, TIMEOUT(), db_id);
4852 if (ret != 0) {
4853 return ret;
4856 return 0;
4859 static int control_setdbsticky(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4860 int argc, const char **argv)
4862 uint32_t db_id;
4863 uint8_t db_flags;
4864 int ret;
4866 if (argc != 1) {
4867 usage("setdbsticky");
4870 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, NULL, &db_flags)) {
4871 return 1;
4874 if (db_flags & CTDB_DB_FLAGS_PERSISTENT) {
4875 fprintf(stderr, "Cannot set STICKY on persistent DB\n");
4876 return 1;
4879 ret = ctdb_ctrl_set_db_sticky(mem_ctx, ctdb->ev, ctdb->client,
4880 ctdb->cmd_pnn, TIMEOUT(), db_id);
4881 if (ret != 0) {
4882 return ret;
4885 return 0;
4888 static int control_pfetch(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4889 int argc, const char **argv)
4891 const char *db_name;
4892 struct ctdb_db_context *db;
4893 struct ctdb_transaction_handle *h;
4894 uint8_t db_flags;
4895 TDB_DATA key, data;
4896 int ret;
4898 if (argc < 2 || argc > 3) {
4899 usage("pfetch");
4902 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
4903 return 1;
4906 if (! (db_flags & CTDB_DB_FLAGS_PERSISTENT)) {
4907 fprintf(stderr, "DB %s is not a persistent database\n",
4908 db_name);
4909 return 1;
4912 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4913 db_flags, &db);
4914 if (ret != 0) {
4915 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4916 return ret;
4919 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
4920 if (ret != 0) {
4921 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
4922 return ret;
4925 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
4926 TIMEOUT(), db, true, &h);
4927 if (ret != 0) {
4928 fprintf(stderr, "Failed to start transaction on db %s\n",
4929 db_name);
4930 return ret;
4933 ret = ctdb_transaction_fetch_record(h, key, mem_ctx, &data);
4934 if (ret != 0) {
4935 fprintf(stderr, "Failed to read record for key %s\n",
4936 argv[1]);
4937 ctdb_transaction_cancel(h);
4938 return ret;
4941 printf("%.*s\n", (int)data.dsize, data.dptr);
4943 ctdb_transaction_cancel(h);
4944 return 0;
4947 static int control_pstore(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4948 int argc, const char **argv)
4950 const char *db_name;
4951 struct ctdb_db_context *db;
4952 struct ctdb_transaction_handle *h;
4953 uint8_t db_flags;
4954 TDB_DATA key, data;
4955 int ret;
4957 if (argc != 3) {
4958 usage("pstore");
4961 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
4962 return 1;
4965 if (! (db_flags & CTDB_DB_FLAGS_PERSISTENT)) {
4966 fprintf(stderr, "DB %s is not a persistent database\n",
4967 db_name);
4968 return 1;
4971 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4972 db_flags, &db);
4973 if (ret != 0) {
4974 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4975 return ret;
4978 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
4979 if (ret != 0) {
4980 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
4981 return ret;
4984 ret = str_to_data(argv[2], strlen(argv[2]), mem_ctx, &data);
4985 if (ret != 0) {
4986 fprintf(stderr, "Failed to parse value %s\n", argv[2]);
4987 return ret;
4990 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
4991 TIMEOUT(), db, false, &h);
4992 if (ret != 0) {
4993 fprintf(stderr, "Failed to start transaction on db %s\n",
4994 db_name);
4995 return ret;
4998 ret = ctdb_transaction_store_record(h, key, data);
4999 if (ret != 0) {
5000 fprintf(stderr, "Failed to store record for key %s\n",
5001 argv[1]);
5002 ctdb_transaction_cancel(h);
5003 return ret;
5006 ret = ctdb_transaction_commit(h);
5007 if (ret != 0) {
5008 fprintf(stderr, "Failed to commit transaction on db %s\n",
5009 db_name);
5010 ctdb_transaction_cancel(h);
5011 return ret;
5014 return 0;
5017 static int control_pdelete(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5018 int argc, const char **argv)
5020 const char *db_name;
5021 struct ctdb_db_context *db;
5022 struct ctdb_transaction_handle *h;
5023 uint8_t db_flags;
5024 TDB_DATA key;
5025 int ret;
5027 if (argc != 2) {
5028 usage("pdelete");
5031 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5032 return 1;
5035 if (! (db_flags & CTDB_DB_FLAGS_PERSISTENT)) {
5036 fprintf(stderr, "DB %s is not a persistent database\n",
5037 db_name);
5038 return 1;
5041 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5042 db_flags, &db);
5043 if (ret != 0) {
5044 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5045 return ret;
5048 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5049 if (ret != 0) {
5050 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5051 return ret;
5054 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
5055 TIMEOUT(), db, false, &h);
5056 if (ret != 0) {
5057 fprintf(stderr, "Failed to start transaction on db %s\n",
5058 db_name);
5059 return ret;
5062 ret = ctdb_transaction_delete_record(h, key);
5063 if (ret != 0) {
5064 fprintf(stderr, "Failed to delete record for key %s\n",
5065 argv[1]);
5066 ctdb_transaction_cancel(h);
5067 return ret;
5070 ret = ctdb_transaction_commit(h);
5071 if (ret != 0) {
5072 fprintf(stderr, "Failed to commit transaction on db %s\n",
5073 db_name);
5074 ctdb_transaction_cancel(h);
5075 return ret;
5078 return 0;
5081 static int ptrans_parse_string(TALLOC_CTX *mem_ctx, const char **ptr, TDB_DATA *data)
5083 const char *t;
5084 size_t n;
5085 int ret;
5087 *data = tdb_null;
5089 /* Skip whitespace */
5090 n = strspn(*ptr, " \t");
5091 t = *ptr + n;
5093 if (t[0] == '"') {
5094 /* Quoted ASCII string - no wide characters! */
5095 t++;
5096 n = strcspn(t, "\"");
5097 if (t[n] == '"') {
5098 if (n > 0) {
5099 ret = str_to_data(t, n, mem_ctx, data);
5100 if (ret != 0) {
5101 return ret;
5104 *ptr = t + n + 1;
5105 } else {
5106 fprintf(stderr, "Unmatched \" in input %s\n", *ptr);
5107 return 1;
5109 } else {
5110 fprintf(stderr, "Unsupported input format in %s\n", *ptr);
5111 return 1;
5114 return 0;
5117 #define MAX_LINE_SIZE 1024
5119 static bool ptrans_get_key_value(TALLOC_CTX *mem_ctx, FILE *file,
5120 TDB_DATA *key, TDB_DATA *value)
5122 char line [MAX_LINE_SIZE]; /* FIXME: make this more flexible? */
5123 const char *ptr;
5124 int ret;
5126 ptr = fgets(line, MAX_LINE_SIZE, file);
5127 if (ptr == NULL) {
5128 return false;
5131 /* Get key */
5132 ret = ptrans_parse_string(mem_ctx, &ptr, key);
5133 if (ret != 0 || ptr == NULL || key->dptr == NULL) {
5134 /* Line Ignored but not EOF */
5135 *key = tdb_null;
5136 return true;
5139 /* Get value */
5140 ret = ptrans_parse_string(mem_ctx, &ptr, value);
5141 if (ret != 0) {
5142 /* Line Ignored but not EOF */
5143 talloc_free(key->dptr);
5144 *key = tdb_null;
5145 return true;
5148 return true;
5151 static int control_ptrans(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5152 int argc, const char **argv)
5154 const char *db_name;
5155 struct ctdb_db_context *db;
5156 struct ctdb_transaction_handle *h;
5157 uint8_t db_flags;
5158 FILE *file;
5159 TDB_DATA key, value;
5160 int ret;
5162 if (argc < 1 || argc > 2) {
5163 usage("ptrans");
5166 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5167 return 1;
5170 if (! (db_flags & CTDB_DB_FLAGS_PERSISTENT)) {
5171 fprintf(stderr, "DB %s is not a persistent database\n",
5172 db_name);
5173 return 1;
5176 if (argc == 2) {
5177 file = fopen(argv[1], "r");
5178 if (file == NULL) {
5179 fprintf(stderr, "Failed to open file %s\n", argv[1]);
5180 return 1;
5182 } else {
5183 file = stdin;
5186 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5187 db_flags, &db);
5188 if (ret != 0) {
5189 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5190 goto done;
5193 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
5194 TIMEOUT(), db, false, &h);
5195 if (ret != 0) {
5196 fprintf(stderr, "Failed to start transaction on db %s\n",
5197 db_name);
5198 goto done;
5201 while (ptrans_get_key_value(mem_ctx, file, &key, &value)) {
5202 if (key.dsize != 0) {
5203 ret = ctdb_transaction_store_record(h, key, value);
5204 if (ret != 0) {
5205 fprintf(stderr, "Failed to store record\n");
5206 ctdb_transaction_cancel(h);
5207 goto done;
5209 talloc_free(key.dptr);
5210 talloc_free(value.dptr);
5214 ret = ctdb_transaction_commit(h);
5215 if (ret != 0) {
5216 fprintf(stderr, "Failed to commit transaction on db %s\n",
5217 db_name);
5218 ctdb_transaction_cancel(h);
5221 done:
5222 if (file != stdin) {
5223 fclose(file);
5225 return ret;
5228 static int control_tfetch(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5229 int argc, const char **argv)
5231 struct tdb_context *tdb;
5232 TDB_DATA key, data;
5233 struct ctdb_ltdb_header header;
5234 int ret;
5236 if (argc < 2 || argc > 3) {
5237 usage("tfetch");
5240 tdb = tdb_open(argv[0], 0, 0, O_RDWR, 0);
5241 if (tdb == NULL) {
5242 fprintf(stderr, "Failed to open TDB file %s\n", argv[0]);
5243 return 1;
5246 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5247 if (ret != 0) {
5248 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5249 tdb_close(tdb);
5250 return ret;
5253 data = tdb_fetch(tdb, key);
5254 if (data.dptr == NULL) {
5255 fprintf(stderr, "No record for key %s\n", argv[1]);
5256 tdb_close(tdb);
5257 return 1;
5260 if (data.dsize < sizeof(struct ctdb_ltdb_header)) {
5261 fprintf(stderr, "Invalid record for key %s\n", argv[1]);
5262 tdb_close(tdb);
5263 return 1;
5266 tdb_close(tdb);
5268 if (argc == 3) {
5269 int fd;
5270 ssize_t nwritten;
5272 fd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0600);
5273 if (fd == -1) {
5274 fprintf(stderr, "Failed to open output file %s\n",
5275 argv[2]);
5276 goto fail;
5279 nwritten = sys_write(fd, data.dptr, data.dsize);
5280 if (nwritten != data.dsize) {
5281 fprintf(stderr, "Failed to write record to file\n");
5282 close(fd);
5283 goto fail;
5286 close(fd);
5289 fail:
5290 ret = ctdb_ltdb_header_extract(&data, &header);
5291 if (ret != 0) {
5292 fprintf(stderr, "Failed to parse header from data\n");
5293 return 1;
5296 dump_ltdb_header(&header);
5297 dump_tdb_data("data", data);
5299 return 0;
5302 static int control_tstore(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5303 int argc, const char **argv)
5305 struct tdb_context *tdb;
5306 TDB_DATA key, data[2], value;
5307 struct ctdb_ltdb_header header;
5308 uint8_t header_buf[sizeof(struct ctdb_ltdb_header)];
5309 int ret;
5311 if (argc < 3 || argc > 5) {
5312 usage("tstore");
5315 tdb = tdb_open(argv[0], 0, 0, O_RDWR, 0);
5316 if (tdb == NULL) {
5317 fprintf(stderr, "Failed to open TDB file %s\n", argv[0]);
5318 return 1;
5321 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5322 if (ret != 0) {
5323 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5324 tdb_close(tdb);
5325 return ret;
5328 ret = str_to_data(argv[2], strlen(argv[2]), mem_ctx, &value);
5329 if (ret != 0) {
5330 fprintf(stderr, "Failed to parse value %s\n", argv[2]);
5331 tdb_close(tdb);
5332 return ret;
5335 ZERO_STRUCT(header);
5337 if (argc > 3) {
5338 header.rsn = (uint64_t)strtoull(argv[3], NULL, 0);
5340 if (argc > 4) {
5341 header.dmaster = (uint32_t)atol(argv[4]);
5343 if (argc > 5) {
5344 header.flags = (uint32_t)atol(argv[5]);
5347 ctdb_ltdb_header_push(&header, header_buf);
5349 data[0].dsize = ctdb_ltdb_header_len(&header);
5350 data[0].dptr = header_buf;
5352 data[1].dsize = value.dsize;
5353 data[1].dptr = value.dptr;
5355 ret = tdb_storev(tdb, key, data, 2, TDB_REPLACE);
5356 if (ret != 0) {
5357 fprintf(stderr, "Failed to write record %s to file %s\n",
5358 argv[1], argv[0]);
5361 tdb_close(tdb);
5363 return ret;
5366 static int control_readkey(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5367 int argc, const char **argv)
5369 const char *db_name;
5370 struct ctdb_db_context *db;
5371 struct ctdb_record_handle *h;
5372 uint8_t db_flags;
5373 TDB_DATA key, data;
5374 bool readonly = false;
5375 int ret;
5377 if (argc < 2 || argc > 3) {
5378 usage("readkey");
5381 if (argc == 3) {
5382 if (strcmp(argv[2], "readonly") == 0) {
5383 readonly = true;
5384 } else {
5385 usage("readkey");
5389 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5390 return 1;
5393 if (db_flags & CTDB_DB_FLAGS_PERSISTENT) {
5394 fprintf(stderr, "DB %s is not a volatile database\n",
5395 db_name);
5396 return 1;
5399 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5400 db_flags, &db);
5401 if (ret != 0) {
5402 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5403 return ret;
5406 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5407 if (ret != 0) {
5408 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5409 return ret;
5412 ret = ctdb_fetch_lock(mem_ctx, ctdb->ev, ctdb->client,
5413 db, key, readonly, &h, NULL, &data);
5414 if (ret != 0) {
5415 fprintf(stderr, "Failed to read record for key %s\n",
5416 argv[1]);
5417 } else {
5418 printf("Data: size:%zu ptr:[%.*s]\n", data.dsize,
5419 (int)data.dsize, data.dptr);
5422 talloc_free(h);
5423 return ret;
5426 static int control_writekey(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5427 int argc, const char **argv)
5429 const char *db_name;
5430 struct ctdb_db_context *db;
5431 struct ctdb_record_handle *h;
5432 uint8_t db_flags;
5433 TDB_DATA key, data;
5434 int ret;
5436 if (argc != 3) {
5437 usage("writekey");
5440 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5441 return 1;
5444 if (db_flags & CTDB_DB_FLAGS_PERSISTENT) {
5445 fprintf(stderr, "DB %s is not a volatile database\n",
5446 db_name);
5447 return 1;
5450 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5451 db_flags, &db);
5452 if (ret != 0) {
5453 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5454 return ret;
5457 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5458 if (ret != 0) {
5459 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5460 return ret;
5463 ret = str_to_data(argv[2], strlen(argv[2]), mem_ctx, &data);
5464 if (ret != 0) {
5465 fprintf(stderr, "Failed to parse value %s\n", argv[2]);
5466 return ret;
5469 ret = ctdb_fetch_lock(mem_ctx, ctdb->ev, ctdb->client,
5470 db, key, false, &h, NULL, NULL);
5471 if (ret != 0) {
5472 fprintf(stderr, "Failed to lock record for key %s\n", argv[0]);
5473 return ret;
5476 ret = ctdb_store_record(h, data);
5477 if (ret != 0) {
5478 fprintf(stderr, "Failed to store record for key %s\n",
5479 argv[1]);
5482 talloc_free(h);
5483 return ret;
5486 static int control_deletekey(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5487 int argc, const char **argv)
5489 const char *db_name;
5490 struct ctdb_db_context *db;
5491 struct ctdb_record_handle *h;
5492 uint8_t db_flags;
5493 TDB_DATA key, data;
5494 int ret;
5496 if (argc != 2) {
5497 usage("deletekey");
5500 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5501 return 1;
5504 if (db_flags & CTDB_DB_FLAGS_PERSISTENT) {
5505 fprintf(stderr, "DB %s is not a volatile database\n",
5506 db_name);
5507 return 1;
5510 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5511 db_flags, &db);
5512 if (ret != 0) {
5513 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5514 return ret;
5517 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5518 if (ret != 0) {
5519 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5520 return ret;
5523 ret = ctdb_fetch_lock(mem_ctx, ctdb->ev, ctdb->client,
5524 db, key, false, &h, NULL, &data);
5525 if (ret != 0) {
5526 fprintf(stderr, "Failed to fetch record for key %s\n",
5527 argv[1]);
5528 return ret;
5531 ret = ctdb_delete_record(h);
5532 if (ret != 0) {
5533 fprintf(stderr, "Failed to delete record for key %s\n",
5534 argv[1]);
5537 talloc_free(h);
5538 return ret;
5541 static int control_checktcpport(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5542 int argc, const char **argv)
5544 struct sockaddr_in sin;
5545 unsigned int port;
5546 int s, v;
5547 int ret;
5549 if (argc != 1) {
5550 usage("chktcpport");
5553 port = atoi(argv[0]);
5555 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
5556 if (s == -1) {
5557 fprintf(stderr, "Failed to open local socket\n");
5558 return errno;
5561 v = fcntl(s, F_GETFL, 0);
5562 if (v == -1 || fcntl(s, F_SETFL, v | O_NONBLOCK)) {
5563 fprintf(stderr, "Unable to set socket non-blocking\n");
5564 close(s);
5565 return errno;
5568 bzero(&sin, sizeof(sin));
5569 sin.sin_family = AF_INET;
5570 sin.sin_port = htons(port);
5571 ret = bind(s, (struct sockaddr *)&sin, sizeof(sin));
5572 close(s);
5573 if (ret == -1) {
5574 fprintf(stderr, "Failed to bind to TCP port %u\n", port);
5575 return errno;
5578 return 0;
5581 static int control_getdbseqnum(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5582 int argc, const char **argv)
5584 uint32_t db_id;
5585 const char *db_name;
5586 uint64_t seqnum;
5587 int ret;
5589 if (argc != 1) {
5590 usage("getdbseqnum");
5593 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, NULL)) {
5594 return 1;
5597 ret = ctdb_ctrl_get_db_seqnum(mem_ctx, ctdb->ev, ctdb->client,
5598 ctdb->cmd_pnn, TIMEOUT(), db_id,
5599 &seqnum);
5600 if (ret != 0) {
5601 fprintf(stderr, "Failed to get sequence number for DB %s\n",
5602 db_name);
5603 return ret;
5606 printf("0x%"PRIx64"\n", seqnum);
5607 return 0;
5610 static int control_nodestatus(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5611 int argc, const char **argv)
5613 const char *nodestring = NULL;
5614 struct ctdb_node_map *nodemap;
5615 int ret, i;
5616 bool print_hdr = false;
5618 if (argc > 1) {
5619 usage("nodestatus");
5622 if (argc == 1) {
5623 nodestring = argv[0];
5624 if (strcmp(nodestring, "all") == 0) {
5625 print_hdr = true;
5629 if (! parse_nodestring(mem_ctx, ctdb, nodestring, &nodemap)) {
5630 return 1;
5633 if (options.machinereadable) {
5634 print_nodemap_machine(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn);
5635 } else {
5636 print_nodemap(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn, print_hdr);
5639 ret = 0;
5640 for (i=0; i<nodemap->num; i++) {
5641 ret |= nodemap->node[i].flags;
5644 return ret;
5647 const struct {
5648 const char *name;
5649 uint32_t offset;
5650 } db_stats_fields[] = {
5651 #define DBSTATISTICS_FIELD(n) { #n, offsetof(struct ctdb_db_statistics, n) }
5652 DBSTATISTICS_FIELD(db_ro_delegations),
5653 DBSTATISTICS_FIELD(db_ro_revokes),
5654 DBSTATISTICS_FIELD(locks.num_calls),
5655 DBSTATISTICS_FIELD(locks.num_current),
5656 DBSTATISTICS_FIELD(locks.num_pending),
5657 DBSTATISTICS_FIELD(locks.num_failed),
5658 DBSTATISTICS_FIELD(db_ro_delegations),
5661 static void print_dbstatistics(const char *db_name,
5662 struct ctdb_db_statistics *s)
5664 int i;
5665 const char *prefix = NULL;
5666 int preflen = 0;
5668 printf("DB Statistics %s\n", db_name);
5670 for (i=0; i<ARRAY_SIZE(db_stats_fields); i++) {
5671 if (strchr(db_stats_fields[i].name, '.') != NULL) {
5672 preflen = strcspn(db_stats_fields[i].name, ".") + 1;
5673 if (! prefix ||
5674 strncmp(prefix, db_stats_fields[i].name, preflen) != 0) {
5675 prefix = db_stats_fields[i].name;
5676 printf(" %*.*s\n", preflen-1, preflen-1,
5677 db_stats_fields[i].name);
5679 } else {
5680 preflen = 0;
5682 printf(" %*s%-22s%*s%10u\n", preflen ? 4 : 0, "",
5683 db_stats_fields[i].name+preflen, preflen ? 0 : 4, "",
5684 *(uint32_t *)(db_stats_fields[i].offset+(uint8_t *)s));
5687 printf(" hop_count_buckets:");
5688 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
5689 printf(" %d", s->hop_count_bucket[i]);
5691 printf("\n");
5693 printf(" lock_buckets:");
5694 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
5695 printf(" %d", s->locks.buckets[i]);
5697 printf("\n");
5699 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
5700 "locks_latency MIN/AVG/MAX",
5701 s->locks.latency.min, LATENCY_AVG(s->locks.latency),
5702 s->locks.latency.max, s->locks.latency.num);
5704 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
5705 "vacuum_latency MIN/AVG/MAX",
5706 s->vacuum.latency.min, LATENCY_AVG(s->vacuum.latency),
5707 s->vacuum.latency.max, s->vacuum.latency.num);
5709 printf(" Num Hot Keys: %d\n", s->num_hot_keys);
5710 for (i=0; i<s->num_hot_keys; i++) {
5711 int j;
5712 printf(" Count:%d Key:", s->hot_keys[i].count);
5713 for (j=0; j<s->hot_keys[i].key.dsize; j++) {
5714 printf("%02x", s->hot_keys[i].key.dptr[j] & 0xff);
5716 printf("\n");
5720 static int control_dbstatistics(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5721 int argc, const char **argv)
5723 uint32_t db_id;
5724 const char *db_name;
5725 struct ctdb_db_statistics *dbstats;
5726 int ret;
5728 if (argc != 1) {
5729 usage("dbstatistics");
5732 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, NULL)) {
5733 return 1;
5736 ret = ctdb_ctrl_get_db_statistics(mem_ctx, ctdb->ev, ctdb->client,
5737 ctdb->cmd_pnn, TIMEOUT(), db_id,
5738 &dbstats);
5739 if (ret != 0) {
5740 fprintf(stderr, "Failed to get statistics for DB %s\n",
5741 db_name);
5742 return ret;
5745 print_dbstatistics(db_name, dbstats);
5746 return 0;
5749 struct disable_takeover_runs_state {
5750 uint32_t *pnn_list;
5751 int node_count;
5752 bool *reply;
5753 int status;
5754 bool done;
5757 static void disable_takeover_run_handler(uint64_t srvid, TDB_DATA data,
5758 void *private_data)
5760 struct disable_takeover_runs_state *state =
5761 (struct disable_takeover_runs_state *)private_data;
5762 int ret, i;
5764 if (data.dsize != sizeof(int)) {
5765 /* Ignore packet */
5766 return;
5769 /* ret will be a PNN (i.e. >=0) on success, or negative on error */
5770 ret = *(int *)data.dptr;
5771 if (ret < 0) {
5772 state->status = ret;
5773 state->done = true;
5774 return;
5776 for (i=0; i<state->node_count; i++) {
5777 if (state->pnn_list[i] == ret) {
5778 state->reply[i] = true;
5779 break;
5783 state->done = true;
5784 for (i=0; i<state->node_count; i++) {
5785 if (! state->reply[i]) {
5786 state->done = false;
5787 break;
5792 static int disable_takeover_runs(TALLOC_CTX *mem_ctx,
5793 struct ctdb_context *ctdb, uint32_t timeout,
5794 uint32_t *pnn_list, int count)
5796 struct ctdb_disable_message disable = { 0 };
5797 struct disable_takeover_runs_state state;
5798 int ret, i;
5800 disable.pnn = ctdb->pnn;
5801 disable.srvid = next_srvid(ctdb);
5802 disable.timeout = timeout;
5804 state.pnn_list = pnn_list;
5805 state.node_count = count;
5806 state.done = false;
5807 state.status = 0;
5808 state.reply = talloc_zero_array(mem_ctx, bool, count);
5809 if (state.reply == NULL) {
5810 return ENOMEM;
5813 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
5814 disable.srvid,
5815 disable_takeover_run_handler,
5816 &state);
5817 if (ret != 0) {
5818 return ret;
5821 for (i=0; i<count; i++) {
5822 ret = ctdb_message_disable_takeover_runs(mem_ctx, ctdb->ev,
5823 ctdb->client,
5824 pnn_list[i],
5825 &disable);
5826 if (ret != 0) {
5827 goto fail;
5831 ret = ctdb_client_wait_timeout(ctdb->ev, &state.done, TIMEOUT());
5832 if (ret == ETIME) {
5833 fprintf(stderr, "Timed out waiting to disable takeover runs\n");
5834 } else {
5835 ret = (state.status >= 0 ? 0 : 1);
5838 fail:
5839 ctdb_client_remove_message_handler(ctdb->ev, ctdb->client,
5840 disable.srvid, &state);
5841 return ret;
5844 static int control_reloadips(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5845 int argc, const char **argv)
5847 const char *nodestring = NULL;
5848 struct ctdb_node_map *nodemap, *nodemap2;
5849 struct ctdb_req_control request;
5850 uint32_t *pnn_list, *pnn_list2;
5851 int ret, count, count2;
5853 if (argc > 1) {
5854 usage("reloadips");
5857 if (argc == 1) {
5858 nodestring = argv[0];
5861 nodemap = get_nodemap(ctdb, false);
5862 if (nodemap == NULL) {
5863 return 1;
5866 if (! parse_nodestring(mem_ctx, ctdb, nodestring, &nodemap2)) {
5867 return 1;
5870 count = list_of_connected_nodes(nodemap, CTDB_UNKNOWN_PNN,
5871 mem_ctx, &pnn_list);
5872 if (count <= 0) {
5873 fprintf(stderr, "Memory allocation error\n");
5874 return 1;
5877 count2 = list_of_active_nodes(nodemap2, CTDB_UNKNOWN_PNN,
5878 mem_ctx, &pnn_list2);
5879 if (count2 <= 0) {
5880 fprintf(stderr, "Memory allocation error\n");
5881 return 1;
5884 /* Disable takeover runs on all connected nodes. A reply
5885 * indicating success is needed from each node so all nodes
5886 * will need to be active.
5888 * A check could be added to not allow reloading of IPs when
5889 * there are disconnected nodes. However, this should
5890 * probably be left up to the administrator.
5892 ret = disable_takeover_runs(mem_ctx, ctdb, 2*options.timelimit,
5893 pnn_list, count);
5894 if (ret != 0) {
5895 fprintf(stderr, "Failed to disable takeover runs\n");
5896 return ret;
5899 /* Now tell all the desired nodes to reload their public IPs.
5900 * Keep trying this until it succeeds. This assumes all
5901 * failures are transient, which might not be true...
5903 ctdb_req_control_reload_public_ips(&request);
5904 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
5905 pnn_list2, count2, TIMEOUT(),
5906 &request, NULL, NULL);
5907 if (ret != 0) {
5908 fprintf(stderr, "Failed to reload IPs on some nodes.\n");
5911 /* It isn't strictly necessary to wait until takeover runs are
5912 * re-enabled but doing so can't hurt.
5914 ret = disable_takeover_runs(mem_ctx, ctdb, 0, pnn_list, count);
5915 if (ret != 0) {
5916 fprintf(stderr, "Failed to enable takeover runs\n");
5917 return ret;
5920 return ipreallocate(mem_ctx, ctdb);
5923 static int control_ipiface(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5924 int argc, const char **argv)
5926 ctdb_sock_addr addr;
5927 char *iface;
5929 if (argc != 1) {
5930 usage("ipiface");
5933 if (! parse_ip(argv[0], NULL, 0, &addr)) {
5934 fprintf(stderr, "Failed to Parse IP %s\n", argv[0]);
5935 return 1;
5938 iface = ctdb_sys_find_ifname(&addr);
5939 if (iface == NULL) {
5940 fprintf(stderr, "Failed to find interface for IP %s\n",
5941 argv[0]);
5942 return 1;
5944 free(iface);
5946 return 0;
5950 static const struct ctdb_cmd {
5951 const char *name;
5952 int (*fn)(TALLOC_CTX *, struct ctdb_context *, int, const char **);
5953 bool without_daemon; /* can be run without daemon running ? */
5954 bool remote; /* can be run on remote nodes */
5955 const char *msg;
5956 const char *args;
5957 } ctdb_commands[] = {
5958 { "version", control_version, true, false,
5959 "show version of ctdb", NULL },
5960 { "status", control_status, false, true,
5961 "show node status", NULL },
5962 { "uptime", control_uptime, false, true,
5963 "show node uptime", NULL },
5964 { "ping", control_ping, false, true,
5965 "ping all nodes", NULL },
5966 { "runstate", control_runstate, false, true,
5967 "get/check runstate of a node",
5968 "[setup|first_recovery|startup|running]" },
5969 { "getvar", control_getvar, false, true,
5970 "get a tunable variable", "<name>" },
5971 { "setvar", control_setvar, false, true,
5972 "set a tunable variable", "<name> <value>" },
5973 { "listvars", control_listvars, false, true,
5974 "list tunable variables", NULL },
5975 { "statistics", control_statistics, false, true,
5976 "show ctdb statistics", NULL },
5977 { "statisticsreset", control_statistics_reset, false, true,
5978 "reset ctdb statistics", NULL },
5979 { "stats", control_stats, false, true,
5980 "show rolling statistics", "[count]" },
5981 { "ip", control_ip, false, true,
5982 "show public ips", "[all]" },
5983 { "ipinfo", control_ipinfo, false, true,
5984 "show public ip details", "<ip>" },
5985 { "ifaces", control_ifaces, false, true,
5986 "show interfaces", NULL },
5987 { "setifacelink", control_setifacelink, false, true,
5988 "set interface link status", "<iface> up|down" },
5989 { "process-exists", control_process_exists, false, true,
5990 "check if a process exists on a node", "<pid>" },
5991 { "getdbmap", control_getdbmap, false, true,
5992 "show attached databases", NULL },
5993 { "getdbstatus", control_getdbstatus, false, true,
5994 "show database status", "<dbname|dbid>" },
5995 { "catdb", control_catdb, false, false,
5996 "dump cluster-wide ctdb database", "<dbname|dbid>" },
5997 { "cattdb", control_cattdb, false, false,
5998 "dump local ctdb database", "<dbname|dbid>" },
5999 { "getmonmode", control_getmonmode, false, true,
6000 "show monitoring mode", NULL },
6001 { "getcapabilities", control_getcapabilities, false, true,
6002 "show node capabilities", NULL },
6003 { "pnn", control_pnn, false, false,
6004 "show the pnn of the currnet node", NULL },
6005 { "lvs", control_lvs, false, false,
6006 "show lvs configuration", "master|list|status" },
6007 { "disablemonitor", control_disable_monitor, false, true,
6008 "disable monitoring", NULL },
6009 { "enablemonitor", control_enable_monitor, false, true,
6010 "enable monitoring", NULL },
6011 { "setdebug", control_setdebug, false, true,
6012 "set debug level", "ERROR|WARNING|NOTICE|INFO|DEBUG" },
6013 { "getdebug", control_getdebug, false, true,
6014 "get debug level", NULL },
6015 { "attach", control_attach, false, false,
6016 "attach a database", "<dbname> [persistent]" },
6017 { "detach", control_detach, false, false,
6018 "detach database(s)", "<dbname|dbid> ..." },
6019 { "dumpmemory", control_dumpmemory, false, true,
6020 "dump ctdbd memory map", NULL },
6021 { "rddumpmemory", control_rddumpmemory, false, true,
6022 "dump recoverd memory map", NULL },
6023 { "getpid", control_getpid, false, true,
6024 "get ctdbd process ID", NULL },
6025 { "disable", control_disable, false, true,
6026 "disable a node", NULL },
6027 { "enable", control_enable, false, true,
6028 "enable a node", NULL },
6029 { "stop", control_stop, false, true,
6030 "stop a node", NULL },
6031 { "continue", control_continue, false, true,
6032 "continue a stopped node", NULL },
6033 { "ban", control_ban, false, true,
6034 "ban a node", "<bantime>"},
6035 { "unban", control_unban, false, true,
6036 "unban a node", NULL },
6037 { "shutdown", control_shutdown, false, true,
6038 "shutdown ctdb daemon", NULL },
6039 { "recover", control_recover, false, true,
6040 "force recovery", NULL },
6041 { "sync", control_ipreallocate, false, true,
6042 "run ip reallocation (deprecated)", NULL },
6043 { "ipreallocate", control_ipreallocate, false, true,
6044 "run ip reallocation", NULL },
6045 { "isnotrecmaster", control_isnotrecmaster, false, false,
6046 "check if local node is the recmaster", NULL },
6047 { "gratarp", control_gratarp, false, true,
6048 "send a gratuitous arp", "<ip> <interface>" },
6049 { "tickle", control_tickle, true, false,
6050 "send a tcp tickle ack", "<srcip:port> <dstip:port>" },
6051 { "gettickles", control_gettickles, false, true,
6052 "get the list of tickles", "<ip> [<port>]" },
6053 { "addtickle", control_addtickle, false, true,
6054 "add a tickle", "<ip>:<port> <ip>:<port>" },
6055 { "deltickle", control_deltickle, false, true,
6056 "delete a tickle", "<ip>:<port> <ip>:<port>" },
6057 { "check_srvids", control_check_srvids, false, true,
6058 "check if srvid is registered", "<id> [<id> ...]" },
6059 { "listnodes", control_listnodes, true, true,
6060 "list nodes in the cluster", NULL },
6061 { "reloadnodes", control_reloadnodes, false, false,
6062 "reload the nodes file all nodes", NULL },
6063 { "moveip", control_moveip, false, false,
6064 "move an ip address to another node", "<ip> <node>" },
6065 { "addip", control_addip, false, true,
6066 "add an ip address to a node", "<ip/mask> <iface>" },
6067 { "delip", control_delip, false, true,
6068 "delete an ip address from a node", "<ip>" },
6069 { "backupdb", control_backupdb, false, false,
6070 "backup a database into a file", "<dbname|dbid> <file>" },
6071 { "restoredb", control_restoredb, false, false,
6072 "restore a database from a file", "<file> [dbname]" },
6073 { "dumpdbbackup", control_dumpdbbackup, true, false,
6074 "dump database from a backup file", "<file>" },
6075 { "wipedb", control_wipedb, false, false,
6076 "wipe the contents of a database.", "<dbname|dbid>"},
6077 { "recmaster", control_recmaster, false, true,
6078 "show the pnn for the recovery master", NULL },
6079 { "event", control_event, true, false,
6080 "event and event script commands", NULL },
6081 { "scriptstatus", control_scriptstatus, true, false,
6082 "show event script status",
6083 "[init|setup|startup|monitor|takeip|releaseip|ipreallocated]" },
6084 { "natgw", control_natgw, false, false,
6085 "show natgw configuration", "master|list|status" },
6086 { "natgwlist", control_natgwlist, false, false,
6087 "show the nodes belonging to this natgw configuration", NULL },
6088 { "getreclock", control_getreclock, false, true,
6089 "get recovery lock file", NULL },
6090 { "setlmasterrole", control_setlmasterrole, false, true,
6091 "set LMASTER role", "on|off" },
6092 { "setrecmasterrole", control_setrecmasterrole, false, true,
6093 "set RECMASTER role", "on|off"},
6094 { "setdbreadonly", control_setdbreadonly, false, true,
6095 "enable readonly records", "<dbname|dbid>" },
6096 { "setdbsticky", control_setdbsticky, false, true,
6097 "enable sticky records", "<dbname|dbid>"},
6098 { "pfetch", control_pfetch, false, false,
6099 "fetch record from persistent database", "<dbname|dbid> <key> [<file>]" },
6100 { "pstore", control_pstore, false, false,
6101 "write record to persistent database", "<dbname|dbid> <key> <value>" },
6102 { "pdelete", control_pdelete, false, false,
6103 "delete record from persistent database", "<dbname|dbid> <key>" },
6104 { "ptrans", control_ptrans, false, false,
6105 "update a persistent database (from file or stdin)", "<dbname|dbid> [<file>]" },
6106 { "tfetch", control_tfetch, false, true,
6107 "fetch a record", "<tdb-file> <key> [<file>]" },
6108 { "tstore", control_tstore, false, true,
6109 "store a record", "<tdb-file> <key> <data> [<rsn> <dmaster> <flags>]" },
6110 { "readkey", control_readkey, false, false,
6111 "read value of a database key", "<dbname|dbid> <key> [readonly]" },
6112 { "writekey", control_writekey, false, false,
6113 "write value for a database key", "<dbname|dbid> <key> <value>" },
6114 { "deletekey", control_deletekey, false, false,
6115 "delete a database key", "<dbname|dbid> <key>" },
6116 { "checktcpport", control_checktcpport, true, false,
6117 "check if a service is bound to a specific tcp port or not", "<port>" },
6118 { "getdbseqnum", control_getdbseqnum, false, false,
6119 "get database sequence number", "<dbname|dbid>" },
6120 { "nodestatus", control_nodestatus, false, true,
6121 "show and return node status", "[all|<pnn-list>]" },
6122 { "dbstatistics", control_dbstatistics, false, true,
6123 "show database statistics", "<dbname|dbid>" },
6124 { "reloadips", control_reloadips, false, false,
6125 "reload the public addresses file", "[all|<pnn-list>]" },
6126 { "ipiface", control_ipiface, true, false,
6127 "Find the interface an ip address is hosted on", "<ip>" },
6130 static const struct ctdb_cmd *match_command(const char *command)
6132 const struct ctdb_cmd *cmd;
6133 int i;
6135 for (i=0; i<ARRAY_SIZE(ctdb_commands); i++) {
6136 cmd = &ctdb_commands[i];
6137 if (strlen(command) == strlen(cmd->name) &&
6138 strncmp(command, cmd->name, strlen(command)) == 0) {
6139 return cmd;
6143 return NULL;
6148 * Show usage message
6150 static void usage_full(void)
6152 int i;
6154 poptPrintHelp(pc, stdout, 0);
6155 printf("\nCommands:\n");
6156 for (i=0; i<ARRAY_SIZE(ctdb_commands); i++) {
6157 printf(" %-15s %-27s %s\n",
6158 ctdb_commands[i].name,
6159 ctdb_commands[i].args ? ctdb_commands[i].args : "",
6160 ctdb_commands[i].msg);
6164 static void usage(const char *command)
6166 const struct ctdb_cmd *cmd;
6168 if (command == NULL) {
6169 usage_full();
6170 exit(1);
6173 cmd = match_command(command);
6174 if (cmd == NULL) {
6175 usage_full();
6176 } else {
6177 poptPrintUsage(pc, stdout, 0);
6178 printf("\nCommands:\n");
6179 printf(" %-15s %-27s %s\n",
6180 cmd->name, cmd->args ? cmd->args : "", cmd->msg);
6183 exit(1);
6186 struct poptOption cmdline_options[] = {
6187 POPT_AUTOHELP
6188 { "socket", 's', POPT_ARG_STRING, &options.socket, 0,
6189 "CTDB socket path", "filename" },
6190 { "debug", 'd', POPT_ARG_STRING, &options.debuglevelstr, 0,
6191 "debug level"},
6192 { "timelimit", 't', POPT_ARG_INT, &options.timelimit, 0,
6193 "timelimit (in seconds)" },
6194 { "node", 'n', POPT_ARG_INT, &options.pnn, 0,
6195 "node specification - integer" },
6196 { NULL, 'Y', POPT_ARG_NONE, &options.machinereadable, 0,
6197 "enable machine readable output", NULL },
6198 { "separator", 'x', POPT_ARG_STRING, &options.sep, 0,
6199 "specify separator for machine readable output", "CHAR" },
6200 { NULL, 'X', POPT_ARG_NONE, &options.machineparsable, 0,
6201 "enable machine parsable output with separator |", NULL },
6202 { "verbose", 'v', POPT_ARG_NONE, &options.verbose, 0,
6203 "enable verbose output", NULL },
6204 { "maxruntime", 'T', POPT_ARG_INT, &options.maxruntime, 0,
6205 "die if runtime exceeds this limit (in seconds)" },
6206 POPT_TABLEEND
6209 static int process_command(const struct ctdb_cmd *cmd, int argc,
6210 const char **argv)
6212 TALLOC_CTX *tmp_ctx;
6213 struct ctdb_context *ctdb;
6214 int ret;
6215 bool status;
6216 uint64_t srvid_offset;
6218 tmp_ctx = talloc_new(NULL);
6219 if (tmp_ctx == NULL) {
6220 fprintf(stderr, "Memory allocation error\n");
6221 goto fail;
6224 if (cmd->without_daemon) {
6225 if (options.pnn != -1) {
6226 fprintf(stderr,
6227 "Cannot specify node for command %s\n",
6228 cmd->name);
6229 goto fail;
6232 ret = cmd->fn(tmp_ctx, NULL, argc-1, argv+1);
6233 talloc_free(tmp_ctx);
6234 return ret;
6237 ctdb = talloc_zero(tmp_ctx, struct ctdb_context);
6238 if (ctdb == NULL) {
6239 fprintf(stderr, "Memory allocation error\n");
6240 goto fail;
6243 ctdb->ev = tevent_context_init(ctdb);
6244 if (ctdb->ev == NULL) {
6245 fprintf(stderr, "Failed to initialize tevent\n");
6246 goto fail;
6249 ret = ctdb_client_init(ctdb, ctdb->ev, options.socket, &ctdb->client);
6250 if (ret != 0) {
6251 fprintf(stderr, "Failed to connect to CTDB daemon (%s)\n",
6252 options.socket);
6254 if (!find_node_xpnn(ctdb, NULL)) {
6255 fprintf(stderr, "Is this node part of CTDB cluster?\n");
6257 goto fail;
6260 ctdb->pnn = ctdb_client_pnn(ctdb->client);
6261 srvid_offset = getpid() & 0xFFFF;
6262 ctdb->srvid = SRVID_CTDB_TOOL | (srvid_offset << 16);
6264 if (options.pnn != -1) {
6265 status = verify_pnn(ctdb, options.pnn);
6266 if (! status) {
6267 goto fail;
6270 ctdb->cmd_pnn = options.pnn;
6271 } else {
6272 ctdb->cmd_pnn = ctdb->pnn;
6275 if (! cmd->remote && ctdb->pnn != ctdb->cmd_pnn) {
6276 fprintf(stderr, "Node cannot be specified for command %s\n",
6277 cmd->name);
6278 goto fail;
6281 ret = cmd->fn(tmp_ctx, ctdb, argc-1, argv+1);
6282 talloc_free(tmp_ctx);
6283 return ret;
6285 fail:
6286 talloc_free(tmp_ctx);
6287 return 1;
6290 static void signal_handler(int sig)
6292 fprintf(stderr, "Maximum runtime exceeded - exiting\n");
6295 static void alarm_handler(int sig)
6297 /* Kill any child processes */
6298 signal(SIGTERM, signal_handler);
6299 kill(0, SIGTERM);
6301 _exit(1);
6304 int main(int argc, const char *argv[])
6306 int opt;
6307 const char **extra_argv;
6308 int extra_argc;
6309 const struct ctdb_cmd *cmd;
6310 const char *ctdb_socket;
6311 int loglevel;
6312 int ret;
6314 setlinebuf(stdout);
6316 /* Set default options */
6317 options.socket = CTDB_SOCKET;
6318 options.debuglevelstr = NULL;
6319 options.timelimit = 10;
6320 options.sep = "|";
6321 options.maxruntime = 0;
6322 options.pnn = -1;
6324 ctdb_socket = getenv("CTDB_SOCKET");
6325 if (ctdb_socket != NULL) {
6326 options.socket = ctdb_socket;
6329 pc = poptGetContext(argv[0], argc, argv, cmdline_options,
6330 POPT_CONTEXT_KEEP_FIRST);
6331 while ((opt = poptGetNextOpt(pc)) != -1) {
6332 fprintf(stderr, "Invalid option %s: %s\n",
6333 poptBadOption(pc, 0), poptStrerror(opt));
6334 exit(1);
6337 if (options.maxruntime == 0) {
6338 const char *ctdb_timeout;
6340 ctdb_timeout = getenv("CTDB_TIMEOUT");
6341 if (ctdb_timeout != NULL) {
6342 options.maxruntime = strtoul(ctdb_timeout, NULL, 0);
6343 } else {
6344 options.maxruntime = 120;
6347 if (options.maxruntime <= 120) {
6348 /* default timeout is 120 seconds */
6349 options.maxruntime = 120;
6352 if (options.machineparsable) {
6353 options.machinereadable = 1;
6356 /* setup the remaining options for the commands */
6357 extra_argc = 0;
6358 extra_argv = poptGetArgs(pc);
6359 if (extra_argv) {
6360 extra_argv++;
6361 while (extra_argv[extra_argc]) extra_argc++;
6364 if (extra_argc < 1) {
6365 usage(NULL);
6368 cmd = match_command(extra_argv[0]);
6369 if (cmd == NULL) {
6370 fprintf(stderr, "Unknown command '%s'\n", extra_argv[0]);
6371 exit(1);
6374 /* Enable logging */
6375 setup_logging("ctdb", DEBUG_STDERR);
6376 if (debug_level_parse(options.debuglevelstr, &loglevel)) {
6377 DEBUGLEVEL = loglevel;
6378 } else {
6379 DEBUGLEVEL = DEBUG_ERR;
6382 signal(SIGALRM, alarm_handler);
6383 alarm(options.maxruntime);
6385 ret = process_command(cmd, extra_argc, extra_argv);
6386 if (ret == -1) {
6387 ret = 1;
6390 (void)poptFreeContext(pc);
6392 return ret;