selftest: Add test to show that sam.ldb does not do a full scan in startup
[Samba.git] / ctdb / tools / ctdb.c
blobe6c37642b956b0c70a9e6c386e21850ef205ffd9
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 "common/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 "protocol/protocol_util.h"
43 #include "common/system.h"
44 #include "client/client.h"
45 #include "client/client_sync.h"
47 #define TIMEOUT() timeval_current_ofs(options.timelimit, 0)
49 #define SRVID_CTDB_TOOL (CTDB_SRVID_TOOL_RANGE | 0x0001000000000000LL)
50 #define SRVID_CTDB_PUSHDB (CTDB_SRVID_TOOL_RANGE | 0x0002000000000000LL)
52 static struct {
53 const char *debuglevelstr;
54 int timelimit;
55 int pnn;
56 int machinereadable;
57 const char *sep;
58 int machineparsable;
59 int verbose;
60 int maxruntime;
61 int printemptyrecords;
62 int printdatasize;
63 int printlmaster;
64 int printhash;
65 int printrecordflags;
66 } options;
68 static poptContext pc;
70 struct ctdb_context {
71 struct tevent_context *ev;
72 struct ctdb_client_context *client;
73 struct ctdb_node_map *nodemap;
74 uint32_t pnn, cmd_pnn;
75 uint64_t srvid;
78 static void usage(const char *command);
81 * Utility Functions
84 static double timeval_delta(struct timeval *tv2, struct timeval *tv)
86 return (tv2->tv_sec - tv->tv_sec) +
87 (tv2->tv_usec - tv->tv_usec) * 1.0e-6;
90 static struct ctdb_node_and_flags *get_node_by_pnn(
91 struct ctdb_node_map *nodemap,
92 uint32_t pnn)
94 int i;
96 for (i=0; i<nodemap->num; i++) {
97 if (nodemap->node[i].pnn == pnn) {
98 return &nodemap->node[i];
101 return NULL;
104 static const char *pretty_print_flags(TALLOC_CTX *mem_ctx, uint32_t flags)
106 static const struct {
107 uint32_t flag;
108 const char *name;
109 } flag_names[] = {
110 { NODE_FLAGS_DISCONNECTED, "DISCONNECTED" },
111 { NODE_FLAGS_PERMANENTLY_DISABLED, "DISABLED" },
112 { NODE_FLAGS_BANNED, "BANNED" },
113 { NODE_FLAGS_UNHEALTHY, "UNHEALTHY" },
114 { NODE_FLAGS_DELETED, "DELETED" },
115 { NODE_FLAGS_STOPPED, "STOPPED" },
116 { NODE_FLAGS_INACTIVE, "INACTIVE" },
118 char *flags_str = NULL;
119 int i;
121 for (i=0; i<ARRAY_SIZE(flag_names); i++) {
122 if (flags & flag_names[i].flag) {
123 if (flags_str == NULL) {
124 flags_str = talloc_asprintf(mem_ctx,
125 "%s", flag_names[i].name);
126 } else {
127 flags_str = talloc_asprintf_append(flags_str,
128 "|%s", flag_names[i].name);
130 if (flags_str == NULL) {
131 return "OUT-OF-MEMORY";
135 if (flags_str == NULL) {
136 return "OK";
139 return flags_str;
142 static uint64_t next_srvid(struct ctdb_context *ctdb)
144 ctdb->srvid += 1;
145 return ctdb->srvid;
149 * Get consistent nodemap information.
151 * If nodemap is already cached, use that. If not get it.
152 * If the current node is BANNED, then get nodemap from "better" node.
154 static struct ctdb_node_map *get_nodemap(struct ctdb_context *ctdb, bool force)
156 TALLOC_CTX *tmp_ctx;
157 struct ctdb_node_map *nodemap;
158 struct ctdb_node_and_flags *node;
159 uint32_t current_node;
160 int ret;
162 if (force) {
163 TALLOC_FREE(ctdb->nodemap);
166 if (ctdb->nodemap != NULL) {
167 return ctdb->nodemap;
170 tmp_ctx = talloc_new(ctdb);
171 if (tmp_ctx == NULL) {
172 return false;
175 current_node = ctdb->pnn;
176 again:
177 ret = ctdb_ctrl_get_nodemap(tmp_ctx, ctdb->ev, ctdb->client,
178 current_node, TIMEOUT(), &nodemap);
179 if (ret != 0) {
180 fprintf(stderr, "Failed to get nodemap from node %u\n",
181 current_node);
182 goto failed;
185 node = get_node_by_pnn(nodemap, current_node);
186 if (node->flags & NODE_FLAGS_BANNED) {
187 /* Pick next node */
188 do {
189 current_node = (current_node + 1) % nodemap->num;
190 node = get_node_by_pnn(nodemap, current_node);
191 if (! (node->flags &
192 (NODE_FLAGS_DELETED|NODE_FLAGS_DISCONNECTED))) {
193 break;
195 } while (current_node != ctdb->pnn);
197 if (current_node == ctdb->pnn) {
198 /* Tried all nodes in the cluster */
199 fprintf(stderr, "Warning: All nodes are banned.\n");
200 goto failed;
203 goto again;
206 ctdb->nodemap = talloc_steal(ctdb, nodemap);
207 return nodemap;
209 failed:
210 talloc_free(tmp_ctx);
211 return NULL;
214 static bool verify_pnn(struct ctdb_context *ctdb, int pnn)
216 struct ctdb_node_map *nodemap;
217 bool found;
218 int i;
220 if (pnn == -1) {
221 return false;
224 nodemap = get_nodemap(ctdb, false);
225 if (nodemap == NULL) {
226 return false;
229 found = false;
230 for (i=0; i<nodemap->num; i++) {
231 if (nodemap->node[i].pnn == pnn) {
232 found = true;
233 break;
236 if (! found) {
237 fprintf(stderr, "Node %u does not exist\n", pnn);
238 return false;
241 if (nodemap->node[i].flags &
242 (NODE_FLAGS_DISCONNECTED|NODE_FLAGS_DELETED)) {
243 fprintf(stderr, "Node %u has status %s\n", pnn,
244 pretty_print_flags(ctdb, nodemap->node[i].flags));
245 return false;
248 return true;
251 static struct ctdb_node_map *talloc_nodemap(TALLOC_CTX *mem_ctx,
252 struct ctdb_node_map *nodemap)
254 struct ctdb_node_map *nodemap2;
256 nodemap2 = talloc_zero(mem_ctx, struct ctdb_node_map);
257 if (nodemap2 == NULL) {
258 return NULL;
261 nodemap2->node = talloc_array(nodemap2, struct ctdb_node_and_flags,
262 nodemap->num);
263 if (nodemap2->node == NULL) {
264 talloc_free(nodemap2);
265 return NULL;
268 return nodemap2;
272 * Get the number and the list of matching nodes
274 * nodestring := NULL | all | pnn,[pnn,...]
276 * If nodestring is NULL, use the current node.
278 static bool parse_nodestring(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
279 const char *nodestring,
280 struct ctdb_node_map **out)
282 struct ctdb_node_map *nodemap, *nodemap2;
283 struct ctdb_node_and_flags *node;
284 int i;
286 nodemap = get_nodemap(ctdb, false);
287 if (nodemap == NULL) {
288 return false;
291 nodemap2 = talloc_nodemap(mem_ctx, nodemap);
292 if (nodemap2 == NULL) {
293 return false;
296 if (nodestring == NULL) {
297 for (i=0; i<nodemap->num; i++) {
298 if (nodemap->node[i].pnn == ctdb->cmd_pnn) {
299 nodemap2->node[0] = nodemap->node[i];
300 break;
303 nodemap2->num = 1;
305 goto done;
308 if (strcmp(nodestring, "all") == 0) {
309 for (i=0; i<nodemap->num; i++) {
310 nodemap2->node[i] = nodemap->node[i];
312 nodemap2->num = nodemap->num;
314 goto done;
315 } else {
316 char *ns, *tok;
318 ns = talloc_strdup(mem_ctx, nodestring);
319 if (ns == NULL) {
320 return false;
323 tok = strtok(ns, ",");
324 while (tok != NULL) {
325 uint32_t pnn;
326 char *endptr;
328 pnn = (uint32_t)strtoul(tok, &endptr, 0);
329 if (pnn == 0 && tok == endptr) {
330 fprintf(stderr, "Invalid node %s\n", tok);
331 return false;
334 node = get_node_by_pnn(nodemap, pnn);
335 if (node == NULL) {
336 fprintf(stderr, "Node %u does not exist\n",
337 pnn);
338 return false;
341 nodemap2->node[nodemap2->num] = *node;
342 nodemap2->num += 1;
344 tok = strtok(NULL, ",");
348 done:
349 *out = nodemap2;
350 return true;
353 /* Compare IP address */
354 static bool ctdb_same_ip(ctdb_sock_addr *ip1, ctdb_sock_addr *ip2)
356 bool ret = false;
358 if (ip1->sa.sa_family != ip2->sa.sa_family) {
359 return false;
362 switch (ip1->sa.sa_family) {
363 case AF_INET:
364 ret = (memcmp(&ip1->ip.sin_addr, &ip2->ip.sin_addr,
365 sizeof(struct in_addr)) == 0);
366 break;
368 case AF_INET6:
369 ret = (memcmp(&ip1->ip6.sin6_addr, &ip2->ip6.sin6_addr,
370 sizeof(struct in6_addr)) == 0);
371 break;
374 return ret;
377 /* Append a node to a node map with given address and flags */
378 static bool node_map_add(struct ctdb_node_map *nodemap,
379 const char *nstr, uint32_t flags)
381 ctdb_sock_addr addr;
382 uint32_t num;
383 struct ctdb_node_and_flags *n;
384 int ret;
386 ret = ctdb_sock_addr_from_string(nstr, &addr, false);
387 if (ret != 0) {
388 fprintf(stderr, "Invalid IP address %s\n", nstr);
389 return false;
392 num = nodemap->num;
393 nodemap->node = talloc_realloc(nodemap, nodemap->node,
394 struct ctdb_node_and_flags, num+1);
395 if (nodemap->node == NULL) {
396 return false;
399 n = &nodemap->node[num];
400 n->addr = addr;
401 n->pnn = num;
402 n->flags = flags;
404 nodemap->num = num+1;
405 return true;
408 /* Read a nodes file into a node map */
409 static struct ctdb_node_map *ctdb_read_nodes_file(TALLOC_CTX *mem_ctx,
410 const char *nlist)
412 char **lines;
413 int nlines;
414 int i;
415 struct ctdb_node_map *nodemap;
417 nodemap = talloc_zero(mem_ctx, struct ctdb_node_map);
418 if (nodemap == NULL) {
419 return NULL;
422 lines = file_lines_load(nlist, &nlines, 0, mem_ctx);
423 if (lines == NULL) {
424 return NULL;
427 while (nlines > 0 && strcmp(lines[nlines-1], "") == 0) {
428 nlines--;
431 for (i=0; i<nlines; i++) {
432 char *node;
433 uint32_t flags;
434 size_t len;
436 node = lines[i];
437 /* strip leading spaces */
438 while((*node == ' ') || (*node == '\t')) {
439 node++;
442 len = strlen(node);
444 /* strip trailing spaces */
445 while ((len > 1) &&
446 ((node[len-1] == ' ') || (node[len-1] == '\t')))
448 node[len-1] = '\0';
449 len--;
452 if (len == 0) {
453 continue;
455 if (*node == '#') {
456 /* A "deleted" node is a node that is
457 commented out in the nodes file. This is
458 used instead of removing a line, which
459 would cause subsequent nodes to change
460 their PNN. */
461 flags = NODE_FLAGS_DELETED;
462 node = discard_const("0.0.0.0");
463 } else {
464 flags = 0;
466 if (! node_map_add(nodemap, node, flags)) {
467 talloc_free(lines);
468 TALLOC_FREE(nodemap);
469 return NULL;
473 talloc_free(lines);
474 return nodemap;
477 static struct ctdb_node_map *read_nodes_file(TALLOC_CTX *mem_ctx, uint32_t pnn)
479 struct ctdb_node_map *nodemap;
480 const char *nodes_list = NULL;
482 const char *basedir = getenv("CTDB_BASE");
483 if (basedir == NULL) {
484 basedir = CTDB_ETCDIR;
486 nodes_list = talloc_asprintf(mem_ctx, "%s/nodes", basedir);
487 if (nodes_list == NULL) {
488 fprintf(stderr, "Memory allocation error\n");
489 return NULL;
492 nodemap = ctdb_read_nodes_file(mem_ctx, nodes_list);
493 if (nodemap == NULL) {
494 fprintf(stderr, "Failed to read nodes file \"%s\"\n",
495 nodes_list);
496 return NULL;
499 return nodemap;
502 static struct ctdb_dbid *db_find(TALLOC_CTX *mem_ctx,
503 struct ctdb_context *ctdb,
504 struct ctdb_dbid_map *dbmap,
505 const char *db_name)
507 struct ctdb_dbid *db = NULL;
508 const char *name;
509 int ret, i;
511 for (i=0; i<dbmap->num; i++) {
512 ret = ctdb_ctrl_get_dbname(mem_ctx, ctdb->ev, ctdb->client,
513 ctdb->pnn, TIMEOUT(),
514 dbmap->dbs[i].db_id, &name);
515 if (ret != 0) {
516 return false;
519 if (strcmp(db_name, name) == 0) {
520 talloc_free(discard_const(name));
521 db = &dbmap->dbs[i];
522 break;
526 return db;
529 static bool db_exists(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
530 const char *db_arg, uint32_t *db_id,
531 const char **db_name, uint8_t *db_flags)
533 struct ctdb_dbid_map *dbmap;
534 struct ctdb_dbid *db = NULL;
535 uint32_t id = 0;
536 const char *name = NULL;
537 int ret, i;
539 ret = ctdb_ctrl_get_dbmap(mem_ctx, ctdb->ev, ctdb->client,
540 ctdb->pnn, TIMEOUT(), &dbmap);
541 if (ret != 0) {
542 return false;
545 if (strncmp(db_arg, "0x", 2) == 0) {
546 id = strtoul(db_arg, NULL, 0);
547 for (i=0; i<dbmap->num; i++) {
548 if (id == dbmap->dbs[i].db_id) {
549 db = &dbmap->dbs[i];
550 break;
553 } else {
554 name = db_arg;
555 db = db_find(mem_ctx, ctdb, dbmap, name);
558 if (db == NULL) {
559 fprintf(stderr, "No database matching '%s' found\n", db_arg);
560 return false;
563 if (name == NULL) {
564 ret = ctdb_ctrl_get_dbname(mem_ctx, ctdb->ev, ctdb->client,
565 ctdb->pnn, TIMEOUT(), id, &name);
566 if (ret != 0) {
567 return false;
571 if (db_id != NULL) {
572 *db_id = db->db_id;
574 if (db_name != NULL) {
575 *db_name = talloc_strdup(mem_ctx, name);
577 if (db_flags != NULL) {
578 *db_flags = db->flags;
580 return true;
583 static int h2i(char h)
585 if (h >= 'a' && h <= 'f') {
586 return h - 'a' + 10;
588 if (h >= 'A' && h <= 'F') {
589 return h - 'f' + 10;
591 return h - '0';
594 static int hex_to_data(const char *str, size_t len, TALLOC_CTX *mem_ctx,
595 TDB_DATA *out)
597 int i;
598 TDB_DATA data;
600 if (len & 0x01) {
601 fprintf(stderr, "Key (%s) contains odd number of hex digits\n",
602 str);
603 return EINVAL;
606 data.dsize = len / 2;
607 data.dptr = talloc_size(mem_ctx, data.dsize);
608 if (data.dptr == NULL) {
609 return ENOMEM;
612 for (i=0; i<data.dsize; i++) {
613 data.dptr[i] = h2i(str[i*2]) << 4 | h2i(str[i*2+1]);
616 *out = data;
617 return 0;
620 static int str_to_data(const char *str, size_t len, TALLOC_CTX *mem_ctx,
621 TDB_DATA *out)
623 TDB_DATA data;
624 int ret = 0;
626 if (strncmp(str, "0x", 2) == 0) {
627 ret = hex_to_data(str+2, len-2, mem_ctx, &data);
628 } else {
629 data.dptr = talloc_memdup(mem_ctx, str, len);
630 if (data.dptr == NULL) {
631 return ENOMEM;
633 data.dsize = len;
636 *out = data;
637 return ret;
640 static int run_helper(TALLOC_CTX *mem_ctx, const char *command,
641 const char *path, int argc, const char **argv)
643 pid_t pid;
644 int save_errno, status, ret;
645 const char **new_argv;
646 int i;
648 new_argv = talloc_array(mem_ctx, const char *, argc + 2);
649 if (new_argv == NULL) {
650 return ENOMEM;
653 new_argv[0] = path;
654 for (i=0; i<argc; i++) {
655 new_argv[i+1] = argv[i];
657 new_argv[argc+1] = NULL;
659 pid = fork();
660 if (pid < 0) {
661 save_errno = errno;
662 talloc_free(new_argv);
663 fprintf(stderr, "Failed to fork %s (%s) - %s\n",
664 command, path, strerror(save_errno));
665 return save_errno;
668 if (pid == 0) {
669 ret = execv(path, discard_const(new_argv));
670 if (ret == -1) {
671 _exit(64+errno);
673 /* Should not happen */
674 _exit(64+ENOEXEC);
677 talloc_free(new_argv);
679 ret = waitpid(pid, &status, 0);
680 if (ret == -1) {
681 save_errno = errno;
682 fprintf(stderr, "waitpid() failed for %s - %s\n",
683 command, strerror(save_errno));
684 return save_errno;
687 if (WIFEXITED(status)) {
688 int pstatus = WEXITSTATUS(status);
689 if (WIFSIGNALED(status)) {
690 fprintf(stderr, "%s terminated with signal %d\n",
691 command, WTERMSIG(status));
692 ret = EINTR;
693 } else if (pstatus >= 64 && pstatus < 255) {
694 fprintf(stderr, "%s failed with error %d\n",
695 command, pstatus-64);
696 ret = pstatus - 64;
697 } else {
698 ret = pstatus;
700 return ret;
701 } else if (WIFSIGNALED(status)) {
702 fprintf(stderr, "%s terminated with signal %d\n",
703 command, WTERMSIG(status));
704 return EINTR;
707 return 0;
711 * Command Functions
714 static int control_version(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
715 int argc, const char **argv)
717 printf("%s\n", ctdb_version_string);
718 return 0;
721 static bool partially_online(TALLOC_CTX *mem_ctx,
722 struct ctdb_context *ctdb,
723 struct ctdb_node_and_flags *node)
725 struct ctdb_iface_list *iface_list;
726 int ret, i;
727 bool status = false;
729 if (node->flags != 0) {
730 return false;
733 ret = ctdb_ctrl_get_ifaces(mem_ctx, ctdb->ev, ctdb->client,
734 node->pnn, TIMEOUT(), &iface_list);
735 if (ret != 0) {
736 return false;
739 status = false;
740 for (i=0; i < iface_list->num; i++) {
741 if (iface_list->iface[i].link_state == 0) {
742 status = true;
743 break;
747 return status;
750 static void print_nodemap_machine(TALLOC_CTX *mem_ctx,
751 struct ctdb_context *ctdb,
752 struct ctdb_node_map *nodemap,
753 uint32_t mypnn)
755 struct ctdb_node_and_flags *node;
756 int i;
758 printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
759 options.sep,
760 "Node", options.sep,
761 "IP", options.sep,
762 "Disconnected", options.sep,
763 "Banned", options.sep,
764 "Disabled", options.sep,
765 "Unhealthy", options.sep,
766 "Stopped", options.sep,
767 "Inactive", options.sep,
768 "PartiallyOnline", options.sep,
769 "ThisNode", options.sep);
771 for (i=0; i<nodemap->num; i++) {
772 node = &nodemap->node[i];
773 if (node->flags & NODE_FLAGS_DELETED) {
774 continue;
777 printf("%s%u%s%s%s%d%s%d%s%d%s%d%s%d%s%d%s%d%s%c%s\n",
778 options.sep,
779 node->pnn, options.sep,
780 ctdb_sock_addr_to_string(mem_ctx, &node->addr, false),
781 options.sep,
782 !! (node->flags & NODE_FLAGS_DISCONNECTED), options.sep,
783 !! (node->flags & NODE_FLAGS_BANNED), options.sep,
784 !! (node->flags & NODE_FLAGS_PERMANENTLY_DISABLED),
785 options.sep,
786 !! (node->flags & NODE_FLAGS_UNHEALTHY), options.sep,
787 !! (node->flags & NODE_FLAGS_STOPPED), options.sep,
788 !! (node->flags & NODE_FLAGS_INACTIVE), options.sep,
789 partially_online(mem_ctx, ctdb, node), options.sep,
790 (node->pnn == mypnn)?'Y':'N', options.sep);
795 static void print_nodemap(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
796 struct ctdb_node_map *nodemap, uint32_t mypnn,
797 bool print_header)
799 struct ctdb_node_and_flags *node;
800 int num_deleted_nodes = 0;
801 int i;
803 for (i=0; i<nodemap->num; i++) {
804 if (nodemap->node[i].flags & NODE_FLAGS_DELETED) {
805 num_deleted_nodes++;
809 if (print_header) {
810 if (num_deleted_nodes == 0) {
811 printf("Number of nodes:%d\n", nodemap->num);
812 } else {
813 printf("Number of nodes:%d "
814 "(including %d deleted nodes)\n",
815 nodemap->num, num_deleted_nodes);
819 for (i=0; i<nodemap->num; i++) {
820 node = &nodemap->node[i];
821 if (node->flags & NODE_FLAGS_DELETED) {
822 continue;
825 printf("pnn:%u %-16s %s%s\n",
826 node->pnn,
827 ctdb_sock_addr_to_string(mem_ctx, &node->addr, false),
828 partially_online(mem_ctx, ctdb, node) ?
829 "PARTIALLYONLINE" :
830 pretty_print_flags(mem_ctx, node->flags),
831 node->pnn == mypnn ? " (THIS NODE)" : "");
835 static void print_status(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
836 struct ctdb_node_map *nodemap, uint32_t mypnn,
837 struct ctdb_vnn_map *vnnmap, int recmode,
838 uint32_t recmaster)
840 int i;
842 print_nodemap(mem_ctx, ctdb, nodemap, mypnn, true);
844 if (vnnmap->generation == INVALID_GENERATION) {
845 printf("Generation:INVALID\n");
846 } else {
847 printf("Generation:%u\n", vnnmap->generation);
849 printf("Size:%d\n", vnnmap->size);
850 for (i=0; i<vnnmap->size; i++) {
851 printf("hash:%d lmaster:%d\n", i, vnnmap->map[i]);
854 printf("Recovery mode:%s (%d)\n",
855 recmode == CTDB_RECOVERY_NORMAL ? "NORMAL" : "RECOVERY",
856 recmode);
857 printf("Recovery master:%d\n", recmaster);
860 static int control_status(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
861 int argc, const char **argv)
863 struct ctdb_node_map *nodemap;
864 struct ctdb_vnn_map *vnnmap;
865 int recmode;
866 uint32_t recmaster;
867 int ret;
869 if (argc != 0) {
870 usage("status");
873 nodemap = get_nodemap(ctdb, false);
874 if (nodemap == NULL) {
875 return 1;
878 if (options.machinereadable == 1) {
879 print_nodemap_machine(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn);
880 return 0;
883 ret = ctdb_ctrl_getvnnmap(mem_ctx, ctdb->ev, ctdb->client,
884 ctdb->cmd_pnn, TIMEOUT(), &vnnmap);
885 if (ret != 0) {
886 return ret;
889 ret = ctdb_ctrl_get_recmode(mem_ctx, ctdb->ev, ctdb->client,
890 ctdb->cmd_pnn, TIMEOUT(), &recmode);
891 if (ret != 0) {
892 return ret;
895 ret = ctdb_ctrl_get_recmaster(mem_ctx, ctdb->ev, ctdb->client,
896 ctdb->cmd_pnn, TIMEOUT(), &recmaster);
897 if (ret != 0) {
898 return ret;
901 print_status(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn, vnnmap,
902 recmode, recmaster);
903 return 0;
906 static int control_uptime(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
907 int argc, const char **argv)
909 struct ctdb_uptime *uptime;
910 int ret, tmp, days, hours, minutes, seconds;
912 ret = ctdb_ctrl_uptime(mem_ctx, ctdb->ev, ctdb->client,
913 ctdb->cmd_pnn, TIMEOUT(), &uptime);
914 if (ret != 0) {
915 return ret;
918 printf("Current time of node %-4u : %s",
919 ctdb->cmd_pnn, ctime(&uptime->current_time.tv_sec));
921 tmp = uptime->current_time.tv_sec - uptime->ctdbd_start_time.tv_sec;
922 seconds = tmp % 60; tmp /= 60;
923 minutes = tmp % 60; tmp /= 60;
924 hours = tmp % 24; tmp /= 24;
925 days = tmp;
927 printf("Ctdbd start time : (%03d %02d:%02d:%02d) %s",
928 days, hours, minutes, seconds,
929 ctime(&uptime->ctdbd_start_time.tv_sec));
931 tmp = uptime->current_time.tv_sec - uptime->last_recovery_finished.tv_sec;
932 seconds = tmp % 60; tmp /= 60;
933 minutes = tmp % 60; tmp /= 60;
934 hours = tmp % 24; tmp /= 24;
935 days = tmp;
937 printf("Time of last recovery/failover: (%03d %02d:%02d:%02d) %s",
938 days, hours, minutes, seconds,
939 ctime(&uptime->last_recovery_finished.tv_sec));
941 printf("Duration of last recovery/failover: %lf seconds\n",
942 timeval_delta(&uptime->last_recovery_finished,
943 &uptime->last_recovery_started));
945 return 0;
948 static int control_ping(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
949 int argc, const char **argv)
951 struct timeval tv;
952 int ret, num_clients;
954 tv = timeval_current();
955 ret = ctdb_ctrl_ping(mem_ctx, ctdb->ev, ctdb->client,
956 ctdb->cmd_pnn, TIMEOUT(), &num_clients);
957 if (ret != 0) {
958 return ret;
961 printf("response from %u time=%.6f sec (%d clients)\n",
962 ctdb->cmd_pnn, timeval_elapsed(&tv), num_clients);
963 return 0;
966 const char *runstate_to_string(enum ctdb_runstate runstate);
967 enum ctdb_runstate runstate_from_string(const char *runstate_str);
969 static int control_runstate(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
970 int argc, const char **argv)
972 enum ctdb_runstate runstate;
973 bool found;
974 int ret, i;
976 ret = ctdb_ctrl_get_runstate(mem_ctx, ctdb->ev, ctdb->client,
977 ctdb->cmd_pnn, TIMEOUT(), &runstate);
978 if (ret != 0) {
979 return ret;
982 found = true;
983 for (i=0; i<argc; i++) {
984 enum ctdb_runstate t;
986 found = false;
987 t = ctdb_runstate_from_string(argv[i]);
988 if (t == CTDB_RUNSTATE_UNKNOWN) {
989 printf("Invalid run state (%s)\n", argv[i]);
990 return 1;
993 if (t == runstate) {
994 found = true;
995 break;
999 if (! found) {
1000 printf("CTDB not in required run state (got %s)\n",
1001 ctdb_runstate_to_string(runstate));
1002 return 1;
1005 printf("%s\n", ctdb_runstate_to_string(runstate));
1006 return 0;
1009 static int control_getvar(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1010 int argc, const char **argv)
1012 struct ctdb_var_list *tun_var_list;
1013 uint32_t value;
1014 int ret, i;
1015 bool found;
1017 if (argc != 1) {
1018 usage("getvar");
1021 ret = ctdb_ctrl_list_tunables(mem_ctx, ctdb->ev, ctdb->client,
1022 ctdb->cmd_pnn, TIMEOUT(), &tun_var_list);
1023 if (ret != 0) {
1024 fprintf(stderr,
1025 "Failed to get list of variables from node %u\n",
1026 ctdb->cmd_pnn);
1027 return ret;
1030 found = false;
1031 for (i=0; i<tun_var_list->count; i++) {
1032 if (strcasecmp(tun_var_list->var[i], argv[0]) == 0) {
1033 found = true;
1034 break;
1038 if (! found) {
1039 printf("No such tunable %s\n", argv[0]);
1040 return 1;
1043 ret = ctdb_ctrl_get_tunable(mem_ctx, ctdb->ev, ctdb->client,
1044 ctdb->cmd_pnn, TIMEOUT(), argv[0], &value);
1045 if (ret != 0) {
1046 return ret;
1049 printf("%-26s = %u\n", argv[0], value);
1050 return 0;
1053 static int control_setvar(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1054 int argc, const char **argv)
1056 struct ctdb_var_list *tun_var_list;
1057 struct ctdb_tunable tunable;
1058 int ret, i;
1059 bool found;
1061 if (argc != 2) {
1062 usage("setvar");
1065 ret = ctdb_ctrl_list_tunables(mem_ctx, ctdb->ev, ctdb->client,
1066 ctdb->cmd_pnn, TIMEOUT(), &tun_var_list);
1067 if (ret != 0) {
1068 fprintf(stderr,
1069 "Failed to get list of variables from node %u\n",
1070 ctdb->cmd_pnn);
1071 return ret;
1074 found = false;
1075 for (i=0; i<tun_var_list->count; i++) {
1076 if (strcasecmp(tun_var_list->var[i], argv[0]) == 0) {
1077 found = true;
1078 break;
1082 if (! found) {
1083 printf("No such tunable %s\n", argv[0]);
1084 return 1;
1087 tunable.name = argv[0];
1088 tunable.value = strtoul(argv[1], NULL, 0);
1090 ret = ctdb_ctrl_set_tunable(mem_ctx, ctdb->ev, ctdb->client,
1091 ctdb->cmd_pnn, TIMEOUT(), &tunable);
1092 if (ret != 0) {
1093 if (ret == 1) {
1094 fprintf(stderr,
1095 "Setting obsolete tunable variable '%s'\n",
1096 tunable.name);
1097 return 0;
1101 return ret;
1104 static int control_listvars(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1105 int argc, const char **argv)
1107 struct ctdb_var_list *tun_var_list;
1108 int ret, i;
1110 if (argc != 0) {
1111 usage("listvars");
1114 ret = ctdb_ctrl_list_tunables(mem_ctx, ctdb->ev, ctdb->client,
1115 ctdb->cmd_pnn, TIMEOUT(), &tun_var_list);
1116 if (ret != 0) {
1117 return ret;
1120 for (i=0; i<tun_var_list->count; i++) {
1121 control_getvar(mem_ctx, ctdb, 1, &tun_var_list->var[i]);
1124 return 0;
1127 const struct {
1128 const char *name;
1129 uint32_t offset;
1130 } stats_fields[] = {
1131 #define STATISTICS_FIELD(n) { #n, offsetof(struct ctdb_statistics, n) }
1132 STATISTICS_FIELD(num_clients),
1133 STATISTICS_FIELD(frozen),
1134 STATISTICS_FIELD(recovering),
1135 STATISTICS_FIELD(num_recoveries),
1136 STATISTICS_FIELD(client_packets_sent),
1137 STATISTICS_FIELD(client_packets_recv),
1138 STATISTICS_FIELD(node_packets_sent),
1139 STATISTICS_FIELD(node_packets_recv),
1140 STATISTICS_FIELD(keepalive_packets_sent),
1141 STATISTICS_FIELD(keepalive_packets_recv),
1142 STATISTICS_FIELD(node.req_call),
1143 STATISTICS_FIELD(node.reply_call),
1144 STATISTICS_FIELD(node.req_dmaster),
1145 STATISTICS_FIELD(node.reply_dmaster),
1146 STATISTICS_FIELD(node.reply_error),
1147 STATISTICS_FIELD(node.req_message),
1148 STATISTICS_FIELD(node.req_control),
1149 STATISTICS_FIELD(node.reply_control),
1150 STATISTICS_FIELD(node.req_tunnel),
1151 STATISTICS_FIELD(client.req_call),
1152 STATISTICS_FIELD(client.req_message),
1153 STATISTICS_FIELD(client.req_control),
1154 STATISTICS_FIELD(client.req_tunnel),
1155 STATISTICS_FIELD(timeouts.call),
1156 STATISTICS_FIELD(timeouts.control),
1157 STATISTICS_FIELD(timeouts.traverse),
1158 STATISTICS_FIELD(locks.num_calls),
1159 STATISTICS_FIELD(locks.num_current),
1160 STATISTICS_FIELD(locks.num_pending),
1161 STATISTICS_FIELD(locks.num_failed),
1162 STATISTICS_FIELD(total_calls),
1163 STATISTICS_FIELD(pending_calls),
1164 STATISTICS_FIELD(childwrite_calls),
1165 STATISTICS_FIELD(pending_childwrite_calls),
1166 STATISTICS_FIELD(memory_used),
1167 STATISTICS_FIELD(max_hop_count),
1168 STATISTICS_FIELD(total_ro_delegations),
1169 STATISTICS_FIELD(total_ro_revokes),
1172 #define LATENCY_AVG(v) ((v).num ? (v).total / (v).num : 0.0 )
1174 static void print_statistics_machine(struct ctdb_statistics *s,
1175 bool show_header)
1177 int i;
1179 if (show_header) {
1180 printf("CTDB version%s", options.sep);
1181 printf("Current time of statistics%s", options.sep);
1182 printf("Statistics collected since%s", options.sep);
1183 for (i=0; i<ARRAY_SIZE(stats_fields); i++) {
1184 printf("%s%s", stats_fields[i].name, options.sep);
1186 printf("num_reclock_ctdbd_latency%s", options.sep);
1187 printf("min_reclock_ctdbd_latency%s", options.sep);
1188 printf("avg_reclock_ctdbd_latency%s", options.sep);
1189 printf("max_reclock_ctdbd_latency%s", options.sep);
1191 printf("num_reclock_recd_latency%s", options.sep);
1192 printf("min_reclock_recd_latency%s", options.sep);
1193 printf("avg_reclock_recd_latency%s", options.sep);
1194 printf("max_reclock_recd_latency%s", options.sep);
1196 printf("num_call_latency%s", options.sep);
1197 printf("min_call_latency%s", options.sep);
1198 printf("avg_call_latency%s", options.sep);
1199 printf("max_call_latency%s", options.sep);
1201 printf("num_lockwait_latency%s", options.sep);
1202 printf("min_lockwait_latency%s", options.sep);
1203 printf("avg_lockwait_latency%s", options.sep);
1204 printf("max_lockwait_latency%s", options.sep);
1206 printf("num_childwrite_latency%s", options.sep);
1207 printf("min_childwrite_latency%s", options.sep);
1208 printf("avg_childwrite_latency%s", options.sep);
1209 printf("max_childwrite_latency%s", options.sep);
1210 printf("\n");
1213 printf("%u%s", CTDB_PROTOCOL, options.sep);
1214 printf("%u%s", (uint32_t)s->statistics_current_time.tv_sec, options.sep);
1215 printf("%u%s", (uint32_t)s->statistics_start_time.tv_sec, options.sep);
1216 for (i=0;i<ARRAY_SIZE(stats_fields);i++) {
1217 printf("%u%s",
1218 *(uint32_t *)(stats_fields[i].offset+(uint8_t *)s),
1219 options.sep);
1221 printf("%u%s", s->reclock.ctdbd.num, options.sep);
1222 printf("%.6f%s", s->reclock.ctdbd.min, options.sep);
1223 printf("%.6f%s", LATENCY_AVG(s->reclock.ctdbd), options.sep);
1224 printf("%.6f%s", s->reclock.ctdbd.max, options.sep);
1226 printf("%u%s", s->reclock.recd.num, options.sep);
1227 printf("%.6f%s", s->reclock.recd.min, options.sep);
1228 printf("%.6f%s", LATENCY_AVG(s->reclock.recd), options.sep);
1229 printf("%.6f%s", s->reclock.recd.max, options.sep);
1231 printf("%d%s", s->call_latency.num, options.sep);
1232 printf("%.6f%s", s->call_latency.min, options.sep);
1233 printf("%.6f%s", LATENCY_AVG(s->call_latency), options.sep);
1234 printf("%.6f%s", s->call_latency.max, options.sep);
1236 printf("%d%s", s->childwrite_latency.num, options.sep);
1237 printf("%.6f%s", s->childwrite_latency.min, options.sep);
1238 printf("%.6f%s", LATENCY_AVG(s->childwrite_latency), options.sep);
1239 printf("%.6f%s", s->childwrite_latency.max, options.sep);
1240 printf("\n");
1243 static void print_statistics(struct ctdb_statistics *s)
1245 int tmp, days, hours, minutes, seconds;
1246 int i;
1247 const char *prefix = NULL;
1248 int preflen = 0;
1250 tmp = s->statistics_current_time.tv_sec -
1251 s->statistics_start_time.tv_sec;
1252 seconds = tmp % 60; tmp /= 60;
1253 minutes = tmp % 60; tmp /= 60;
1254 hours = tmp % 24; tmp /= 24;
1255 days = tmp;
1257 printf("CTDB version %u\n", CTDB_PROTOCOL);
1258 printf("Current time of statistics : %s",
1259 ctime(&s->statistics_current_time.tv_sec));
1260 printf("Statistics collected since : (%03d %02d:%02d:%02d) %s",
1261 days, hours, minutes, seconds,
1262 ctime(&s->statistics_start_time.tv_sec));
1264 for (i=0; i<ARRAY_SIZE(stats_fields); i++) {
1265 if (strchr(stats_fields[i].name, '.') != NULL) {
1266 preflen = strcspn(stats_fields[i].name, ".") + 1;
1267 if (! prefix ||
1268 strncmp(prefix, stats_fields[i].name, preflen) != 0) {
1269 prefix = stats_fields[i].name;
1270 printf(" %*.*s\n", preflen-1, preflen-1,
1271 stats_fields[i].name);
1273 } else {
1274 preflen = 0;
1276 printf(" %*s%-22s%*s%10u\n", preflen ? 4 : 0, "",
1277 stats_fields[i].name+preflen, preflen ? 0 : 4, "",
1278 *(uint32_t *)(stats_fields[i].offset+(uint8_t *)s));
1281 printf(" hop_count_buckets:");
1282 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
1283 printf(" %d", s->hop_count_bucket[i]);
1285 printf("\n");
1286 printf(" lock_buckets:");
1287 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
1288 printf(" %d", s->locks.buckets[i]);
1290 printf("\n");
1291 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1292 "locks_latency MIN/AVG/MAX",
1293 s->locks.latency.min, LATENCY_AVG(s->locks.latency),
1294 s->locks.latency.max, s->locks.latency.num);
1296 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1297 "reclock_ctdbd MIN/AVG/MAX",
1298 s->reclock.ctdbd.min, LATENCY_AVG(s->reclock.ctdbd),
1299 s->reclock.ctdbd.max, s->reclock.ctdbd.num);
1301 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1302 "reclock_recd MIN/AVG/MAX",
1303 s->reclock.recd.min, LATENCY_AVG(s->reclock.recd),
1304 s->reclock.recd.max, s->reclock.recd.num);
1306 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1307 "call_latency MIN/AVG/MAX",
1308 s->call_latency.min, LATENCY_AVG(s->call_latency),
1309 s->call_latency.max, s->call_latency.num);
1311 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1312 "childwrite_latency MIN/AVG/MAX",
1313 s->childwrite_latency.min,
1314 LATENCY_AVG(s->childwrite_latency),
1315 s->childwrite_latency.max, s->childwrite_latency.num);
1318 static int control_statistics(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1319 int argc, const char **argv)
1321 struct ctdb_statistics *stats;
1322 int ret;
1324 if (argc != 0) {
1325 usage("statistics");
1328 ret = ctdb_ctrl_statistics(mem_ctx, ctdb->ev, ctdb->client,
1329 ctdb->cmd_pnn, TIMEOUT(), &stats);
1330 if (ret != 0) {
1331 return ret;
1334 if (options.machinereadable) {
1335 print_statistics_machine(stats, true);
1336 } else {
1337 print_statistics(stats);
1340 return 0;
1343 static int control_statistics_reset(TALLOC_CTX *mem_ctx,
1344 struct ctdb_context *ctdb,
1345 int argc, const char **argv)
1347 int ret;
1349 if (argc != 0) {
1350 usage("statisticsreset");
1353 ret = ctdb_ctrl_statistics_reset(mem_ctx, ctdb->ev, ctdb->client,
1354 ctdb->cmd_pnn, TIMEOUT());
1355 if (ret != 0) {
1356 return ret;
1359 return 0;
1362 static int control_stats(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1363 int argc, const char **argv)
1365 struct ctdb_statistics_list *slist;
1366 int ret, count = 0, i;
1367 bool show_header = true;
1369 if (argc > 1) {
1370 usage("stats");
1373 if (argc == 1) {
1374 count = atoi(argv[0]);
1377 ret = ctdb_ctrl_get_stat_history(mem_ctx, ctdb->ev, ctdb->client,
1378 ctdb->cmd_pnn, TIMEOUT(), &slist);
1379 if (ret != 0) {
1380 return ret;
1383 for (i=0; i<slist->num; i++) {
1384 if (slist->stats[i].statistics_start_time.tv_sec == 0) {
1385 continue;
1387 if (options.machinereadable == 1) {
1388 print_statistics_machine(&slist->stats[i],
1389 show_header);
1390 show_header = false;
1391 } else {
1392 print_statistics(&slist->stats[i]);
1394 if (count > 0 && i == count) {
1395 break;
1399 return 0;
1402 static int ctdb_public_ip_cmp(const void *a, const void *b)
1404 const struct ctdb_public_ip *ip_a = a;
1405 const struct ctdb_public_ip *ip_b = b;
1407 return ctdb_sock_addr_cmp(&ip_a->addr, &ip_b->addr);
1410 static void print_ip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1411 struct ctdb_public_ip_list *ips,
1412 struct ctdb_public_ip_info **ipinfo,
1413 bool all_nodes)
1415 int i, j;
1416 char *conf, *avail, *active;
1418 if (options.machinereadable == 1) {
1419 printf("%s%s%s%s%s", options.sep,
1420 "Public IP", options.sep,
1421 "Node", options.sep);
1422 if (options.verbose == 1) {
1423 printf("%s%s%s%s%s%s\n",
1424 "ActiveInterfaces", options.sep,
1425 "AvailableInterfaces", options.sep,
1426 "ConfiguredInterfaces", options.sep);
1427 } else {
1428 printf("\n");
1430 } else {
1431 if (all_nodes) {
1432 printf("Public IPs on ALL nodes\n");
1433 } else {
1434 printf("Public IPs on node %u\n", ctdb->cmd_pnn);
1438 for (i = 0; i < ips->num; i++) {
1440 if (options.machinereadable == 1) {
1441 printf("%s%s%s%d%s", options.sep,
1442 ctdb_sock_addr_to_string(
1443 mem_ctx, &ips->ip[i].addr, false),
1444 options.sep,
1445 (int)ips->ip[i].pnn, options.sep);
1446 } else {
1447 printf("%s", ctdb_sock_addr_to_string(
1448 mem_ctx, &ips->ip[i].addr, false));
1451 if (options.verbose == 0) {
1452 if (options.machinereadable == 1) {
1453 printf("\n");
1454 } else {
1455 printf(" %d\n", (int)ips->ip[i].pnn);
1457 continue;
1460 conf = NULL;
1461 avail = NULL;
1462 active = NULL;
1464 if (ipinfo[i] == NULL) {
1465 goto skip_ipinfo;
1468 for (j=0; j<ipinfo[i]->ifaces->num; j++) {
1469 struct ctdb_iface *iface;
1471 iface = &ipinfo[i]->ifaces->iface[j];
1472 if (conf == NULL) {
1473 conf = talloc_strdup(mem_ctx, iface->name);
1474 } else {
1475 conf = talloc_asprintf_append(
1476 conf, ",%s", iface->name);
1479 if (ipinfo[i]->active_idx == j) {
1480 active = iface->name;
1483 if (iface->link_state == 0) {
1484 continue;
1487 if (avail == NULL) {
1488 avail = talloc_strdup(mem_ctx, iface->name);
1489 } else {
1490 avail = talloc_asprintf_append(
1491 avail, ",%s", iface->name);
1495 skip_ipinfo:
1497 if (options.machinereadable == 1) {
1498 printf("%s%s%s%s%s%s\n",
1499 active ? active : "", options.sep,
1500 avail ? avail : "", options.sep,
1501 conf ? conf : "", options.sep);
1502 } else {
1503 printf(" node[%d] active[%s] available[%s]"
1504 " configured[%s]\n",
1505 (int)ips->ip[i].pnn, active ? active : "",
1506 avail ? avail : "", conf ? conf : "");
1511 static int collect_ips(uint8_t *keybuf, size_t keylen, uint8_t *databuf,
1512 size_t datalen, void *private_data)
1514 struct ctdb_public_ip_list *ips = talloc_get_type_abort(
1515 private_data, struct ctdb_public_ip_list);
1516 struct ctdb_public_ip *ip;
1518 ip = (struct ctdb_public_ip *)databuf;
1519 ips->ip[ips->num] = *ip;
1520 ips->num += 1;
1522 return 0;
1525 static int get_all_public_ips(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx,
1526 struct ctdb_public_ip_list **out)
1528 struct ctdb_node_map *nodemap;
1529 struct ctdb_public_ip_list *ips;
1530 struct db_hash_context *ipdb;
1531 uint32_t *pnn_list;
1532 int ret, count, i, j;
1534 nodemap = get_nodemap(ctdb, false);
1535 if (nodemap == NULL) {
1536 return 1;
1539 ret = db_hash_init(mem_ctx, "ips", 101, DB_HASH_COMPLEX, &ipdb);
1540 if (ret != 0) {
1541 goto failed;
1544 count = list_of_active_nodes(nodemap, CTDB_UNKNOWN_PNN, mem_ctx,
1545 &pnn_list);
1546 if (count <= 0) {
1547 goto failed;
1550 for (i=0; i<count; i++) {
1551 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
1552 pnn_list[i], TIMEOUT(),
1553 false, &ips);
1554 if (ret != 0) {
1555 goto failed;
1558 for (j=0; j<ips->num; j++) {
1559 struct ctdb_public_ip ip;
1561 ip.pnn = ips->ip[j].pnn;
1562 ip.addr = ips->ip[j].addr;
1564 if (pnn_list[i] == ip.pnn) {
1565 /* Node claims IP is hosted on it, so
1566 * save that information
1568 ret = db_hash_add(ipdb, (uint8_t *)&ip.addr,
1569 sizeof(ip.addr),
1570 (uint8_t *)&ip, sizeof(ip));
1571 if (ret != 0) {
1572 goto failed;
1574 } else {
1575 /* Node thinks IP is hosted elsewhere,
1576 * so overwrite with CTDB_UNKNOWN_PNN
1577 * if there's no existing entry
1579 ret = db_hash_exists(ipdb, (uint8_t *)&ip.addr,
1580 sizeof(ip.addr));
1581 if (ret == ENOENT) {
1582 ip.pnn = CTDB_UNKNOWN_PNN;
1583 ret = db_hash_add(ipdb,
1584 (uint8_t *)&ip.addr,
1585 sizeof(ip.addr),
1586 (uint8_t *)&ip,
1587 sizeof(ip));
1588 if (ret != 0) {
1589 goto failed;
1595 TALLOC_FREE(ips);
1598 talloc_free(pnn_list);
1600 ret = db_hash_traverse(ipdb, NULL, NULL, &count);
1601 if (ret != 0) {
1602 goto failed;
1605 ips = talloc_zero(mem_ctx, struct ctdb_public_ip_list);
1606 if (ips == NULL) {
1607 goto failed;
1610 ips->ip = talloc_array(ips, struct ctdb_public_ip, count);
1611 if (ips->ip == NULL) {
1612 goto failed;
1615 ret = db_hash_traverse(ipdb, collect_ips, ips, &count);
1616 if (ret != 0) {
1617 goto failed;
1620 if (count != ips->num) {
1621 goto failed;
1624 talloc_free(ipdb);
1626 *out = ips;
1627 return 0;
1629 failed:
1630 talloc_free(ipdb);
1631 return 1;
1634 static int control_ip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1635 int argc, const char **argv)
1637 struct ctdb_public_ip_list *ips;
1638 struct ctdb_public_ip_info **ipinfo;
1639 int ret, i;
1640 bool do_all = false;
1642 if (argc > 1) {
1643 usage("ip");
1646 if (argc == 1) {
1647 if (strcmp(argv[0], "all") == 0) {
1648 do_all = true;
1649 } else {
1650 usage("ip");
1654 if (do_all) {
1655 ret = get_all_public_ips(ctdb, mem_ctx, &ips);
1656 } else {
1657 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
1658 ctdb->cmd_pnn, TIMEOUT(),
1659 false, &ips);
1661 if (ret != 0) {
1662 return ret;
1665 qsort(ips->ip, ips->num, sizeof(struct ctdb_public_ip),
1666 ctdb_public_ip_cmp);
1668 ipinfo = talloc_array(mem_ctx, struct ctdb_public_ip_info *, ips->num);
1669 if (ipinfo == NULL) {
1670 return 1;
1673 for (i=0; i<ips->num; i++) {
1674 uint32_t pnn;
1675 if (do_all) {
1676 pnn = ips->ip[i].pnn;
1677 } else {
1678 pnn = ctdb->cmd_pnn;
1680 if (pnn == CTDB_UNKNOWN_PNN) {
1681 ipinfo[i] = NULL;
1682 continue;
1684 ret = ctdb_ctrl_get_public_ip_info(mem_ctx, ctdb->ev,
1685 ctdb->client, pnn,
1686 TIMEOUT(), &ips->ip[i].addr,
1687 &ipinfo[i]);
1688 if (ret != 0) {
1689 return ret;
1693 print_ip(mem_ctx, ctdb, ips, ipinfo, do_all);
1694 return 0;
1697 static int control_ipinfo(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1698 int argc, const char **argv)
1700 struct ctdb_public_ip_info *ipinfo;
1701 ctdb_sock_addr addr;
1702 int ret, i;
1704 if (argc != 1) {
1705 usage("ipinfo");
1708 ret = ctdb_sock_addr_from_string(argv[0], &addr, false);
1709 if (ret != 0) {
1710 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
1711 return 1;
1714 ret = ctdb_ctrl_get_public_ip_info(mem_ctx, ctdb->ev, ctdb->client,
1715 ctdb->cmd_pnn, TIMEOUT(), &addr,
1716 &ipinfo);
1717 if (ret != 0) {
1718 if (ret == -1) {
1719 printf("Node %u does not know about IP %s\n",
1720 ctdb->cmd_pnn, argv[0]);
1722 return ret;
1725 printf("Public IP[%s] info on node %u\n",
1726 ctdb_sock_addr_to_string(mem_ctx, &ipinfo->ip.addr, false),
1727 ctdb->cmd_pnn);
1729 printf("IP:%s\nCurrentNode:%u\nNumInterfaces:%u\n",
1730 ctdb_sock_addr_to_string(mem_ctx, &ipinfo->ip.addr, false),
1731 ipinfo->ip.pnn, ipinfo->ifaces->num);
1733 for (i=0; i<ipinfo->ifaces->num; i++) {
1734 struct ctdb_iface *iface;
1736 iface = &ipinfo->ifaces->iface[i];
1737 iface->name[CTDB_IFACE_SIZE] = '\0';
1738 printf("Interface[%u]: Name:%s Link:%s References:%u%s\n",
1739 i+1, iface->name,
1740 iface->link_state == 0 ? "down" : "up",
1741 iface->references,
1742 (i == ipinfo->active_idx) ? " (active)" : "");
1745 return 0;
1748 static int control_ifaces(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1749 int argc, const char **argv)
1751 struct ctdb_iface_list *ifaces;
1752 int ret, i;
1754 if (argc != 0) {
1755 usage("ifaces");
1758 ret = ctdb_ctrl_get_ifaces(mem_ctx, ctdb->ev, ctdb->client,
1759 ctdb->cmd_pnn, TIMEOUT(), &ifaces);
1760 if (ret != 0) {
1761 return ret;
1764 if (ifaces->num == 0) {
1765 printf("No interfaces configured on node %u\n",
1766 ctdb->cmd_pnn);
1767 return 0;
1770 if (options.machinereadable) {
1771 printf("%s%s%s%s%s%s%s\n", options.sep,
1772 "Name", options.sep,
1773 "LinkStatus", options.sep,
1774 "References", options.sep);
1775 } else {
1776 printf("Interfaces on node %u\n", ctdb->cmd_pnn);
1779 for (i=0; i<ifaces->num; i++) {
1780 if (options.machinereadable) {
1781 printf("%s%s%s%u%s%u%s\n", options.sep,
1782 ifaces->iface[i].name, options.sep,
1783 ifaces->iface[i].link_state, options.sep,
1784 ifaces->iface[i].references, options.sep);
1785 } else {
1786 printf("name:%s link:%s references:%u\n",
1787 ifaces->iface[i].name,
1788 ifaces->iface[i].link_state ? "up" : "down",
1789 ifaces->iface[i].references);
1793 return 0;
1796 static int control_setifacelink(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1797 int argc, const char **argv)
1799 struct ctdb_iface_list *ifaces;
1800 struct ctdb_iface *iface;
1801 int ret, i;
1803 if (argc != 2) {
1804 usage("setifacelink");
1807 if (strlen(argv[0]) > CTDB_IFACE_SIZE) {
1808 fprintf(stderr, "Interface name '%s' too long\n", argv[0]);
1809 return 1;
1812 ret = ctdb_ctrl_get_ifaces(mem_ctx, ctdb->ev, ctdb->client,
1813 ctdb->cmd_pnn, TIMEOUT(), &ifaces);
1814 if (ret != 0) {
1815 fprintf(stderr,
1816 "Failed to get interface information from node %u\n",
1817 ctdb->cmd_pnn);
1818 return ret;
1821 iface = NULL;
1822 for (i=0; i<ifaces->num; i++) {
1823 if (strcmp(ifaces->iface[i].name, argv[0]) == 0) {
1824 iface = &ifaces->iface[i];
1825 break;
1829 if (iface == NULL) {
1830 printf("Interface %s not configured on node %u\n",
1831 argv[0], ctdb->cmd_pnn);
1832 return 1;
1835 if (strcmp(argv[1], "up") == 0) {
1836 iface->link_state = 1;
1837 } else if (strcmp(argv[1], "down") == 0) {
1838 iface->link_state = 0;
1839 } else {
1840 usage("setifacelink");
1841 return 1;
1844 iface->references = 0;
1846 ret = ctdb_ctrl_set_iface_link_state(mem_ctx, ctdb->ev, ctdb->client,
1847 ctdb->cmd_pnn, TIMEOUT(), iface);
1848 if (ret != 0) {
1849 return ret;
1852 return 0;
1855 static int control_process_exists(TALLOC_CTX *mem_ctx,
1856 struct ctdb_context *ctdb,
1857 int argc, const char **argv)
1859 pid_t pid;
1860 uint64_t srvid = 0;
1861 int ret, status;
1863 if (argc != 1 && argc != 2) {
1864 usage("process-exists");
1867 pid = atoi(argv[0]);
1868 if (argc == 2) {
1869 srvid = strtoull(argv[1], NULL, 0);
1872 if (srvid == 0) {
1873 ret = ctdb_ctrl_process_exists(mem_ctx, ctdb->ev, ctdb->client,
1874 ctdb->cmd_pnn, TIMEOUT(), pid, &status);
1875 } else {
1876 struct ctdb_pid_srvid pid_srvid;
1878 pid_srvid.pid = pid;
1879 pid_srvid.srvid = srvid;
1881 ret = ctdb_ctrl_check_pid_srvid(mem_ctx, ctdb->ev,
1882 ctdb->client, ctdb->cmd_pnn,
1883 TIMEOUT(), &pid_srvid,
1884 &status);
1887 if (ret != 0) {
1888 return ret;
1891 if (srvid == 0) {
1892 printf("PID %d %s\n", pid,
1893 (status == 0 ? "exists" : "does not exist"));
1894 } else {
1895 printf("PID %d with SRVID 0x%"PRIx64" %s\n", pid, srvid,
1896 (status == 0 ? "exists" : "does not exist"));
1898 return status;
1901 static int control_getdbmap(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1902 int argc, const char **argv)
1904 struct ctdb_dbid_map *dbmap;
1905 int ret, i;
1907 if (argc != 0) {
1908 usage("getdbmap");
1911 ret = ctdb_ctrl_get_dbmap(mem_ctx, ctdb->ev, ctdb->client,
1912 ctdb->cmd_pnn, TIMEOUT(), &dbmap);
1913 if (ret != 0) {
1914 return ret;
1917 if (options.machinereadable == 1) {
1918 printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
1919 options.sep,
1920 "ID", options.sep,
1921 "Name", options.sep,
1922 "Path", options.sep,
1923 "Persistent", options.sep,
1924 "Sticky", options.sep,
1925 "Unhealthy", options.sep,
1926 "Readonly", options.sep,
1927 "Replicated", options.sep);
1928 } else {
1929 printf("Number of databases:%d\n", dbmap->num);
1932 for (i=0; i<dbmap->num; i++) {
1933 const char *name;
1934 const char *path;
1935 const char *health;
1936 bool persistent;
1937 bool readonly;
1938 bool sticky;
1939 bool replicated;
1940 uint32_t db_id;
1942 db_id = dbmap->dbs[i].db_id;
1944 ret = ctdb_ctrl_get_dbname(mem_ctx, ctdb->ev, ctdb->client,
1945 ctdb->cmd_pnn, TIMEOUT(), db_id,
1946 &name);
1947 if (ret != 0) {
1948 return ret;
1951 ret = ctdb_ctrl_getdbpath(mem_ctx, ctdb->ev, ctdb->client,
1952 ctdb->cmd_pnn, TIMEOUT(), db_id,
1953 &path);
1954 if (ret != 0) {
1955 return ret;
1958 ret = ctdb_ctrl_db_get_health(mem_ctx, ctdb->ev, ctdb->client,
1959 ctdb->cmd_pnn, TIMEOUT(), db_id,
1960 &health);
1961 if (ret != 0) {
1962 return ret;
1965 persistent = dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT;
1966 readonly = dbmap->dbs[i].flags & CTDB_DB_FLAGS_READONLY;
1967 sticky = dbmap->dbs[i].flags & CTDB_DB_FLAGS_STICKY;
1968 replicated = dbmap->dbs[i].flags & CTDB_DB_FLAGS_REPLICATED;
1970 if (options.machinereadable == 1) {
1971 printf("%s0x%08X%s%s%s%s%s%d%s%d%s%d%s%d%s%d%s\n",
1972 options.sep,
1973 db_id, options.sep,
1974 name, options.sep,
1975 path, options.sep,
1976 !! (persistent), options.sep,
1977 !! (sticky), options.sep,
1978 !! (health), options.sep,
1979 !! (readonly), options.sep,
1980 !! (replicated), options.sep);
1981 } else {
1982 printf("dbid:0x%08x name:%s path:%s%s%s%s%s%s\n",
1983 db_id, name, path,
1984 persistent ? " PERSISTENT" : "",
1985 sticky ? " STICKY" : "",
1986 readonly ? " READONLY" : "",
1987 replicated ? " REPLICATED" : "",
1988 health ? " UNHEALTHY" : "");
1991 talloc_free(discard_const(name));
1992 talloc_free(discard_const(path));
1993 talloc_free(discard_const(health));
1996 return 0;
1999 static int control_getdbstatus(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2000 int argc, const char **argv)
2002 uint32_t db_id;
2003 const char *db_name, *db_path, *db_health;
2004 uint8_t db_flags;
2005 int ret;
2007 if (argc != 1) {
2008 usage("getdbstatus");
2011 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
2012 return 1;
2015 ret = ctdb_ctrl_getdbpath(mem_ctx, ctdb->ev, ctdb->client,
2016 ctdb->cmd_pnn, TIMEOUT(), db_id,
2017 &db_path);
2018 if (ret != 0) {
2019 return ret;
2022 ret = ctdb_ctrl_db_get_health(mem_ctx, ctdb->ev, ctdb->client,
2023 ctdb->cmd_pnn, TIMEOUT(), db_id,
2024 &db_health);
2025 if (ret != 0) {
2026 return ret;
2029 printf("dbid: 0x%08x\nname: %s\npath: %s\n", db_id, db_name, db_path);
2030 printf("PERSISTENT: %s\nREPLICATED: %s\nSTICKY: %s\nREADONLY: %s\n",
2031 (db_flags & CTDB_DB_FLAGS_PERSISTENT ? "yes" : "no"),
2032 (db_flags & CTDB_DB_FLAGS_REPLICATED ? "yes" : "no"),
2033 (db_flags & CTDB_DB_FLAGS_STICKY ? "yes" : "no"),
2034 (db_flags & CTDB_DB_FLAGS_READONLY ? "yes" : "no"));
2035 printf("HEALTH: %s\n", (db_health ? db_health : "OK"));
2036 return 0;
2039 struct dump_record_state {
2040 uint32_t count;
2043 #define ISASCII(x) (isprint(x) && ! strchr("\"\\", (x)))
2045 static void dump_tdb_data(const char *name, TDB_DATA val)
2047 int i;
2049 fprintf(stdout, "%s(%zu) = \"", name, val.dsize);
2050 for (i=0; i<val.dsize; i++) {
2051 if (ISASCII(val.dptr[i])) {
2052 fprintf(stdout, "%c", val.dptr[i]);
2053 } else {
2054 fprintf(stdout, "\\%02X", val.dptr[i]);
2057 fprintf(stdout, "\"\n");
2060 static void dump_ltdb_header(struct ctdb_ltdb_header *header)
2062 fprintf(stdout, "dmaster: %u\n", header->dmaster);
2063 fprintf(stdout, "rsn: %" PRIu64 "\n", header->rsn);
2064 fprintf(stdout, "flags: 0x%08x", header->flags);
2065 if (header->flags & CTDB_REC_FLAG_MIGRATED_WITH_DATA) {
2066 fprintf(stdout, " MIGRATED_WITH_DATA");
2068 if (header->flags & CTDB_REC_FLAG_VACUUM_MIGRATED) {
2069 fprintf(stdout, " VACUUM_MIGRATED");
2071 if (header->flags & CTDB_REC_FLAG_AUTOMATIC) {
2072 fprintf(stdout, " AUTOMATIC");
2074 if (header->flags & CTDB_REC_RO_HAVE_DELEGATIONS) {
2075 fprintf(stdout, " RO_HAVE_DELEGATIONS");
2077 if (header->flags & CTDB_REC_RO_HAVE_READONLY) {
2078 fprintf(stdout, " RO_HAVE_READONLY");
2080 if (header->flags & CTDB_REC_RO_REVOKING_READONLY) {
2081 fprintf(stdout, " RO_REVOKING_READONLY");
2083 if (header->flags & CTDB_REC_RO_REVOKE_COMPLETE) {
2084 fprintf(stdout, " RO_REVOKE_COMPLETE");
2086 fprintf(stdout, "\n");
2090 static int dump_record(uint32_t reqid, struct ctdb_ltdb_header *header,
2091 TDB_DATA key, TDB_DATA data, void *private_data)
2093 struct dump_record_state *state =
2094 (struct dump_record_state *)private_data;
2096 state->count += 1;
2098 dump_tdb_data("key", key);
2099 dump_ltdb_header(header);
2100 dump_tdb_data("data", data);
2101 fprintf(stdout, "\n");
2103 return 0;
2106 static int control_catdb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2107 int argc, const char **argv)
2109 struct ctdb_db_context *db;
2110 const char *db_name;
2111 uint32_t db_id;
2112 uint8_t db_flags;
2113 struct dump_record_state state;
2114 int ret;
2116 if (argc != 1) {
2117 usage("catdb");
2120 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
2121 return 1;
2124 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
2125 db_flags, &db);
2126 if (ret != 0) {
2127 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
2128 return ret;
2131 state.count = 0;
2133 ret = ctdb_db_traverse(mem_ctx, ctdb->ev, ctdb->client, db,
2134 ctdb->cmd_pnn, TIMEOUT(),
2135 dump_record, &state);
2137 printf("Dumped %u records\n", state.count);
2139 return ret;
2142 static int control_cattdb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2143 int argc, const char **argv)
2145 struct ctdb_db_context *db;
2146 const char *db_name;
2147 uint32_t db_id;
2148 uint8_t db_flags;
2149 struct dump_record_state state;
2150 int ret;
2152 if (argc != 1) {
2153 usage("catdb");
2156 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
2157 return 1;
2160 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
2161 db_flags, &db);
2162 if (ret != 0) {
2163 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
2164 return ret;
2167 state.count = 0;
2168 ret = ctdb_db_traverse_local(db, true, true, dump_record, &state);
2170 printf("Dumped %u record(s)\n", state.count);
2172 return ret;
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_setdebug(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2243 int argc, const char **argv)
2245 int log_level;
2246 int ret;
2247 bool found;
2249 if (argc != 1) {
2250 usage("setdebug");
2253 found = debug_level_parse(argv[0], &log_level);
2254 if (! found) {
2255 fprintf(stderr,
2256 "Invalid debug level '%s'. Valid levels are:\n",
2257 argv[0]);
2258 fprintf(stderr, "\tERROR | WARNING | NOTICE | INFO | DEBUG\n");
2259 return 1;
2262 ret = ctdb_ctrl_setdebug(mem_ctx, ctdb->ev, ctdb->client,
2263 ctdb->cmd_pnn, TIMEOUT(), log_level);
2264 if (ret != 0) {
2265 return ret;
2268 return 0;
2271 static int control_getdebug(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2272 int argc, const char **argv)
2274 int loglevel;
2275 const char *log_str;
2276 int ret;
2278 if (argc != 0) {
2279 usage("getdebug");
2282 ret = ctdb_ctrl_getdebug(mem_ctx, ctdb->ev, ctdb->client,
2283 ctdb->cmd_pnn, TIMEOUT(), &loglevel);
2284 if (ret != 0) {
2285 return ret;
2288 log_str = debug_level_to_string(loglevel);
2289 printf("%s\n", log_str);
2291 return 0;
2294 static int control_attach(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2295 int argc, const char **argv)
2297 const char *db_name;
2298 uint8_t db_flags = 0;
2299 int ret;
2301 if (argc < 1 || argc > 2) {
2302 usage("attach");
2305 db_name = argv[0];
2306 if (argc == 2) {
2307 if (strcmp(argv[1], "persistent") == 0) {
2308 db_flags = CTDB_DB_FLAGS_PERSISTENT;
2309 } else if (strcmp(argv[1], "readonly") == 0) {
2310 db_flags = CTDB_DB_FLAGS_READONLY;
2311 } else if (strcmp(argv[1], "sticky") == 0) {
2312 db_flags = CTDB_DB_FLAGS_STICKY;
2313 } else if (strcmp(argv[1], "replicated") == 0) {
2314 db_flags = CTDB_DB_FLAGS_REPLICATED;
2315 } else {
2316 usage("attach");
2320 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
2321 db_flags, NULL);
2322 if (ret != 0) {
2323 return ret;
2326 return 0;
2329 static int control_detach(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2330 int argc, const char **argv)
2332 const char *db_name;
2333 uint32_t db_id;
2334 uint8_t db_flags;
2335 struct ctdb_node_map *nodemap;
2336 int recmode;
2337 int ret, ret2, i;
2339 if (argc < 1) {
2340 usage("detach");
2343 ret = ctdb_ctrl_get_recmode(mem_ctx, ctdb->ev, ctdb->client,
2344 ctdb->cmd_pnn, TIMEOUT(), &recmode);
2345 if (ret != 0) {
2346 return ret;
2349 if (recmode == CTDB_RECOVERY_ACTIVE) {
2350 fprintf(stderr, "Database cannot be detached"
2351 " when recovery is active\n");
2352 return 1;
2355 nodemap = get_nodemap(ctdb, false);
2356 if (nodemap == NULL) {
2357 return 1;
2360 for (i=0; i<nodemap->num; i++) {
2361 uint32_t value;
2363 if (nodemap->node[i].flags & NODE_FLAGS_DISCONNECTED) {
2364 continue;
2366 if (nodemap->node[i].flags & NODE_FLAGS_DELETED) {
2367 continue;
2369 if (nodemap->node[i].flags & NODE_FLAGS_INACTIVE) {
2370 fprintf(stderr, "Database cannot be detached on"
2371 " inactive (stopped or banned) node %u\n",
2372 nodemap->node[i].pnn);
2373 return 1;
2376 ret = ctdb_ctrl_get_tunable(mem_ctx, ctdb->ev, ctdb->client,
2377 nodemap->node[i].pnn, TIMEOUT(),
2378 "AllowClientDBAttach", &value);
2379 if (ret != 0) {
2380 fprintf(stderr,
2381 "Unable to get tunable AllowClientDBAttach"
2382 " from node %u\n", nodemap->node[i].pnn);
2383 return ret;
2386 if (value == 1) {
2387 fprintf(stderr,
2388 "Database access is still active on node %u."
2389 " Set AllowclientDBAttach=0 on all nodes.\n",
2390 nodemap->node[i].pnn);
2391 return 1;
2395 ret2 = 0;
2396 for (i=0; i<argc; i++) {
2397 if (! db_exists(mem_ctx, ctdb, argv[i], &db_id, &db_name,
2398 &db_flags)) {
2399 continue;
2402 if (db_flags &
2403 (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
2404 fprintf(stderr,
2405 "Only volatile databases can be detached\n");
2406 return 1;
2409 ret = ctdb_detach(ctdb->ev, ctdb->client, TIMEOUT(), db_id);
2410 if (ret != 0) {
2411 fprintf(stderr, "Database %s detach failed\n", db_name);
2412 ret2 = ret;
2416 return ret2;
2419 static int control_dumpmemory(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2420 int argc, const char **argv)
2422 const char *mem_str;
2423 ssize_t n;
2424 int ret;
2426 ret = ctdb_ctrl_dump_memory(mem_ctx, ctdb->ev, ctdb->client,
2427 ctdb->cmd_pnn, TIMEOUT(), &mem_str);
2428 if (ret != 0) {
2429 return ret;
2432 n = write(1, mem_str, strlen(mem_str)+1);
2433 if (n < 0 || n != strlen(mem_str)+1) {
2434 fprintf(stderr, "Failed to write talloc summary\n");
2435 return 1;
2438 return 0;
2441 static void dump_memory(uint64_t srvid, TDB_DATA data, void *private_data)
2443 bool *done = (bool *)private_data;
2444 ssize_t n;
2446 n = write(1, data.dptr, data.dsize);
2447 if (n < 0 || n != data.dsize) {
2448 fprintf(stderr, "Failed to write talloc summary\n");
2451 *done = true;
2454 static int control_rddumpmemory(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2455 int argc, const char **argv)
2457 struct ctdb_srvid_message msg = { 0 };
2458 int ret;
2459 bool done = false;
2461 msg.pnn = ctdb->pnn;
2462 msg.srvid = next_srvid(ctdb);
2464 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
2465 msg.srvid, dump_memory, &done);
2466 if (ret != 0) {
2467 return ret;
2470 ret = ctdb_message_mem_dump(mem_ctx, ctdb->ev, ctdb->client,
2471 ctdb->cmd_pnn, &msg);
2472 if (ret != 0) {
2473 return ret;
2476 ctdb_client_wait(ctdb->ev, &done);
2477 return 0;
2480 static int control_getpid(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2481 int argc, const char **argv)
2483 pid_t pid;
2484 int ret;
2486 ret = ctdb_ctrl_get_pid(mem_ctx, ctdb->ev, ctdb->client,
2487 ctdb->cmd_pnn, TIMEOUT(), &pid);
2488 if (ret != 0) {
2489 return ret;
2492 printf("%u\n", pid);
2493 return 0;
2496 static int check_flags(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2497 const char *desc, uint32_t flag, bool set_flag)
2499 struct ctdb_node_map *nodemap;
2500 bool flag_is_set;
2502 nodemap = get_nodemap(ctdb, false);
2503 if (nodemap == NULL) {
2504 return 1;
2507 flag_is_set = nodemap->node[ctdb->cmd_pnn].flags & flag;
2508 if (set_flag == flag_is_set) {
2509 if (set_flag) {
2510 fprintf(stderr, "Node %u is already %s\n",
2511 ctdb->cmd_pnn, desc);
2512 } else {
2513 fprintf(stderr, "Node %u is not %s\n",
2514 ctdb->cmd_pnn, desc);
2516 return 0;
2519 return 1;
2522 static void wait_for_flags(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2523 uint32_t flag, bool set_flag)
2525 struct ctdb_node_map *nodemap;
2526 bool flag_is_set;
2528 while (1) {
2529 nodemap = get_nodemap(ctdb, true);
2530 if (nodemap == NULL) {
2531 fprintf(stderr,
2532 "Failed to get nodemap, trying again\n");
2533 sleep(1);
2534 continue;
2537 flag_is_set = nodemap->node[ctdb->cmd_pnn].flags & flag;
2538 if (flag_is_set == set_flag) {
2539 break;
2542 sleep(1);
2546 static int ctdb_ctrl_modflags(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
2547 struct ctdb_client_context *client,
2548 uint32_t destnode, struct timeval timeout,
2549 uint32_t set, uint32_t clear)
2551 struct ctdb_node_map *nodemap;
2552 struct ctdb_node_flag_change flag_change;
2553 struct ctdb_req_control request;
2554 uint32_t *pnn_list;
2555 int ret, count;
2557 ret = ctdb_ctrl_get_nodemap(mem_ctx, ev, client, destnode,
2558 tevent_timeval_zero(), &nodemap);
2559 if (ret != 0) {
2560 return ret;
2563 flag_change.pnn = destnode;
2564 flag_change.old_flags = nodemap->node[destnode].flags;
2565 flag_change.new_flags = flag_change.old_flags | set;
2566 flag_change.new_flags &= ~clear;
2568 count = list_of_connected_nodes(nodemap, -1, mem_ctx, &pnn_list);
2569 if (count == -1) {
2570 return ENOMEM;
2573 ctdb_req_control_modify_flags(&request, &flag_change);
2574 ret = ctdb_client_control_multi(mem_ctx, ev, client, pnn_list, count,
2575 tevent_timeval_zero(), &request,
2576 NULL, NULL);
2577 return ret;
2580 struct ipreallocate_state {
2581 int status;
2582 bool done;
2585 static void ipreallocate_handler(uint64_t srvid, TDB_DATA data,
2586 void *private_data)
2588 struct ipreallocate_state *state =
2589 (struct ipreallocate_state *)private_data;
2591 if (data.dsize != sizeof(int)) {
2592 /* Ignore packet */
2593 return;
2596 state->status = *(int *)data.dptr;
2597 state->done = true;
2600 static int ipreallocate(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb)
2602 struct ctdb_srvid_message msg = { 0 };
2603 struct ipreallocate_state state;
2604 int ret;
2606 msg.pnn = ctdb->pnn;
2607 msg.srvid = next_srvid(ctdb);
2609 state.done = false;
2610 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
2611 msg.srvid,
2612 ipreallocate_handler, &state);
2613 if (ret != 0) {
2614 return ret;
2617 while (true) {
2618 ret = ctdb_message_takeover_run(mem_ctx, ctdb->ev,
2619 ctdb->client,
2620 CTDB_BROADCAST_CONNECTED,
2621 &msg);
2622 if (ret != 0) {
2623 goto fail;
2626 ret = ctdb_client_wait_timeout(ctdb->ev, &state.done,
2627 TIMEOUT());
2628 if (ret != 0) {
2629 continue;
2632 if (state.status >= 0) {
2633 ret = 0;
2634 } else {
2635 ret = state.status;
2637 break;
2640 fail:
2641 ctdb_client_remove_message_handler(ctdb->ev, ctdb->client,
2642 msg.srvid, &state);
2643 return ret;
2646 static int control_disable(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2647 int argc, const char **argv)
2649 int ret;
2651 if (argc != 0) {
2652 usage("disable");
2655 ret = check_flags(mem_ctx, ctdb, "disabled",
2656 NODE_FLAGS_PERMANENTLY_DISABLED, true);
2657 if (ret == 0) {
2658 return 0;
2661 ret = ctdb_ctrl_modflags(mem_ctx, ctdb->ev, ctdb->client,
2662 ctdb->cmd_pnn, TIMEOUT(),
2663 NODE_FLAGS_PERMANENTLY_DISABLED, 0);
2664 if (ret != 0) {
2665 fprintf(stderr,
2666 "Failed to set DISABLED flag on node %u\n",
2667 ctdb->cmd_pnn);
2668 return ret;
2671 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_PERMANENTLY_DISABLED, true);
2672 return ipreallocate(mem_ctx, ctdb);
2675 static int control_enable(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2676 int argc, const char **argv)
2678 int ret;
2680 if (argc != 0) {
2681 usage("enable");
2684 ret = check_flags(mem_ctx, ctdb, "disabled",
2685 NODE_FLAGS_PERMANENTLY_DISABLED, false);
2686 if (ret == 0) {
2687 return 0;
2690 ret = ctdb_ctrl_modflags(mem_ctx, ctdb->ev, ctdb->client,
2691 ctdb->cmd_pnn, TIMEOUT(),
2692 0, NODE_FLAGS_PERMANENTLY_DISABLED);
2693 if (ret != 0) {
2694 fprintf(stderr, "Failed to reset DISABLED flag on node %u\n",
2695 ctdb->cmd_pnn);
2696 return ret;
2699 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_PERMANENTLY_DISABLED, false);
2700 return ipreallocate(mem_ctx, ctdb);
2703 static int control_stop(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2704 int argc, const char **argv)
2706 int ret;
2708 if (argc != 0) {
2709 usage("stop");
2712 ret = check_flags(mem_ctx, ctdb, "stopped",
2713 NODE_FLAGS_STOPPED, true);
2714 if (ret == 0) {
2715 return 0;
2718 ret = ctdb_ctrl_stop_node(mem_ctx, ctdb->ev, ctdb->client,
2719 ctdb->cmd_pnn, TIMEOUT());
2720 if (ret != 0) {
2721 fprintf(stderr, "Failed to stop node %u\n", ctdb->cmd_pnn);
2722 return ret;
2725 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_STOPPED, true);
2726 return ipreallocate(mem_ctx, ctdb);
2729 static int control_continue(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2730 int argc, const char **argv)
2732 int ret;
2734 if (argc != 0) {
2735 usage("continue");
2738 ret = check_flags(mem_ctx, ctdb, "stopped",
2739 NODE_FLAGS_STOPPED, false);
2740 if (ret == 0) {
2741 return 0;
2744 ret = ctdb_ctrl_continue_node(mem_ctx, ctdb->ev, ctdb->client,
2745 ctdb->cmd_pnn, TIMEOUT());
2746 if (ret != 0) {
2747 fprintf(stderr, "Failed to continue stopped node %u\n",
2748 ctdb->cmd_pnn);
2749 return ret;
2752 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_STOPPED, false);
2753 return ipreallocate(mem_ctx, ctdb);
2756 static int control_ban(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2757 int argc, const char **argv)
2759 struct ctdb_ban_state ban_state;
2760 int ret;
2762 if (argc != 1) {
2763 usage("ban");
2766 ret = check_flags(mem_ctx, ctdb, "banned",
2767 NODE_FLAGS_BANNED, true);
2768 if (ret == 0) {
2769 return 0;
2772 ban_state.pnn = ctdb->cmd_pnn;
2773 ban_state.time = strtoul(argv[0], NULL, 0);
2775 if (ban_state.time == 0) {
2776 fprintf(stderr, "Ban time cannot be zero\n");
2777 return EINVAL;
2780 ret = ctdb_ctrl_set_ban_state(mem_ctx, ctdb->ev, ctdb->client,
2781 ctdb->cmd_pnn, TIMEOUT(), &ban_state);
2782 if (ret != 0) {
2783 fprintf(stderr, "Failed to ban node %u\n", ctdb->cmd_pnn);
2784 return ret;
2787 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_BANNED, true);
2788 return ipreallocate(mem_ctx, ctdb);
2792 static int control_unban(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 != 0) {
2799 usage("unban");
2802 ret = check_flags(mem_ctx, ctdb, "banned",
2803 NODE_FLAGS_BANNED, false);
2804 if (ret == 0) {
2805 return 0;
2808 ban_state.pnn = ctdb->cmd_pnn;
2809 ban_state.time = 0;
2811 ret = ctdb_ctrl_set_ban_state(mem_ctx, ctdb->ev, ctdb->client,
2812 ctdb->cmd_pnn, TIMEOUT(), &ban_state);
2813 if (ret != 0) {
2814 fprintf(stderr, "Failed to unban node %u\n", ctdb->cmd_pnn);
2815 return ret;
2818 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_BANNED, false);
2819 return ipreallocate(mem_ctx, ctdb);
2823 static void wait_for_shutdown(void *private_data)
2825 bool *done = (bool *)private_data;
2827 *done = true;
2830 static int control_shutdown(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2831 int argc, const char **argv)
2833 int ret;
2834 bool done = false;
2836 if (argc != 0) {
2837 usage("shutdown");
2840 if (ctdb->pnn == ctdb->cmd_pnn) {
2841 ctdb_client_set_disconnect_callback(ctdb->client,
2842 wait_for_shutdown,
2843 &done);
2846 ret = ctdb_ctrl_shutdown(mem_ctx, ctdb->ev, ctdb->client,
2847 ctdb->cmd_pnn, TIMEOUT());
2848 if (ret != 0) {
2849 fprintf(stderr, "Unable to shutdown node %u\n", ctdb->cmd_pnn);
2850 return ret;
2853 if (ctdb->pnn == ctdb->cmd_pnn) {
2854 ctdb_client_wait(ctdb->ev, &done);
2857 return 0;
2860 static int get_generation(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2861 uint32_t *generation)
2863 uint32_t recmaster;
2864 int recmode;
2865 struct ctdb_vnn_map *vnnmap;
2866 int ret;
2868 again:
2869 ret = ctdb_ctrl_get_recmaster(mem_ctx, ctdb->ev, ctdb->client,
2870 ctdb->cmd_pnn, TIMEOUT(), &recmaster);
2871 if (ret != 0) {
2872 fprintf(stderr, "Failed to find recovery master\n");
2873 return ret;
2876 ret = ctdb_ctrl_get_recmode(mem_ctx, ctdb->ev, ctdb->client,
2877 recmaster, TIMEOUT(), &recmode);
2878 if (ret != 0) {
2879 fprintf(stderr, "Failed to get recovery mode from node %u\n",
2880 recmaster);
2881 return ret;
2884 if (recmode == CTDB_RECOVERY_ACTIVE) {
2885 sleep(1);
2886 goto again;
2889 ret = ctdb_ctrl_getvnnmap(mem_ctx, ctdb->ev, ctdb->client,
2890 recmaster, TIMEOUT(), &vnnmap);
2891 if (ret != 0) {
2892 fprintf(stderr, "Failed to get generation from node %u\n",
2893 recmaster);
2894 return ret;
2897 if (vnnmap->generation == INVALID_GENERATION) {
2898 talloc_free(vnnmap);
2899 sleep(1);
2900 goto again;
2903 *generation = vnnmap->generation;
2904 talloc_free(vnnmap);
2905 return 0;
2909 static int control_recover(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2910 int argc, const char **argv)
2912 uint32_t generation, next_generation;
2913 int ret;
2915 if (argc != 0) {
2916 usage("recover");
2919 ret = get_generation(mem_ctx, ctdb, &generation);
2920 if (ret != 0) {
2921 return ret;
2924 ret = ctdb_ctrl_set_recmode(mem_ctx, ctdb->ev, ctdb->client,
2925 ctdb->cmd_pnn, TIMEOUT(),
2926 CTDB_RECOVERY_ACTIVE);
2927 if (ret != 0) {
2928 fprintf(stderr, "Failed to set recovery mode active\n");
2929 return ret;
2932 while (1) {
2933 ret = get_generation(mem_ctx, ctdb, &next_generation);
2934 if (ret != 0) {
2935 fprintf(stderr,
2936 "Failed to confirm end of recovery\n");
2937 return ret;
2940 if (next_generation != generation) {
2941 break;
2944 sleep (1);
2947 return 0;
2950 static int control_ipreallocate(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2951 int argc, const char **argv)
2953 if (argc != 0) {
2954 usage("ipreallocate");
2957 return ipreallocate(mem_ctx, ctdb);
2960 static int control_isnotrecmaster(TALLOC_CTX *mem_ctx,
2961 struct ctdb_context *ctdb,
2962 int argc, const char **argv)
2964 uint32_t recmaster;
2965 int ret;
2967 if (argc != 0) {
2968 usage("isnotrecmaster");
2971 ret = ctdb_ctrl_get_recmaster(mem_ctx, ctdb->ev, ctdb->client,
2972 ctdb->pnn, TIMEOUT(), &recmaster);
2973 if (ret != 0) {
2974 fprintf(stderr, "Failed to get recmaster\n");
2975 return ret;
2978 if (recmaster != ctdb->pnn) {
2979 printf("this node is not the recmaster\n");
2980 return 1;
2983 printf("this node is the recmaster\n");
2984 return 0;
2987 static int control_gratarp(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2988 int argc, const char **argv)
2990 struct ctdb_addr_info addr_info;
2991 int ret;
2993 if (argc != 2) {
2994 usage("gratarp");
2997 ret = ctdb_sock_addr_from_string(argv[0], &addr_info.addr, false);
2998 if (ret != 0) {
2999 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3000 return 1;
3002 addr_info.iface = argv[1];
3004 ret = ctdb_ctrl_send_gratuitous_arp(mem_ctx, ctdb->ev, ctdb->client,
3005 ctdb->cmd_pnn, TIMEOUT(),
3006 &addr_info);
3007 if (ret != 0) {
3008 fprintf(stderr, "Unable to send gratuitous arp from node %u\n",
3009 ctdb->cmd_pnn);
3010 return ret;
3013 return 0;
3016 static int control_tickle(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3017 int argc, const char **argv)
3019 ctdb_sock_addr src, dst;
3020 int ret;
3022 if (argc != 0 && argc != 2) {
3023 usage("tickle");
3026 if (argc == 0) {
3027 struct ctdb_connection_list *clist;
3028 int i;
3029 unsigned int num_failed;
3031 /* Client first but the src/dst logic is confused */
3032 ret = ctdb_connection_list_read(mem_ctx, false, &clist);
3033 if (ret != 0) {
3034 return ret;
3037 num_failed = 0;
3038 for (i = 0; i < clist->num; i++) {
3039 ret = ctdb_sys_send_tcp(&clist->conn[i].src,
3040 &clist->conn[i].dst,
3041 0, 0, 0);
3042 if (ret != 0) {
3043 num_failed += 1;
3047 TALLOC_FREE(clist);
3049 if (num_failed > 0) {
3050 fprintf(stderr, "Failed to send %d tickles\n",
3051 num_failed);
3052 return 1;
3055 return 0;
3059 ret = ctdb_sock_addr_from_string(argv[0], &src, true);
3060 if (ret != 0) {
3061 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3062 return 1;
3065 ret = ctdb_sock_addr_from_string(argv[1], &dst, true);
3066 if (ret != 0) {
3067 fprintf(stderr, "Invalid IP address %s\n", argv[1]);
3068 return 1;
3071 ret = ctdb_sys_send_tcp(&src, &dst, 0, 0, 0);
3072 if (ret != 0) {
3073 fprintf(stderr, "Failed to send tickle ack\n");
3074 return ret;
3077 return 0;
3080 static int control_gettickles(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3081 int argc, const char **argv)
3083 ctdb_sock_addr addr;
3084 struct ctdb_tickle_list *tickles;
3085 unsigned port = 0;
3086 int ret, i;
3088 if (argc < 1 || argc > 2) {
3089 usage("gettickles");
3092 if (argc == 2) {
3093 port = strtoul(argv[1], NULL, 10);
3096 ret = ctdb_sock_addr_from_string(argv[0], &addr, false);
3097 if (ret != 0) {
3098 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3099 return 1;
3101 ctdb_sock_addr_set_port(&addr, port);
3103 ret = ctdb_ctrl_get_tcp_tickle_list(mem_ctx, ctdb->ev, ctdb->client,
3104 ctdb->cmd_pnn, TIMEOUT(), &addr,
3105 &tickles);
3106 if (ret != 0) {
3107 fprintf(stderr, "Failed to get list of connections\n");
3108 return ret;
3111 if (options.machinereadable) {
3112 printf("%s%s%s%s%s%s%s%s%s\n",
3113 options.sep,
3114 "Source IP", options.sep,
3115 "Port", options.sep,
3116 "Destiation IP", options.sep,
3117 "Port", options.sep);
3118 for (i=0; i<tickles->num; i++) {
3119 printf("%s%s%s%u%s%s%s%u%s\n", options.sep,
3120 ctdb_sock_addr_to_string(
3121 mem_ctx, &tickles->conn[i].src, false),
3122 options.sep,
3123 ntohs(tickles->conn[i].src.ip.sin_port),
3124 options.sep,
3125 ctdb_sock_addr_to_string(
3126 mem_ctx, &tickles->conn[i].dst, false),
3127 options.sep,
3128 ntohs(tickles->conn[i].dst.ip.sin_port),
3129 options.sep);
3131 } else {
3132 printf("Connections for IP: %s\n",
3133 ctdb_sock_addr_to_string(mem_ctx,
3134 &tickles->addr, false));
3135 printf("Num connections: %u\n", tickles->num);
3136 for (i=0; i<tickles->num; i++) {
3137 printf("SRC: %s DST: %s\n",
3138 ctdb_sock_addr_to_string(
3139 mem_ctx, &tickles->conn[i].src, true),
3140 ctdb_sock_addr_to_string(
3141 mem_ctx, &tickles->conn[i].dst, true));
3145 talloc_free(tickles);
3146 return 0;
3149 typedef void (*clist_request_func)(struct ctdb_req_control *request,
3150 struct ctdb_connection *conn);
3152 typedef int (*clist_reply_func)(struct ctdb_reply_control *reply);
3154 struct process_clist_state {
3155 struct ctdb_connection_list *clist;
3156 int count;
3157 int num_failed, num_total;
3158 clist_reply_func reply_func;
3161 static void process_clist_done(struct tevent_req *subreq);
3163 static struct tevent_req *process_clist_send(
3164 TALLOC_CTX *mem_ctx,
3165 struct ctdb_context *ctdb,
3166 struct ctdb_connection_list *clist,
3167 clist_request_func request_func,
3168 clist_reply_func reply_func)
3170 struct tevent_req *req, *subreq;
3171 struct process_clist_state *state;
3172 struct ctdb_req_control request;
3173 int i;
3175 req = tevent_req_create(mem_ctx, &state, struct process_clist_state);
3176 if (req == NULL) {
3177 return NULL;
3180 state->clist = clist;
3181 state->reply_func = reply_func;
3183 for (i = 0; i < clist->num; i++) {
3184 request_func(&request, &clist->conn[i]);
3185 subreq = ctdb_client_control_send(state, ctdb->ev,
3186 ctdb->client, ctdb->cmd_pnn,
3187 TIMEOUT(), &request);
3188 if (tevent_req_nomem(subreq, req)) {
3189 return tevent_req_post(req, ctdb->ev);
3191 tevent_req_set_callback(subreq, process_clist_done, req);
3194 return req;
3197 static void process_clist_done(struct tevent_req *subreq)
3199 struct tevent_req *req = tevent_req_callback_data(
3200 subreq, struct tevent_req);
3201 struct process_clist_state *state = tevent_req_data(
3202 req, struct process_clist_state);
3203 struct ctdb_reply_control *reply;
3204 int ret;
3205 bool status;
3207 status = ctdb_client_control_recv(subreq, NULL, state, &reply);
3208 TALLOC_FREE(subreq);
3209 if (! status) {
3210 state->num_failed += 1;
3211 goto done;
3214 ret = state->reply_func(reply);
3215 if (ret != 0) {
3216 state->num_failed += 1;
3217 goto done;
3220 done:
3221 state->num_total += 1;
3222 if (state->num_total == state->clist->num) {
3223 tevent_req_done(req);
3227 static int process_clist_recv(struct tevent_req *req)
3229 struct process_clist_state *state = tevent_req_data(
3230 req, struct process_clist_state);
3232 return state->num_failed;
3235 static int control_addtickle(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3236 int argc, const char **argv)
3238 struct ctdb_connection conn;
3239 int ret;
3241 if (argc != 0 && argc != 2) {
3242 usage("addtickle");
3245 if (argc == 0) {
3246 struct ctdb_connection_list *clist;
3247 struct tevent_req *req;
3249 /* Client first but the src/dst logic is confused */
3250 ret = ctdb_connection_list_read(mem_ctx, false, &clist);
3251 if (ret != 0) {
3252 return ret;
3254 if (clist->num == 0) {
3255 return 0;
3258 req = process_clist_send(mem_ctx, ctdb, clist,
3259 ctdb_req_control_tcp_add_delayed_update,
3260 ctdb_reply_control_tcp_add_delayed_update);
3261 if (req == NULL) {
3262 talloc_free(clist);
3263 return ENOMEM;
3266 tevent_req_poll(req, ctdb->ev);
3267 talloc_free(clist);
3269 ret = process_clist_recv(req);
3270 if (ret != 0) {
3271 fprintf(stderr, "Failed to add %d tickles\n", ret);
3272 return 1;
3275 return 0;
3278 ret = ctdb_sock_addr_from_string(argv[0], &conn.src, true);
3279 if (ret != 0) {
3280 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3281 return 1;
3283 ret = ctdb_sock_addr_from_string(argv[1], &conn.dst, true);
3284 if (ret != 0) {
3285 fprintf(stderr, "Invalid IP address %s\n", argv[1]);
3286 return 1;
3289 ret = ctdb_ctrl_tcp_add_delayed_update(mem_ctx, ctdb->ev,
3290 ctdb->client, ctdb->cmd_pnn,
3291 TIMEOUT(), &conn);
3292 if (ret != 0) {
3293 fprintf(stderr, "Failed to register connection\n");
3294 return ret;
3297 return 0;
3300 static int control_deltickle(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3301 int argc, const char **argv)
3303 struct ctdb_connection conn;
3304 int ret;
3306 if (argc != 0 && argc != 2) {
3307 usage("deltickle");
3310 if (argc == 0) {
3311 struct ctdb_connection_list *clist;
3312 struct tevent_req *req;
3314 /* Client first but the src/dst logic is confused */
3315 ret = ctdb_connection_list_read(mem_ctx, false, &clist);
3316 if (ret != 0) {
3317 return ret;
3319 if (clist->num == 0) {
3320 return 0;
3323 req = process_clist_send(mem_ctx, ctdb, clist,
3324 ctdb_req_control_tcp_remove,
3325 ctdb_reply_control_tcp_remove);
3326 if (req == NULL) {
3327 talloc_free(clist);
3328 return ENOMEM;
3331 tevent_req_poll(req, ctdb->ev);
3332 talloc_free(clist);
3334 ret = process_clist_recv(req);
3335 if (ret != 0) {
3336 fprintf(stderr, "Failed to remove %d tickles\n", ret);
3337 return 1;
3340 return 0;
3343 ret = ctdb_sock_addr_from_string(argv[0], &conn.src, true);
3344 if (ret != 0) {
3345 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3346 return 1;
3348 ret = ctdb_sock_addr_from_string(argv[1], &conn.dst, true);
3349 if (ret != 0) {
3350 fprintf(stderr, "Invalid IP address %s\n", argv[1]);
3351 return 1;
3354 ret = ctdb_ctrl_tcp_remove(mem_ctx, ctdb->ev, ctdb->client,
3355 ctdb->cmd_pnn, TIMEOUT(), &conn);
3356 if (ret != 0) {
3357 fprintf(stderr, "Failed to unregister connection\n");
3358 return ret;
3361 return 0;
3364 static int control_listnodes(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3365 int argc, const char **argv)
3367 struct ctdb_node_map *nodemap;
3368 int i;
3370 if (argc != 0) {
3371 usage("listnodes");
3374 nodemap = read_nodes_file(mem_ctx, CTDB_UNKNOWN_PNN);
3375 if (nodemap == NULL) {
3376 return 1;
3379 for (i=0; i<nodemap->num; i++) {
3380 if (nodemap->node[i].flags & NODE_FLAGS_DELETED) {
3381 continue;
3384 if (options.machinereadable) {
3385 printf("%s%u%s%s%s\n", options.sep,
3386 nodemap->node[i].pnn, options.sep,
3387 ctdb_sock_addr_to_string(
3388 mem_ctx, &nodemap->node[i].addr, false),
3389 options.sep);
3390 } else {
3391 printf("%s\n",
3392 ctdb_sock_addr_to_string(
3393 mem_ctx, &nodemap->node[i].addr, false));
3397 return 0;
3400 static bool nodemap_identical(struct ctdb_node_map *nodemap1,
3401 struct ctdb_node_map *nodemap2)
3403 int i;
3405 if (nodemap1->num != nodemap2->num) {
3406 return false;
3409 for (i=0; i<nodemap1->num; i++) {
3410 struct ctdb_node_and_flags *n1, *n2;
3412 n1 = &nodemap1->node[i];
3413 n2 = &nodemap2->node[i];
3415 if ((n1->pnn != n2->pnn) ||
3416 (n1->flags != n2->flags) ||
3417 ! ctdb_sock_addr_same_ip(&n1->addr, &n2->addr)) {
3418 return false;
3422 return true;
3425 static int check_node_file_changes(TALLOC_CTX *mem_ctx,
3426 struct ctdb_node_map *nm,
3427 struct ctdb_node_map *fnm,
3428 bool *reload)
3430 int i;
3431 bool check_failed = false;
3433 *reload = false;
3435 for (i=0; i<nm->num; i++) {
3436 if (i >= fnm->num) {
3437 fprintf(stderr,
3438 "Node %u (%s) missing from nodes file\n",
3439 nm->node[i].pnn,
3440 ctdb_sock_addr_to_string(
3441 mem_ctx, &nm->node[i].addr, false));
3442 check_failed = true;
3443 continue;
3445 if (nm->node[i].flags & NODE_FLAGS_DELETED &&
3446 fnm->node[i].flags & NODE_FLAGS_DELETED) {
3447 /* Node remains deleted */
3448 continue;
3451 if (! (nm->node[i].flags & NODE_FLAGS_DELETED) &&
3452 ! (fnm->node[i].flags & NODE_FLAGS_DELETED)) {
3453 /* Node not newly nor previously deleted */
3454 if (! ctdb_same_ip(&nm->node[i].addr,
3455 &fnm->node[i].addr)) {
3456 fprintf(stderr,
3457 "Node %u has changed IP address"
3458 " (was %s, now %s)\n",
3459 nm->node[i].pnn,
3460 ctdb_sock_addr_to_string(
3461 mem_ctx,
3462 &nm->node[i].addr, false),
3463 ctdb_sock_addr_to_string(
3464 mem_ctx,
3465 &fnm->node[i].addr, false));
3466 check_failed = true;
3467 } else {
3468 if (nm->node[i].flags & NODE_FLAGS_DISCONNECTED) {
3469 fprintf(stderr,
3470 "WARNING: Node %u is disconnected."
3471 " You MUST fix this node manually!\n",
3472 nm->node[i].pnn);
3475 continue;
3478 if (fnm->node[i].flags & NODE_FLAGS_DELETED) {
3479 /* Node is being deleted */
3480 printf("Node %u is DELETED\n", nm->node[i].pnn);
3481 *reload = true;
3482 if (! (nm->node[i].flags & NODE_FLAGS_DISCONNECTED)) {
3483 fprintf(stderr,
3484 "ERROR: Node %u is still connected\n",
3485 nm->node[i].pnn);
3486 check_failed = true;
3488 continue;
3491 if (nm->node[i].flags & NODE_FLAGS_DELETED) {
3492 /* Node was previously deleted */
3493 printf("Node %u is UNDELETED\n", nm->node[i].pnn);
3494 *reload = true;
3498 if (check_failed) {
3499 fprintf(stderr,
3500 "ERROR: Nodes will not be reloaded due to previous error\n");
3501 return 1;
3504 /* Leftover nodes in file are NEW */
3505 for (; i < fnm->num; i++) {
3506 printf("Node %u is NEW\n", fnm->node[i].pnn);
3507 *reload = true;
3510 return 0;
3513 struct disable_recoveries_state {
3514 uint32_t *pnn_list;
3515 int node_count;
3516 bool *reply;
3517 int status;
3518 bool done;
3521 static void disable_recoveries_handler(uint64_t srvid, TDB_DATA data,
3522 void *private_data)
3524 struct disable_recoveries_state *state =
3525 (struct disable_recoveries_state *)private_data;
3526 int ret, i;
3528 if (data.dsize != sizeof(int)) {
3529 /* Ignore packet */
3530 return;
3533 /* ret will be a PNN (i.e. >=0) on success, or negative on error */
3534 ret = *(int *)data.dptr;
3535 if (ret < 0) {
3536 state->status = ret;
3537 state->done = true;
3538 return;
3540 for (i=0; i<state->node_count; i++) {
3541 if (state->pnn_list[i] == ret) {
3542 state->reply[i] = true;
3543 break;
3547 state->done = true;
3548 for (i=0; i<state->node_count; i++) {
3549 if (! state->reply[i]) {
3550 state->done = false;
3551 break;
3556 static int disable_recoveries(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3557 uint32_t timeout, uint32_t *pnn_list, int count)
3559 struct ctdb_disable_message disable = { 0 };
3560 struct disable_recoveries_state state;
3561 int ret, i;
3563 disable.pnn = ctdb->pnn;
3564 disable.srvid = next_srvid(ctdb);
3565 disable.timeout = timeout;
3567 state.pnn_list = pnn_list;
3568 state.node_count = count;
3569 state.done = false;
3570 state.status = 0;
3571 state.reply = talloc_zero_array(mem_ctx, bool, count);
3572 if (state.reply == NULL) {
3573 return ENOMEM;
3576 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
3577 disable.srvid,
3578 disable_recoveries_handler,
3579 &state);
3580 if (ret != 0) {
3581 return ret;
3584 for (i=0; i<count; i++) {
3585 ret = ctdb_message_disable_recoveries(mem_ctx, ctdb->ev,
3586 ctdb->client,
3587 pnn_list[i],
3588 &disable);
3589 if (ret != 0) {
3590 goto fail;
3594 ret = ctdb_client_wait_timeout(ctdb->ev, &state.done, TIMEOUT());
3595 if (ret == ETIME) {
3596 fprintf(stderr, "Timed out waiting to disable recoveries\n");
3597 } else {
3598 ret = (state.status >= 0 ? 0 : 1);
3601 fail:
3602 ctdb_client_remove_message_handler(ctdb->ev, ctdb->client,
3603 disable.srvid, &state);
3604 return ret;
3607 static int control_reloadnodes(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3608 int argc, const char **argv)
3610 struct ctdb_node_map *nodemap = NULL;
3611 struct ctdb_node_map *file_nodemap;
3612 struct ctdb_node_map *remote_nodemap;
3613 struct ctdb_req_control request;
3614 struct ctdb_reply_control **reply;
3615 bool reload;
3616 int ret, i;
3617 uint32_t *pnn_list;
3618 int count;
3620 nodemap = get_nodemap(ctdb, false);
3621 if (nodemap == NULL) {
3622 return 1;
3625 file_nodemap = read_nodes_file(mem_ctx, ctdb->pnn);
3626 if (file_nodemap == NULL) {
3627 return 1;
3630 for (i=0; i<nodemap->num; i++) {
3631 if (nodemap->node[i].flags & NODE_FLAGS_DISCONNECTED) {
3632 continue;
3635 ret = ctdb_ctrl_get_nodes_file(mem_ctx, ctdb->ev, ctdb->client,
3636 nodemap->node[i].pnn, TIMEOUT(),
3637 &remote_nodemap);
3638 if (ret != 0) {
3639 fprintf(stderr,
3640 "ERROR: Failed to get nodes file from node %u\n",
3641 nodemap->node[i].pnn);
3642 return ret;
3645 if (! nodemap_identical(file_nodemap, remote_nodemap)) {
3646 fprintf(stderr,
3647 "ERROR: Nodes file on node %u differs"
3648 " from current node (%u)\n",
3649 nodemap->node[i].pnn, ctdb->pnn);
3650 return 1;
3654 ret = check_node_file_changes(mem_ctx, nodemap, file_nodemap, &reload);
3655 if (ret != 0) {
3656 return ret;
3659 if (! reload) {
3660 fprintf(stderr, "No change in nodes file,"
3661 " skipping unnecessary reload\n");
3662 return 0;
3665 count = list_of_connected_nodes(nodemap, CTDB_UNKNOWN_PNN,
3666 mem_ctx, &pnn_list);
3667 if (count <= 0) {
3668 fprintf(stderr, "Memory allocation error\n");
3669 return 1;
3672 ret = disable_recoveries(mem_ctx, ctdb, 2*options.timelimit,
3673 pnn_list, count);
3674 if (ret != 0) {
3675 fprintf(stderr, "Failed to disable recoveries\n");
3676 return ret;
3679 ctdb_req_control_reload_nodes_file(&request);
3680 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
3681 pnn_list, count, TIMEOUT(),
3682 &request, NULL, &reply);
3683 if (ret != 0) {
3684 bool failed = false;
3686 for (i=0; i<count; i++) {
3687 ret = ctdb_reply_control_reload_nodes_file(reply[i]);
3688 if (ret != 0) {
3689 fprintf(stderr,
3690 "Node %u failed to reload nodes\n",
3691 pnn_list[i]);
3692 failed = true;
3695 if (failed) {
3696 fprintf(stderr,
3697 "You MUST fix failed nodes manually!\n");
3701 ret = disable_recoveries(mem_ctx, ctdb, 0, pnn_list, count);
3702 if (ret != 0) {
3703 fprintf(stderr, "Failed to enable recoveries\n");
3704 return ret;
3707 return 0;
3710 static int moveip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3711 ctdb_sock_addr *addr, uint32_t pnn)
3713 struct ctdb_public_ip_list *pubip_list;
3714 struct ctdb_public_ip pubip;
3715 struct ctdb_node_map *nodemap;
3716 struct ctdb_req_control request;
3717 uint32_t *pnn_list;
3718 int ret, i, count;
3720 ret = ctdb_message_disable_ip_check(mem_ctx, ctdb->ev, ctdb->client,
3721 CTDB_BROADCAST_CONNECTED,
3722 2*options.timelimit);
3723 if (ret != 0) {
3724 fprintf(stderr, "Failed to disable IP check\n");
3725 return ret;
3728 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
3729 pnn, TIMEOUT(), false, &pubip_list);
3730 if (ret != 0) {
3731 fprintf(stderr, "Failed to get Public IPs from node %u\n",
3732 pnn);
3733 return ret;
3736 for (i=0; i<pubip_list->num; i++) {
3737 if (ctdb_same_ip(addr, &pubip_list->ip[i].addr)) {
3738 break;
3742 if (i == pubip_list->num) {
3743 fprintf(stderr, "Node %u CANNOT host IP address %s\n",
3744 pnn, ctdb_sock_addr_to_string(mem_ctx, addr, false));
3745 return 1;
3748 nodemap = get_nodemap(ctdb, false);
3749 if (nodemap == NULL) {
3750 return 1;
3753 count = list_of_active_nodes(nodemap, pnn, mem_ctx, &pnn_list);
3754 if (count <= 0) {
3755 fprintf(stderr, "Memory allocation error\n");
3756 return 1;
3759 pubip.pnn = pnn;
3760 pubip.addr = *addr;
3761 ctdb_req_control_release_ip(&request, &pubip);
3763 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
3764 pnn_list, count, TIMEOUT(),
3765 &request, NULL, NULL);
3766 if (ret != 0) {
3767 fprintf(stderr, "Failed to release IP on nodes\n");
3768 return ret;
3771 ret = ctdb_ctrl_takeover_ip(mem_ctx, ctdb->ev, ctdb->client,
3772 pnn, TIMEOUT(), &pubip);
3773 if (ret != 0) {
3774 fprintf(stderr, "Failed to takeover IP on node %u\n", pnn);
3775 return ret;
3778 return 0;
3781 static int control_moveip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3782 int argc, const char **argv)
3784 ctdb_sock_addr addr;
3785 uint32_t pnn;
3786 int ret, retries = 0;
3788 if (argc != 2) {
3789 usage("moveip");
3792 ret = ctdb_sock_addr_from_string(argv[0], &addr, false);
3793 if (ret != 0) {
3794 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3795 return 1;
3798 pnn = strtoul(argv[1], NULL, 10);
3799 if (pnn == CTDB_UNKNOWN_PNN) {
3800 fprintf(stderr, "Invalid PNN %s\n", argv[1]);
3801 return 1;
3804 while (retries < 5) {
3805 ret = moveip(mem_ctx, ctdb, &addr, pnn);
3806 if (ret == 0) {
3807 break;
3810 sleep(3);
3811 retries++;
3814 if (ret != 0) {
3815 fprintf(stderr, "Failed to move IP %s to node %u\n",
3816 argv[0], pnn);
3817 return ret;
3820 return 0;
3823 static int rebalancenode(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3824 uint32_t pnn)
3826 int ret;
3828 ret = ctdb_message_rebalance_node(mem_ctx, ctdb->ev, ctdb->client,
3829 CTDB_BROADCAST_CONNECTED, pnn);
3830 if (ret != 0) {
3831 fprintf(stderr,
3832 "Failed to ask recovery master to distribute IPs\n");
3833 return ret;
3836 return 0;
3839 static int control_addip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3840 int argc, const char **argv)
3842 ctdb_sock_addr addr;
3843 struct ctdb_public_ip_list *pubip_list;
3844 struct ctdb_addr_info addr_info;
3845 unsigned int mask;
3846 int ret, i, retries = 0;
3848 if (argc != 2) {
3849 usage("addip");
3852 if (! parse_ip_mask(argv[0], argv[1], &addr, &mask)) {
3853 fprintf(stderr, "Invalid IP/Mask %s\n", argv[0]);
3854 return 1;
3857 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
3858 ctdb->cmd_pnn, TIMEOUT(),
3859 false, &pubip_list);
3860 if (ret != 0) {
3861 fprintf(stderr, "Failed to get Public IPs from node %u\n",
3862 ctdb->cmd_pnn);
3863 return 1;
3866 for (i=0; i<pubip_list->num; i++) {
3867 if (ctdb_same_ip(&addr, &pubip_list->ip[i].addr)) {
3868 fprintf(stderr, "Node already knows about IP %s\n",
3869 ctdb_sock_addr_to_string(mem_ctx,
3870 &addr, false));
3871 return 0;
3875 addr_info.addr = addr;
3876 addr_info.mask = mask;
3877 addr_info.iface = argv[1];
3879 while (retries < 5) {
3880 ret = ctdb_ctrl_add_public_ip(mem_ctx, ctdb->ev, ctdb->client,
3881 ctdb->cmd_pnn, TIMEOUT(),
3882 &addr_info);
3883 if (ret == 0) {
3884 break;
3887 sleep(3);
3888 retries++;
3891 if (ret != 0) {
3892 fprintf(stderr, "Failed to add public IP to node %u."
3893 " Giving up\n", ctdb->cmd_pnn);
3894 return ret;
3897 ret = rebalancenode(mem_ctx, ctdb, ctdb->cmd_pnn);
3898 if (ret != 0) {
3899 return ret;
3902 return 0;
3905 static int control_delip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3906 int argc, const char **argv)
3908 ctdb_sock_addr addr;
3909 struct ctdb_public_ip_list *pubip_list;
3910 struct ctdb_addr_info addr_info;
3911 int ret, i;
3913 if (argc != 1) {
3914 usage("delip");
3917 ret = ctdb_sock_addr_from_string(argv[0], &addr, false);
3918 if (ret != 0) {
3919 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3920 return 1;
3923 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
3924 ctdb->cmd_pnn, TIMEOUT(),
3925 false, &pubip_list);
3926 if (ret != 0) {
3927 fprintf(stderr, "Failed to get Public IPs from node %u\n",
3928 ctdb->cmd_pnn);
3929 return 1;
3932 for (i=0; i<pubip_list->num; i++) {
3933 if (ctdb_same_ip(&addr, &pubip_list->ip[i].addr)) {
3934 break;
3938 if (i == pubip_list->num) {
3939 fprintf(stderr, "Node does not know about IP address %s\n",
3940 ctdb_sock_addr_to_string(mem_ctx, &addr, false));
3941 return 0;
3944 addr_info.addr = addr;
3945 addr_info.mask = 0;
3946 addr_info.iface = NULL;
3948 ret = ctdb_ctrl_del_public_ip(mem_ctx, ctdb->ev, ctdb->client,
3949 ctdb->cmd_pnn, TIMEOUT(), &addr_info);
3950 if (ret != 0) {
3951 fprintf(stderr, "Failed to delete public IP from node %u\n",
3952 ctdb->cmd_pnn);
3953 return ret;
3956 return 0;
3959 #define DB_VERSION 3
3960 #define MAX_DB_NAME 64
3961 #define MAX_REC_BUFFER_SIZE (100*1000)
3963 struct db_header {
3964 unsigned long version;
3965 time_t timestamp;
3966 unsigned long flags;
3967 unsigned long nbuf;
3968 unsigned long nrec;
3969 char name[MAX_DB_NAME];
3972 struct backup_state {
3973 TALLOC_CTX *mem_ctx;
3974 struct ctdb_rec_buffer *recbuf;
3975 uint32_t db_id;
3976 int fd;
3977 unsigned int nbuf, nrec;
3980 static int backup_handler(uint32_t reqid, struct ctdb_ltdb_header *header,
3981 TDB_DATA key, TDB_DATA data, void *private_data)
3983 struct backup_state *state = (struct backup_state *)private_data;
3984 size_t len;
3985 int ret;
3987 if (state->recbuf == NULL) {
3988 state->recbuf = ctdb_rec_buffer_init(state->mem_ctx,
3989 state->db_id);
3990 if (state->recbuf == NULL) {
3991 return ENOMEM;
3995 ret = ctdb_rec_buffer_add(state->recbuf, state->recbuf, reqid,
3996 header, key, data);
3997 if (ret != 0) {
3998 return ret;
4001 len = ctdb_rec_buffer_len(state->recbuf);
4002 if (len < MAX_REC_BUFFER_SIZE) {
4003 return 0;
4006 ret = ctdb_rec_buffer_write(state->recbuf, state->fd);
4007 if (ret != 0) {
4008 fprintf(stderr, "Failed to write records to backup file\n");
4009 return ret;
4012 state->nbuf += 1;
4013 state->nrec += state->recbuf->count;
4014 TALLOC_FREE(state->recbuf);
4016 return 0;
4019 static int control_backupdb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4020 int argc, const char **argv)
4022 const char *db_name;
4023 struct ctdb_db_context *db;
4024 uint32_t db_id;
4025 uint8_t db_flags;
4026 struct backup_state state;
4027 struct db_header db_hdr;
4028 int fd, ret;
4030 if (argc != 2) {
4031 usage("backupdb");
4034 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
4035 return 1;
4038 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4039 db_flags, &db);
4040 if (ret != 0) {
4041 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4042 return ret;
4045 fd = open(argv[1], O_RDWR|O_CREAT, 0600);
4046 if (fd == -1) {
4047 ret = errno;
4048 fprintf(stderr, "Failed to open file %s for writing\n",
4049 argv[1]);
4050 return ret;
4053 /* Write empty header first */
4054 ZERO_STRUCT(db_hdr);
4055 ret = write(fd, &db_hdr, sizeof(struct db_header));
4056 if (ret == -1) {
4057 ret = errno;
4058 close(fd);
4059 fprintf(stderr, "Failed to write header to file %s\n", argv[1]);
4060 return ret;
4063 state.mem_ctx = mem_ctx;
4064 state.recbuf = NULL;
4065 state.fd = fd;
4066 state.nbuf = 0;
4067 state.nrec = 0;
4069 ret = ctdb_db_traverse_local(db, true, false, backup_handler, &state);
4070 if (ret != 0) {
4071 fprintf(stderr, "Failed to collect records from DB %s\n",
4072 db_name);
4073 close(fd);
4074 return ret;
4077 if (state.recbuf != NULL) {
4078 ret = ctdb_rec_buffer_write(state.recbuf, state.fd);
4079 if (ret != 0) {
4080 fprintf(stderr,
4081 "Failed to write records to backup file\n");
4082 close(fd);
4083 return ret;
4086 state.nbuf += 1;
4087 state.nrec += state.recbuf->count;
4088 TALLOC_FREE(state.recbuf);
4091 db_hdr.version = DB_VERSION;
4092 db_hdr.timestamp = time(NULL);
4093 db_hdr.flags = db_flags;
4094 db_hdr.nbuf = state.nbuf;
4095 db_hdr.nrec = state.nrec;
4096 strncpy(db_hdr.name, db_name, MAX_DB_NAME-1);
4098 lseek(fd, 0, SEEK_SET);
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 close(fd);
4108 printf("Database backed up to %s\n", argv[1]);
4109 return 0;
4112 static int control_restoredb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4113 int argc, const char **argv)
4115 const char *db_name = NULL;
4116 struct ctdb_db_context *db;
4117 struct db_header db_hdr;
4118 struct ctdb_node_map *nodemap;
4119 struct ctdb_req_control request;
4120 struct ctdb_reply_control **reply;
4121 struct ctdb_transdb wipedb;
4122 struct ctdb_pulldb_ext pulldb;
4123 struct ctdb_rec_buffer *recbuf;
4124 uint32_t generation;
4125 uint32_t *pnn_list;
4126 char timebuf[128];
4127 ssize_t n;
4128 int fd, i;
4129 int count, ret;
4130 uint8_t db_flags;
4132 if (argc < 1 || argc > 2) {
4133 usage("restoredb");
4136 fd = open(argv[0], O_RDONLY, 0600);
4137 if (fd == -1) {
4138 ret = errno;
4139 fprintf(stderr, "Failed to open file %s for reading\n",
4140 argv[0]);
4141 return ret;
4144 if (argc == 2) {
4145 db_name = argv[1];
4148 n = read(fd, &db_hdr, sizeof(struct db_header));
4149 if (n == -1) {
4150 ret = errno;
4151 close(fd);
4152 fprintf(stderr, "Failed to read db header from file %s\n",
4153 argv[0]);
4154 return ret;
4156 db_hdr.name[sizeof(db_hdr.name)-1] = '\0';
4158 if (db_hdr.version != DB_VERSION) {
4159 fprintf(stderr,
4160 "Wrong version of backup file, expected %u, got %lu\n",
4161 DB_VERSION, db_hdr.version);
4162 close(fd);
4163 return EINVAL;
4166 if (db_name == NULL) {
4167 db_name = db_hdr.name;
4170 strftime(timebuf, sizeof(timebuf)-1, "%Y/%m/%d %H:%M:%S",
4171 localtime(&db_hdr.timestamp));
4172 printf("Restoring database %s from backup @ %s\n", db_name, timebuf);
4174 db_flags = db_hdr.flags & 0xff;
4175 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4176 db_flags, &db);
4177 if (ret != 0) {
4178 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4179 close(fd);
4180 return ret;
4183 nodemap = get_nodemap(ctdb, false);
4184 if (nodemap == NULL) {
4185 fprintf(stderr, "Failed to get nodemap\n");
4186 close(fd);
4187 return ENOMEM;
4190 ret = get_generation(mem_ctx, ctdb, &generation);
4191 if (ret != 0) {
4192 fprintf(stderr, "Failed to get current generation\n");
4193 close(fd);
4194 return ret;
4197 count = list_of_active_nodes(nodemap, CTDB_UNKNOWN_PNN, mem_ctx,
4198 &pnn_list);
4199 if (count <= 0) {
4200 close(fd);
4201 return ENOMEM;
4204 wipedb.db_id = ctdb_db_id(db);
4205 wipedb.tid = generation;
4207 ctdb_req_control_db_freeze(&request, wipedb.db_id);
4208 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4209 ctdb->client, pnn_list, count,
4210 TIMEOUT(), &request, NULL, NULL);
4211 if (ret != 0) {
4212 goto failed;
4216 ctdb_req_control_db_transaction_start(&request, &wipedb);
4217 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4218 pnn_list, count, TIMEOUT(),
4219 &request, NULL, NULL);
4220 if (ret != 0) {
4221 goto failed;
4224 ctdb_req_control_wipe_database(&request, &wipedb);
4225 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4226 pnn_list, count, TIMEOUT(),
4227 &request, NULL, NULL);
4228 if (ret != 0) {
4229 goto failed;
4232 pulldb.db_id = ctdb_db_id(db);
4233 pulldb.lmaster = 0;
4234 pulldb.srvid = SRVID_CTDB_PUSHDB;
4236 ctdb_req_control_db_push_start(&request, &pulldb);
4237 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4238 pnn_list, count, TIMEOUT(),
4239 &request, NULL, NULL);
4240 if (ret != 0) {
4241 goto failed;
4244 for (i=0; i<db_hdr.nbuf; i++) {
4245 struct ctdb_req_message message;
4246 TDB_DATA data;
4247 size_t np;
4249 ret = ctdb_rec_buffer_read(fd, mem_ctx, &recbuf);
4250 if (ret != 0) {
4251 goto failed;
4254 data.dsize = ctdb_rec_buffer_len(recbuf);
4255 data.dptr = talloc_size(mem_ctx, data.dsize);
4256 if (data.dptr == NULL) {
4257 goto failed;
4260 ctdb_rec_buffer_push(recbuf, data.dptr, &np);
4262 message.srvid = pulldb.srvid;
4263 message.data.data = data;
4265 ret = ctdb_client_message_multi(mem_ctx, ctdb->ev,
4266 ctdb->client,
4267 pnn_list, count,
4268 &message, NULL);
4269 if (ret != 0) {
4270 goto failed;
4273 talloc_free(recbuf);
4274 talloc_free(data.dptr);
4277 ctdb_req_control_db_push_confirm(&request, pulldb.db_id);
4278 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4279 pnn_list, count, TIMEOUT(),
4280 &request, NULL, &reply);
4281 if (ret != 0) {
4282 goto failed;
4285 for (i=0; i<count; i++) {
4286 uint32_t num_records;
4288 ret = ctdb_reply_control_db_push_confirm(reply[i],
4289 &num_records);
4290 if (ret != 0) {
4291 fprintf(stderr, "Invalid response from node %u\n",
4292 pnn_list[i]);
4293 goto failed;
4296 if (num_records != db_hdr.nrec) {
4297 fprintf(stderr, "Node %u received %u of %lu records\n",
4298 pnn_list[i], num_records, db_hdr.nrec);
4299 goto failed;
4303 ctdb_req_control_db_set_healthy(&request, wipedb.db_id);
4304 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4305 pnn_list, count, TIMEOUT(),
4306 &request, NULL, NULL);
4307 if (ret != 0) {
4308 goto failed;
4311 ctdb_req_control_db_transaction_commit(&request, &wipedb);
4312 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4313 pnn_list, count, TIMEOUT(),
4314 &request, NULL, NULL);
4315 if (ret != 0) {
4316 goto failed;
4319 ctdb_req_control_db_thaw(&request, wipedb.db_id);
4320 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4321 ctdb->client, pnn_list, count,
4322 TIMEOUT(), &request, NULL, NULL);
4323 if (ret != 0) {
4324 goto failed;
4327 printf("Database %s restored\n", db_name);
4328 close(fd);
4329 return 0;
4332 failed:
4333 close(fd);
4334 ctdb_ctrl_set_recmode(mem_ctx, ctdb->ev, ctdb->client,
4335 ctdb->pnn, TIMEOUT(), CTDB_RECOVERY_ACTIVE);
4336 return ret;
4339 struct dumpdbbackup_state {
4340 ctdb_rec_parser_func_t parser;
4341 struct dump_record_state sub_state;
4344 static int dumpdbbackup_handler(uint32_t reqid,
4345 struct ctdb_ltdb_header *header,
4346 TDB_DATA key, TDB_DATA data,
4347 void *private_data)
4349 struct dumpdbbackup_state *state =
4350 (struct dumpdbbackup_state *)private_data;
4351 struct ctdb_ltdb_header hdr;
4352 int ret;
4354 ret = ctdb_ltdb_header_extract(&data, &hdr);
4355 if (ret != 0) {
4356 return ret;
4359 return state->parser(reqid, &hdr, key, data, &state->sub_state);
4362 static int control_dumpdbbackup(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4363 int argc, const char **argv)
4365 struct db_header db_hdr;
4366 char timebuf[128];
4367 struct dumpdbbackup_state state;
4368 ssize_t n;
4369 int fd, ret, i;
4371 if (argc != 1) {
4372 usage("dumpbackup");
4375 fd = open(argv[0], O_RDONLY, 0600);
4376 if (fd == -1) {
4377 ret = errno;
4378 fprintf(stderr, "Failed to open file %s for reading\n",
4379 argv[0]);
4380 return ret;
4383 n = read(fd, &db_hdr, sizeof(struct db_header));
4384 if (n == -1) {
4385 ret = errno;
4386 close(fd);
4387 fprintf(stderr, "Failed to read db header from file %s\n",
4388 argv[0]);
4389 return ret;
4391 db_hdr.name[sizeof(db_hdr.name)-1] = '\0';
4393 if (db_hdr.version != DB_VERSION) {
4394 fprintf(stderr,
4395 "Wrong version of backup file, expected %u, got %lu\n",
4396 DB_VERSION, db_hdr.version);
4397 close(fd);
4398 return EINVAL;
4401 strftime(timebuf, sizeof(timebuf)-1, "%Y/%m/%d %H:%M:%S",
4402 localtime(&db_hdr.timestamp));
4403 printf("Dumping database %s from backup @ %s\n",
4404 db_hdr.name, timebuf);
4406 state.parser = dump_record;
4407 state.sub_state.count = 0;
4409 for (i=0; i<db_hdr.nbuf; i++) {
4410 struct ctdb_rec_buffer *recbuf;
4412 ret = ctdb_rec_buffer_read(fd, mem_ctx, &recbuf);
4413 if (ret != 0) {
4414 fprintf(stderr, "Failed to read records\n");
4415 close(fd);
4416 return ret;
4419 ret = ctdb_rec_buffer_traverse(recbuf, dumpdbbackup_handler,
4420 &state);
4421 if (ret != 0) {
4422 fprintf(stderr, "Failed to dump records\n");
4423 close(fd);
4424 return ret;
4428 close(fd);
4429 printf("Dumped %u record(s)\n", state.sub_state.count);
4430 return 0;
4433 static int control_wipedb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4434 int argc, const char **argv)
4436 const char *db_name;
4437 struct ctdb_db_context *db;
4438 uint32_t db_id;
4439 uint8_t db_flags;
4440 struct ctdb_node_map *nodemap;
4441 struct ctdb_req_control request;
4442 struct ctdb_transdb wipedb;
4443 uint32_t generation;
4444 uint32_t *pnn_list;
4445 int count, ret;
4447 if (argc != 1) {
4448 usage("wipedb");
4451 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
4452 return 1;
4455 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4456 db_flags, &db);
4457 if (ret != 0) {
4458 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4459 return ret;
4462 nodemap = get_nodemap(ctdb, false);
4463 if (nodemap == NULL) {
4464 fprintf(stderr, "Failed to get nodemap\n");
4465 return ENOMEM;
4468 ret = get_generation(mem_ctx, ctdb, &generation);
4469 if (ret != 0) {
4470 fprintf(stderr, "Failed to get current generation\n");
4471 return ret;
4474 count = list_of_active_nodes(nodemap, CTDB_UNKNOWN_PNN, mem_ctx,
4475 &pnn_list);
4476 if (count <= 0) {
4477 return ENOMEM;
4480 ctdb_req_control_db_freeze(&request, db_id);
4481 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4482 ctdb->client, pnn_list, count,
4483 TIMEOUT(), &request, NULL, NULL);
4484 if (ret != 0) {
4485 goto failed;
4488 wipedb.db_id = db_id;
4489 wipedb.tid = generation;
4491 ctdb_req_control_db_transaction_start(&request, &wipedb);
4492 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4493 pnn_list, count, TIMEOUT(),
4494 &request, NULL, NULL);
4495 if (ret != 0) {
4496 goto failed;
4499 ctdb_req_control_wipe_database(&request, &wipedb);
4500 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4501 pnn_list, count, TIMEOUT(),
4502 &request, NULL, NULL);
4503 if (ret != 0) {
4504 goto failed;
4507 ctdb_req_control_db_set_healthy(&request, db_id);
4508 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4509 pnn_list, count, TIMEOUT(),
4510 &request, NULL, NULL);
4511 if (ret != 0) {
4512 goto failed;
4515 ctdb_req_control_db_transaction_commit(&request, &wipedb);
4516 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4517 pnn_list, count, TIMEOUT(),
4518 &request, NULL, NULL);
4519 if (ret != 0) {
4520 goto failed;
4523 ctdb_req_control_db_thaw(&request, db_id);
4524 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4525 ctdb->client, pnn_list, count,
4526 TIMEOUT(), &request, NULL, NULL);
4527 if (ret != 0) {
4528 goto failed;
4531 printf("Database %s wiped\n", db_name);
4532 return 0;
4535 failed:
4536 ctdb_ctrl_set_recmode(mem_ctx, ctdb->ev, ctdb->client,
4537 ctdb->pnn, TIMEOUT(), CTDB_RECOVERY_ACTIVE);
4538 return ret;
4541 static int control_recmaster(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4542 int argc, const char **argv)
4544 uint32_t recmaster;
4545 int ret;
4547 ret = ctdb_ctrl_get_recmaster(mem_ctx, ctdb->ev, ctdb->client,
4548 ctdb->cmd_pnn, TIMEOUT(), &recmaster);
4549 if (ret != 0) {
4550 return ret;
4553 printf("%u\n", recmaster);
4554 return 0;
4557 static int control_event(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4558 int argc, const char **argv)
4560 char *t, *event_helper = NULL;
4561 char *eventd_socket = NULL;
4562 const char **new_argv;
4563 int i;
4565 t = getenv("CTDB_EVENT_HELPER");
4566 if (t != NULL) {
4567 event_helper = talloc_strdup(mem_ctx, t);
4568 } else {
4569 event_helper = talloc_asprintf(mem_ctx, "%s/ctdb_event",
4570 CTDB_HELPER_BINDIR);
4573 if (event_helper == NULL) {
4574 fprintf(stderr, "Unable to set event daemon helper\n");
4575 return 1;
4578 t = getenv("CTDB_SOCKET");
4579 if (t != NULL) {
4580 eventd_socket = talloc_asprintf(mem_ctx, "%s/eventd.sock",
4581 dirname(t));
4582 } else {
4583 eventd_socket = talloc_asprintf(mem_ctx, "%s/eventd.sock",
4584 CTDB_RUNDIR);
4587 if (eventd_socket == NULL) {
4588 fprintf(stderr, "Unable to set event daemon socket\n");
4589 return 1;
4592 new_argv = talloc_array(mem_ctx, const char *, argc + 1);
4593 if (new_argv == NULL) {
4594 fprintf(stderr, "Memory allocation error\n");
4595 return 1;
4598 new_argv[0] = eventd_socket;
4599 for (i=0; i<argc; i++) {
4600 new_argv[i+1] = argv[i];
4603 return run_helper(mem_ctx, "event daemon helper", event_helper,
4604 argc+1, new_argv);
4607 static int control_scriptstatus(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4608 int argc, const char **argv)
4610 const char *new_argv[3];
4612 if (argc > 1) {
4613 usage("scriptstatus");
4616 new_argv[0] = "status";
4617 new_argv[1] = (argc == 0) ? "monitor" : argv[0];
4618 new_argv[2] = NULL;
4620 (void) control_event(mem_ctx, ctdb, 2, new_argv);
4621 return 0;
4624 static int control_natgw(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4625 int argc, const char **argv)
4627 char *t, *natgw_helper = NULL;
4629 if (argc != 1) {
4630 usage("natgw");
4633 t = getenv("CTDB_NATGW_HELPER");
4634 if (t != NULL) {
4635 natgw_helper = talloc_strdup(mem_ctx, t);
4636 } else {
4637 natgw_helper = talloc_asprintf(mem_ctx, "%s/ctdb_natgw",
4638 CTDB_HELPER_BINDIR);
4641 if (natgw_helper == NULL) {
4642 fprintf(stderr, "Unable to set NAT gateway helper\n");
4643 return 1;
4646 return run_helper(mem_ctx, "NAT gateway helper", natgw_helper,
4647 argc, argv);
4651 * Find the PNN of the current node
4652 * discover the pnn by loading the nodes file and try to bind
4653 * to all addresses one at a time until the ip address is found.
4655 static bool find_node_xpnn(TALLOC_CTX *mem_ctx, uint32_t *pnn)
4657 struct ctdb_node_map *nodemap;
4658 int i;
4660 nodemap = read_nodes_file(mem_ctx, CTDB_UNKNOWN_PNN);
4661 if (nodemap == NULL) {
4662 return false;
4665 for (i=0; i<nodemap->num; i++) {
4666 if (nodemap->node[i].flags & NODE_FLAGS_DELETED) {
4667 continue;
4669 if (ctdb_sys_have_ip(&nodemap->node[i].addr)) {
4670 if (pnn != NULL) {
4671 *pnn = nodemap->node[i].pnn;
4673 talloc_free(nodemap);
4674 return true;
4678 fprintf(stderr, "Failed to detect PNN of the current node.\n");
4679 talloc_free(nodemap);
4680 return false;
4683 static int control_getreclock(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4684 int argc, const char **argv)
4686 const char *reclock;
4687 int ret;
4689 if (argc != 0) {
4690 usage("getreclock");
4693 ret = ctdb_ctrl_get_reclock_file(mem_ctx, ctdb->ev, ctdb->client,
4694 ctdb->cmd_pnn, TIMEOUT(), &reclock);
4695 if (ret != 0) {
4696 return ret;
4699 if (reclock != NULL) {
4700 printf("%s\n", reclock);
4703 return 0;
4706 static int control_setlmasterrole(TALLOC_CTX *mem_ctx,
4707 struct ctdb_context *ctdb,
4708 int argc, const char **argv)
4710 uint32_t lmasterrole = 0;
4711 int ret;
4713 if (argc != 1) {
4714 usage("setlmasterrole");
4717 if (strcmp(argv[0], "on") == 0) {
4718 lmasterrole = 1;
4719 } else if (strcmp(argv[0], "off") == 0) {
4720 lmasterrole = 0;
4721 } else {
4722 usage("setlmasterrole");
4725 ret = ctdb_ctrl_set_lmasterrole(mem_ctx, ctdb->ev, ctdb->client,
4726 ctdb->cmd_pnn, TIMEOUT(), lmasterrole);
4727 if (ret != 0) {
4728 return ret;
4731 return 0;
4734 static int control_setrecmasterrole(TALLOC_CTX *mem_ctx,
4735 struct ctdb_context *ctdb,
4736 int argc, const char **argv)
4738 uint32_t recmasterrole = 0;
4739 int ret;
4741 if (argc != 1) {
4742 usage("setrecmasterrole");
4745 if (strcmp(argv[0], "on") == 0) {
4746 recmasterrole = 1;
4747 } else if (strcmp(argv[0], "off") == 0) {
4748 recmasterrole = 0;
4749 } else {
4750 usage("setrecmasterrole");
4753 ret = ctdb_ctrl_set_recmasterrole(mem_ctx, ctdb->ev, ctdb->client,
4754 ctdb->cmd_pnn, TIMEOUT(),
4755 recmasterrole);
4756 if (ret != 0) {
4757 return ret;
4760 return 0;
4763 static int control_setdbreadonly(TALLOC_CTX *mem_ctx,
4764 struct ctdb_context *ctdb,
4765 int argc, const char **argv)
4767 uint32_t db_id;
4768 uint8_t db_flags;
4769 int ret;
4771 if (argc != 1) {
4772 usage("setdbreadonly");
4775 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, NULL, &db_flags)) {
4776 return 1;
4779 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
4780 fprintf(stderr, "READONLY can be set only on volatile DB\n");
4781 return 1;
4784 ret = ctdb_ctrl_set_db_readonly(mem_ctx, ctdb->ev, ctdb->client,
4785 ctdb->cmd_pnn, TIMEOUT(), db_id);
4786 if (ret != 0) {
4787 return ret;
4790 return 0;
4793 static int control_setdbsticky(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4794 int argc, const char **argv)
4796 uint32_t db_id;
4797 uint8_t db_flags;
4798 int ret;
4800 if (argc != 1) {
4801 usage("setdbsticky");
4804 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, NULL, &db_flags)) {
4805 return 1;
4808 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
4809 fprintf(stderr, "STICKY can be set only on volatile DB\n");
4810 return 1;
4813 ret = ctdb_ctrl_set_db_sticky(mem_ctx, ctdb->ev, ctdb->client,
4814 ctdb->cmd_pnn, TIMEOUT(), db_id);
4815 if (ret != 0) {
4816 return ret;
4819 return 0;
4822 static int control_pfetch(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4823 int argc, const char **argv)
4825 const char *db_name;
4826 struct ctdb_db_context *db;
4827 struct ctdb_transaction_handle *h;
4828 uint8_t db_flags;
4829 TDB_DATA key, data;
4830 int ret;
4832 if (argc < 2 || argc > 3) {
4833 usage("pfetch");
4836 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
4837 return 1;
4840 if (! (db_flags &
4841 (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED))) {
4842 fprintf(stderr, "Transactions not supported on DB %s\n",
4843 db_name);
4844 return 1;
4847 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4848 db_flags, &db);
4849 if (ret != 0) {
4850 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4851 return ret;
4854 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
4855 if (ret != 0) {
4856 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
4857 return ret;
4860 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
4861 TIMEOUT(), db, true, &h);
4862 if (ret != 0) {
4863 fprintf(stderr, "Failed to start transaction on db %s\n",
4864 db_name);
4865 return ret;
4868 ret = ctdb_transaction_fetch_record(h, key, mem_ctx, &data);
4869 if (ret != 0) {
4870 fprintf(stderr, "Failed to read record for key %s\n",
4871 argv[1]);
4872 ctdb_transaction_cancel(h);
4873 return ret;
4876 printf("%.*s\n", (int)data.dsize, data.dptr);
4878 ctdb_transaction_cancel(h);
4879 return 0;
4882 static int control_pstore(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4883 int argc, const char **argv)
4885 const char *db_name;
4886 struct ctdb_db_context *db;
4887 struct ctdb_transaction_handle *h;
4888 uint8_t db_flags;
4889 TDB_DATA key, data;
4890 int ret;
4892 if (argc != 3) {
4893 usage("pstore");
4896 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
4897 return 1;
4900 if (! (db_flags &
4901 (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED))) {
4902 fprintf(stderr, "Transactions not supported on DB %s\n",
4903 db_name);
4904 return 1;
4907 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4908 db_flags, &db);
4909 if (ret != 0) {
4910 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4911 return ret;
4914 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
4915 if (ret != 0) {
4916 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
4917 return ret;
4920 ret = str_to_data(argv[2], strlen(argv[2]), mem_ctx, &data);
4921 if (ret != 0) {
4922 fprintf(stderr, "Failed to parse value %s\n", argv[2]);
4923 return ret;
4926 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
4927 TIMEOUT(), db, false, &h);
4928 if (ret != 0) {
4929 fprintf(stderr, "Failed to start transaction on db %s\n",
4930 db_name);
4931 return ret;
4934 ret = ctdb_transaction_store_record(h, key, data);
4935 if (ret != 0) {
4936 fprintf(stderr, "Failed to store record for key %s\n",
4937 argv[1]);
4938 ctdb_transaction_cancel(h);
4939 return ret;
4942 ret = ctdb_transaction_commit(h);
4943 if (ret != 0) {
4944 fprintf(stderr, "Failed to commit transaction on db %s\n",
4945 db_name);
4946 ctdb_transaction_cancel(h);
4947 return ret;
4950 return 0;
4953 static int control_pdelete(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4954 int argc, const char **argv)
4956 const char *db_name;
4957 struct ctdb_db_context *db;
4958 struct ctdb_transaction_handle *h;
4959 uint8_t db_flags;
4960 TDB_DATA key;
4961 int ret;
4963 if (argc != 2) {
4964 usage("pdelete");
4967 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
4968 return 1;
4971 if (! (db_flags &
4972 (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED))) {
4973 fprintf(stderr, "Transactions not supported on DB %s\n",
4974 db_name);
4975 return 1;
4978 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4979 db_flags, &db);
4980 if (ret != 0) {
4981 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4982 return ret;
4985 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
4986 if (ret != 0) {
4987 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
4988 return ret;
4991 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
4992 TIMEOUT(), db, false, &h);
4993 if (ret != 0) {
4994 fprintf(stderr, "Failed to start transaction on db %s\n",
4995 db_name);
4996 return ret;
4999 ret = ctdb_transaction_delete_record(h, key);
5000 if (ret != 0) {
5001 fprintf(stderr, "Failed to delete record for key %s\n",
5002 argv[1]);
5003 ctdb_transaction_cancel(h);
5004 return ret;
5007 ret = ctdb_transaction_commit(h);
5008 if (ret != 0) {
5009 fprintf(stderr, "Failed to commit transaction on db %s\n",
5010 db_name);
5011 ctdb_transaction_cancel(h);
5012 return ret;
5015 return 0;
5018 static int ptrans_parse_string(TALLOC_CTX *mem_ctx, const char **ptr, TDB_DATA *data)
5020 const char *t;
5021 size_t n;
5022 int ret;
5024 *data = tdb_null;
5026 /* Skip whitespace */
5027 n = strspn(*ptr, " \t");
5028 t = *ptr + n;
5030 if (t[0] == '"') {
5031 /* Quoted ASCII string - no wide characters! */
5032 t++;
5033 n = strcspn(t, "\"");
5034 if (t[n] == '"') {
5035 if (n > 0) {
5036 ret = str_to_data(t, n, mem_ctx, data);
5037 if (ret != 0) {
5038 return ret;
5041 *ptr = t + n + 1;
5042 } else {
5043 fprintf(stderr, "Unmatched \" in input %s\n", *ptr);
5044 return 1;
5046 } else {
5047 fprintf(stderr, "Unsupported input format in %s\n", *ptr);
5048 return 1;
5051 return 0;
5054 #define MAX_LINE_SIZE 1024
5056 static bool ptrans_get_key_value(TALLOC_CTX *mem_ctx, FILE *file,
5057 TDB_DATA *key, TDB_DATA *value)
5059 char line [MAX_LINE_SIZE]; /* FIXME: make this more flexible? */
5060 const char *ptr;
5061 int ret;
5063 ptr = fgets(line, MAX_LINE_SIZE, file);
5064 if (ptr == NULL) {
5065 return false;
5068 /* Get key */
5069 ret = ptrans_parse_string(mem_ctx, &ptr, key);
5070 if (ret != 0 || ptr == NULL || key->dptr == NULL) {
5071 /* Line Ignored but not EOF */
5072 *key = tdb_null;
5073 return true;
5076 /* Get value */
5077 ret = ptrans_parse_string(mem_ctx, &ptr, value);
5078 if (ret != 0) {
5079 /* Line Ignored but not EOF */
5080 talloc_free(key->dptr);
5081 *key = tdb_null;
5082 return true;
5085 return true;
5088 static int control_ptrans(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5089 int argc, const char **argv)
5091 const char *db_name;
5092 struct ctdb_db_context *db;
5093 struct ctdb_transaction_handle *h;
5094 uint8_t db_flags;
5095 FILE *file;
5096 TDB_DATA key, value;
5097 int ret;
5099 if (argc < 1 || argc > 2) {
5100 usage("ptrans");
5103 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5104 return 1;
5107 if (! (db_flags &
5108 (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED))) {
5109 fprintf(stderr, "Transactions not supported on DB %s\n",
5110 db_name);
5111 return 1;
5114 if (argc == 2) {
5115 file = fopen(argv[1], "r");
5116 if (file == NULL) {
5117 fprintf(stderr, "Failed to open file %s\n", argv[1]);
5118 return 1;
5120 } else {
5121 file = stdin;
5124 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5125 db_flags, &db);
5126 if (ret != 0) {
5127 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5128 goto done;
5131 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
5132 TIMEOUT(), db, false, &h);
5133 if (ret != 0) {
5134 fprintf(stderr, "Failed to start transaction on db %s\n",
5135 db_name);
5136 goto done;
5139 while (ptrans_get_key_value(mem_ctx, file, &key, &value)) {
5140 if (key.dsize != 0) {
5141 ret = ctdb_transaction_store_record(h, key, value);
5142 if (ret != 0) {
5143 fprintf(stderr, "Failed to store record\n");
5144 ctdb_transaction_cancel(h);
5145 goto done;
5147 talloc_free(key.dptr);
5148 talloc_free(value.dptr);
5152 ret = ctdb_transaction_commit(h);
5153 if (ret != 0) {
5154 fprintf(stderr, "Failed to commit transaction on db %s\n",
5155 db_name);
5156 ctdb_transaction_cancel(h);
5159 done:
5160 if (file != stdin) {
5161 fclose(file);
5163 return ret;
5166 static int control_tfetch(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5167 int argc, const char **argv)
5169 struct tdb_context *tdb;
5170 TDB_DATA key, data;
5171 struct ctdb_ltdb_header header;
5172 int ret;
5174 if (argc < 2 || argc > 3) {
5175 usage("tfetch");
5178 tdb = tdb_open(argv[0], 0, 0, O_RDWR, 0);
5179 if (tdb == NULL) {
5180 fprintf(stderr, "Failed to open TDB file %s\n", argv[0]);
5181 return 1;
5184 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5185 if (ret != 0) {
5186 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5187 tdb_close(tdb);
5188 return ret;
5191 data = tdb_fetch(tdb, key);
5192 if (data.dptr == NULL) {
5193 fprintf(stderr, "No record for key %s\n", argv[1]);
5194 tdb_close(tdb);
5195 return 1;
5198 if (data.dsize < sizeof(struct ctdb_ltdb_header)) {
5199 fprintf(stderr, "Invalid record for key %s\n", argv[1]);
5200 tdb_close(tdb);
5201 return 1;
5204 tdb_close(tdb);
5206 if (argc == 3) {
5207 int fd;
5208 ssize_t nwritten;
5210 fd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0600);
5211 if (fd == -1) {
5212 fprintf(stderr, "Failed to open output file %s\n",
5213 argv[2]);
5214 goto fail;
5217 nwritten = sys_write(fd, data.dptr, data.dsize);
5218 if (nwritten != data.dsize) {
5219 fprintf(stderr, "Failed to write record to file\n");
5220 close(fd);
5221 goto fail;
5224 close(fd);
5227 fail:
5228 ret = ctdb_ltdb_header_extract(&data, &header);
5229 if (ret != 0) {
5230 fprintf(stderr, "Failed to parse header from data\n");
5231 return 1;
5234 dump_ltdb_header(&header);
5235 dump_tdb_data("data", data);
5237 return 0;
5240 static int control_tstore(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5241 int argc, const char **argv)
5243 struct tdb_context *tdb;
5244 TDB_DATA key, data[2], value;
5245 struct ctdb_ltdb_header header;
5246 uint8_t header_buf[sizeof(struct ctdb_ltdb_header)];
5247 size_t np;
5248 int ret;
5250 if (argc < 3 || argc > 5) {
5251 usage("tstore");
5254 tdb = tdb_open(argv[0], 0, 0, O_RDWR, 0);
5255 if (tdb == NULL) {
5256 fprintf(stderr, "Failed to open TDB file %s\n", argv[0]);
5257 return 1;
5260 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5261 if (ret != 0) {
5262 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5263 tdb_close(tdb);
5264 return ret;
5267 ret = str_to_data(argv[2], strlen(argv[2]), mem_ctx, &value);
5268 if (ret != 0) {
5269 fprintf(stderr, "Failed to parse value %s\n", argv[2]);
5270 tdb_close(tdb);
5271 return ret;
5274 ZERO_STRUCT(header);
5276 if (argc > 3) {
5277 header.rsn = (uint64_t)strtoull(argv[3], NULL, 0);
5279 if (argc > 4) {
5280 header.dmaster = (uint32_t)atol(argv[4]);
5282 if (argc > 5) {
5283 header.flags = (uint32_t)atol(argv[5]);
5286 ctdb_ltdb_header_push(&header, header_buf, &np);
5288 data[0].dsize = np;
5289 data[0].dptr = header_buf;
5291 data[1].dsize = value.dsize;
5292 data[1].dptr = value.dptr;
5294 ret = tdb_storev(tdb, key, data, 2, TDB_REPLACE);
5295 if (ret != 0) {
5296 fprintf(stderr, "Failed to write record %s to file %s\n",
5297 argv[1], argv[0]);
5300 tdb_close(tdb);
5302 return ret;
5305 static int control_readkey(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5306 int argc, const char **argv)
5308 const char *db_name;
5309 struct ctdb_db_context *db;
5310 struct ctdb_record_handle *h;
5311 uint8_t db_flags;
5312 TDB_DATA key, data;
5313 bool readonly = false;
5314 int ret;
5316 if (argc < 2 || argc > 3) {
5317 usage("readkey");
5320 if (argc == 3) {
5321 if (strcmp(argv[2], "readonly") == 0) {
5322 readonly = true;
5323 } else {
5324 usage("readkey");
5328 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5329 return 1;
5332 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
5333 fprintf(stderr, "DB %s is not a volatile database\n",
5334 db_name);
5335 return 1;
5338 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5339 db_flags, &db);
5340 if (ret != 0) {
5341 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5342 return ret;
5345 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5346 if (ret != 0) {
5347 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5348 return ret;
5351 ret = ctdb_fetch_lock(mem_ctx, ctdb->ev, ctdb->client,
5352 db, key, readonly, &h, NULL, &data);
5353 if (ret != 0) {
5354 fprintf(stderr, "Failed to read record for key %s\n",
5355 argv[1]);
5356 } else {
5357 printf("Data: size:%zu ptr:[%.*s]\n", data.dsize,
5358 (int)data.dsize, data.dptr);
5361 talloc_free(h);
5362 return ret;
5365 static int control_writekey(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5366 int argc, const char **argv)
5368 const char *db_name;
5369 struct ctdb_db_context *db;
5370 struct ctdb_record_handle *h;
5371 uint8_t db_flags;
5372 TDB_DATA key, data;
5373 int ret;
5375 if (argc != 3) {
5376 usage("writekey");
5379 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5380 return 1;
5383 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
5384 fprintf(stderr, "DB %s is not a volatile database\n",
5385 db_name);
5386 return 1;
5389 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5390 db_flags, &db);
5391 if (ret != 0) {
5392 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5393 return ret;
5396 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5397 if (ret != 0) {
5398 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5399 return ret;
5402 ret = str_to_data(argv[2], strlen(argv[2]), mem_ctx, &data);
5403 if (ret != 0) {
5404 fprintf(stderr, "Failed to parse value %s\n", argv[2]);
5405 return ret;
5408 ret = ctdb_fetch_lock(mem_ctx, ctdb->ev, ctdb->client,
5409 db, key, false, &h, NULL, NULL);
5410 if (ret != 0) {
5411 fprintf(stderr, "Failed to lock record for key %s\n", argv[0]);
5412 return ret;
5415 ret = ctdb_store_record(h, data);
5416 if (ret != 0) {
5417 fprintf(stderr, "Failed to store record for key %s\n",
5418 argv[1]);
5421 talloc_free(h);
5422 return ret;
5425 static int control_deletekey(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5426 int argc, const char **argv)
5428 const char *db_name;
5429 struct ctdb_db_context *db;
5430 struct ctdb_record_handle *h;
5431 uint8_t db_flags;
5432 TDB_DATA key, data;
5433 int ret;
5435 if (argc != 2) {
5436 usage("deletekey");
5439 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5440 return 1;
5443 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
5444 fprintf(stderr, "DB %s is not a volatile database\n",
5445 db_name);
5446 return 1;
5449 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5450 db_flags, &db);
5451 if (ret != 0) {
5452 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5453 return ret;
5456 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5457 if (ret != 0) {
5458 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5459 return ret;
5462 ret = ctdb_fetch_lock(mem_ctx, ctdb->ev, ctdb->client,
5463 db, key, false, &h, NULL, &data);
5464 if (ret != 0) {
5465 fprintf(stderr, "Failed to fetch record for key %s\n",
5466 argv[1]);
5467 return ret;
5470 ret = ctdb_delete_record(h);
5471 if (ret != 0) {
5472 fprintf(stderr, "Failed to delete record for key %s\n",
5473 argv[1]);
5476 talloc_free(h);
5477 return ret;
5480 static int control_checktcpport(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5481 int argc, const char **argv)
5483 struct sockaddr_in sin;
5484 unsigned int port;
5485 int s, v;
5486 int ret;
5488 if (argc != 1) {
5489 usage("chktcpport");
5492 port = atoi(argv[0]);
5494 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
5495 if (s == -1) {
5496 fprintf(stderr, "Failed to open local socket\n");
5497 return errno;
5500 v = fcntl(s, F_GETFL, 0);
5501 if (v == -1 || fcntl(s, F_SETFL, v | O_NONBLOCK)) {
5502 fprintf(stderr, "Unable to set socket non-blocking\n");
5503 close(s);
5504 return errno;
5507 bzero(&sin, sizeof(sin));
5508 sin.sin_family = AF_INET;
5509 sin.sin_port = htons(port);
5510 ret = bind(s, (struct sockaddr *)&sin, sizeof(sin));
5511 close(s);
5512 if (ret == -1) {
5513 fprintf(stderr, "Failed to bind to TCP port %u\n", port);
5514 return errno;
5517 return 0;
5520 static int control_getdbseqnum(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5521 int argc, const char **argv)
5523 uint32_t db_id;
5524 const char *db_name;
5525 uint64_t seqnum;
5526 int ret;
5528 if (argc != 1) {
5529 usage("getdbseqnum");
5532 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, NULL)) {
5533 return 1;
5536 ret = ctdb_ctrl_get_db_seqnum(mem_ctx, ctdb->ev, ctdb->client,
5537 ctdb->cmd_pnn, TIMEOUT(), db_id,
5538 &seqnum);
5539 if (ret != 0) {
5540 fprintf(stderr, "Failed to get sequence number for DB %s\n",
5541 db_name);
5542 return ret;
5545 printf("0x%"PRIx64"\n", seqnum);
5546 return 0;
5549 static int control_nodestatus(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5550 int argc, const char **argv)
5552 const char *nodestring = NULL;
5553 struct ctdb_node_map *nodemap;
5554 int ret, i;
5555 bool print_hdr = false;
5557 if (argc > 1) {
5558 usage("nodestatus");
5561 if (argc == 1) {
5562 nodestring = argv[0];
5563 if (strcmp(nodestring, "all") == 0) {
5564 print_hdr = true;
5568 if (! parse_nodestring(mem_ctx, ctdb, nodestring, &nodemap)) {
5569 return 1;
5572 if (options.machinereadable) {
5573 print_nodemap_machine(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn);
5574 } else {
5575 print_nodemap(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn, print_hdr);
5578 ret = 0;
5579 for (i=0; i<nodemap->num; i++) {
5580 ret |= nodemap->node[i].flags;
5583 return ret;
5586 const struct {
5587 const char *name;
5588 uint32_t offset;
5589 } db_stats_fields[] = {
5590 #define DBSTATISTICS_FIELD(n) { #n, offsetof(struct ctdb_db_statistics, n) }
5591 DBSTATISTICS_FIELD(db_ro_delegations),
5592 DBSTATISTICS_FIELD(db_ro_revokes),
5593 DBSTATISTICS_FIELD(locks.num_calls),
5594 DBSTATISTICS_FIELD(locks.num_current),
5595 DBSTATISTICS_FIELD(locks.num_pending),
5596 DBSTATISTICS_FIELD(locks.num_failed),
5599 static void print_dbstatistics(const char *db_name,
5600 struct ctdb_db_statistics *s)
5602 int i;
5603 const char *prefix = NULL;
5604 int preflen = 0;
5606 printf("DB Statistics %s\n", db_name);
5608 for (i=0; i<ARRAY_SIZE(db_stats_fields); i++) {
5609 if (strchr(db_stats_fields[i].name, '.') != NULL) {
5610 preflen = strcspn(db_stats_fields[i].name, ".") + 1;
5611 if (! prefix ||
5612 strncmp(prefix, db_stats_fields[i].name, preflen) != 0) {
5613 prefix = db_stats_fields[i].name;
5614 printf(" %*.*s\n", preflen-1, preflen-1,
5615 db_stats_fields[i].name);
5617 } else {
5618 preflen = 0;
5620 printf(" %*s%-22s%*s%10u\n", preflen ? 4 : 0, "",
5621 db_stats_fields[i].name+preflen, preflen ? 0 : 4, "",
5622 *(uint32_t *)(db_stats_fields[i].offset+(uint8_t *)s));
5625 printf(" hop_count_buckets:");
5626 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
5627 printf(" %d", s->hop_count_bucket[i]);
5629 printf("\n");
5631 printf(" lock_buckets:");
5632 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
5633 printf(" %d", s->locks.buckets[i]);
5635 printf("\n");
5637 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
5638 "locks_latency MIN/AVG/MAX",
5639 s->locks.latency.min, LATENCY_AVG(s->locks.latency),
5640 s->locks.latency.max, s->locks.latency.num);
5642 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
5643 "vacuum_latency MIN/AVG/MAX",
5644 s->vacuum.latency.min, LATENCY_AVG(s->vacuum.latency),
5645 s->vacuum.latency.max, s->vacuum.latency.num);
5647 printf(" Num Hot Keys: %d\n", s->num_hot_keys);
5648 for (i=0; i<s->num_hot_keys; i++) {
5649 int j;
5650 printf(" Count:%d Key:", s->hot_keys[i].count);
5651 for (j=0; j<s->hot_keys[i].key.dsize; j++) {
5652 printf("%02x", s->hot_keys[i].key.dptr[j] & 0xff);
5654 printf("\n");
5658 static int control_dbstatistics(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5659 int argc, const char **argv)
5661 uint32_t db_id;
5662 const char *db_name;
5663 struct ctdb_db_statistics *dbstats;
5664 int ret;
5666 if (argc != 1) {
5667 usage("dbstatistics");
5670 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, NULL)) {
5671 return 1;
5674 ret = ctdb_ctrl_get_db_statistics(mem_ctx, ctdb->ev, ctdb->client,
5675 ctdb->cmd_pnn, TIMEOUT(), db_id,
5676 &dbstats);
5677 if (ret != 0) {
5678 fprintf(stderr, "Failed to get statistics for DB %s\n",
5679 db_name);
5680 return ret;
5683 print_dbstatistics(db_name, dbstats);
5684 return 0;
5687 struct disable_takeover_runs_state {
5688 uint32_t *pnn_list;
5689 int node_count;
5690 bool *reply;
5691 int status;
5692 bool done;
5695 static void disable_takeover_run_handler(uint64_t srvid, TDB_DATA data,
5696 void *private_data)
5698 struct disable_takeover_runs_state *state =
5699 (struct disable_takeover_runs_state *)private_data;
5700 int ret, i;
5702 if (data.dsize != sizeof(int)) {
5703 /* Ignore packet */
5704 return;
5707 /* ret will be a PNN (i.e. >=0) on success, or negative on error */
5708 ret = *(int *)data.dptr;
5709 if (ret < 0) {
5710 state->status = ret;
5711 state->done = true;
5712 return;
5714 for (i=0; i<state->node_count; i++) {
5715 if (state->pnn_list[i] == ret) {
5716 state->reply[i] = true;
5717 break;
5721 state->done = true;
5722 for (i=0; i<state->node_count; i++) {
5723 if (! state->reply[i]) {
5724 state->done = false;
5725 break;
5730 static int disable_takeover_runs(TALLOC_CTX *mem_ctx,
5731 struct ctdb_context *ctdb, uint32_t timeout,
5732 uint32_t *pnn_list, int count)
5734 struct ctdb_disable_message disable = { 0 };
5735 struct disable_takeover_runs_state state;
5736 int ret, i;
5738 disable.pnn = ctdb->pnn;
5739 disable.srvid = next_srvid(ctdb);
5740 disable.timeout = timeout;
5742 state.pnn_list = pnn_list;
5743 state.node_count = count;
5744 state.done = false;
5745 state.status = 0;
5746 state.reply = talloc_zero_array(mem_ctx, bool, count);
5747 if (state.reply == NULL) {
5748 return ENOMEM;
5751 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
5752 disable.srvid,
5753 disable_takeover_run_handler,
5754 &state);
5755 if (ret != 0) {
5756 return ret;
5759 for (i=0; i<count; i++) {
5760 ret = ctdb_message_disable_takeover_runs(mem_ctx, ctdb->ev,
5761 ctdb->client,
5762 pnn_list[i],
5763 &disable);
5764 if (ret != 0) {
5765 goto fail;
5769 ret = ctdb_client_wait_timeout(ctdb->ev, &state.done, TIMEOUT());
5770 if (ret == ETIME) {
5771 fprintf(stderr, "Timed out waiting to disable takeover runs\n");
5772 } else {
5773 ret = (state.status >= 0 ? 0 : 1);
5776 fail:
5777 ctdb_client_remove_message_handler(ctdb->ev, ctdb->client,
5778 disable.srvid, &state);
5779 return ret;
5782 static int control_reloadips(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5783 int argc, const char **argv)
5785 const char *nodestring = NULL;
5786 struct ctdb_node_map *nodemap, *nodemap2;
5787 struct ctdb_req_control request;
5788 uint32_t *pnn_list, *pnn_list2;
5789 int ret, count, count2;
5791 if (argc > 1) {
5792 usage("reloadips");
5795 if (argc == 1) {
5796 nodestring = argv[0];
5799 nodemap = get_nodemap(ctdb, false);
5800 if (nodemap == NULL) {
5801 return 1;
5804 if (! parse_nodestring(mem_ctx, ctdb, nodestring, &nodemap2)) {
5805 return 1;
5808 count = list_of_connected_nodes(nodemap, CTDB_UNKNOWN_PNN,
5809 mem_ctx, &pnn_list);
5810 if (count <= 0) {
5811 fprintf(stderr, "Memory allocation error\n");
5812 return 1;
5815 count2 = list_of_active_nodes(nodemap2, CTDB_UNKNOWN_PNN,
5816 mem_ctx, &pnn_list2);
5817 if (count2 <= 0) {
5818 fprintf(stderr, "Memory allocation error\n");
5819 return 1;
5822 /* Disable takeover runs on all connected nodes. A reply
5823 * indicating success is needed from each node so all nodes
5824 * will need to be active.
5826 * A check could be added to not allow reloading of IPs when
5827 * there are disconnected nodes. However, this should
5828 * probably be left up to the administrator.
5830 ret = disable_takeover_runs(mem_ctx, ctdb, 2*options.timelimit,
5831 pnn_list, count);
5832 if (ret != 0) {
5833 fprintf(stderr, "Failed to disable takeover runs\n");
5834 return ret;
5837 /* Now tell all the desired nodes to reload their public IPs.
5838 * Keep trying this until it succeeds. This assumes all
5839 * failures are transient, which might not be true...
5841 ctdb_req_control_reload_public_ips(&request);
5842 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
5843 pnn_list2, count2, TIMEOUT(),
5844 &request, NULL, NULL);
5845 if (ret != 0) {
5846 fprintf(stderr, "Failed to reload IPs on some nodes.\n");
5849 /* It isn't strictly necessary to wait until takeover runs are
5850 * re-enabled but doing so can't hurt.
5852 ret = disable_takeover_runs(mem_ctx, ctdb, 0, pnn_list, count);
5853 if (ret != 0) {
5854 fprintf(stderr, "Failed to enable takeover runs\n");
5855 return ret;
5858 return ipreallocate(mem_ctx, ctdb);
5862 static const struct ctdb_cmd {
5863 const char *name;
5864 int (*fn)(TALLOC_CTX *, struct ctdb_context *, int, const char **);
5865 bool without_daemon; /* can be run without daemon running ? */
5866 bool remote; /* can be run on remote nodes */
5867 const char *msg;
5868 const char *args;
5869 } ctdb_commands[] = {
5870 { "version", control_version, true, false,
5871 "show version of ctdb", NULL },
5872 { "status", control_status, false, true,
5873 "show node status", NULL },
5874 { "uptime", control_uptime, false, true,
5875 "show node uptime", NULL },
5876 { "ping", control_ping, false, true,
5877 "ping a node", NULL },
5878 { "runstate", control_runstate, false, true,
5879 "get/check runstate of a node",
5880 "[setup|first_recovery|startup|running]" },
5881 { "getvar", control_getvar, false, true,
5882 "get a tunable variable", "<name>" },
5883 { "setvar", control_setvar, false, true,
5884 "set a tunable variable", "<name> <value>" },
5885 { "listvars", control_listvars, false, true,
5886 "list tunable variables", NULL },
5887 { "statistics", control_statistics, false, true,
5888 "show ctdb statistics", NULL },
5889 { "statisticsreset", control_statistics_reset, false, true,
5890 "reset ctdb statistics", NULL },
5891 { "stats", control_stats, false, true,
5892 "show rolling statistics", "[count]" },
5893 { "ip", control_ip, false, true,
5894 "show public ips", "[all]" },
5895 { "ipinfo", control_ipinfo, false, true,
5896 "show public ip details", "<ip>" },
5897 { "ifaces", control_ifaces, false, true,
5898 "show interfaces", NULL },
5899 { "setifacelink", control_setifacelink, false, true,
5900 "set interface link status", "<iface> up|down" },
5901 { "process-exists", control_process_exists, false, true,
5902 "check if a process exists on a node", "<pid> [<srvid>]" },
5903 { "getdbmap", control_getdbmap, false, true,
5904 "show attached databases", NULL },
5905 { "getdbstatus", control_getdbstatus, false, true,
5906 "show database status", "<dbname|dbid>" },
5907 { "catdb", control_catdb, false, false,
5908 "dump cluster-wide ctdb database", "<dbname|dbid>" },
5909 { "cattdb", control_cattdb, false, false,
5910 "dump local ctdb database", "<dbname|dbid>" },
5911 { "getcapabilities", control_getcapabilities, false, true,
5912 "show node capabilities", NULL },
5913 { "pnn", control_pnn, false, false,
5914 "show the pnn of the currnet node", NULL },
5915 { "lvs", control_lvs, false, false,
5916 "show lvs configuration", "master|list|status" },
5917 { "setdebug", control_setdebug, false, true,
5918 "set debug level", "ERROR|WARNING|NOTICE|INFO|DEBUG" },
5919 { "getdebug", control_getdebug, false, true,
5920 "get debug level", NULL },
5921 { "attach", control_attach, false, false,
5922 "attach a database", "<dbname> [persistent|replicated]" },
5923 { "detach", control_detach, false, false,
5924 "detach database(s)", "<dbname|dbid> ..." },
5925 { "dumpmemory", control_dumpmemory, false, true,
5926 "dump ctdbd memory map", NULL },
5927 { "rddumpmemory", control_rddumpmemory, false, true,
5928 "dump recoverd memory map", NULL },
5929 { "getpid", control_getpid, false, true,
5930 "get ctdbd process ID", NULL },
5931 { "disable", control_disable, false, true,
5932 "disable a node", NULL },
5933 { "enable", control_enable, false, true,
5934 "enable a node", NULL },
5935 { "stop", control_stop, false, true,
5936 "stop a node", NULL },
5937 { "continue", control_continue, false, true,
5938 "continue a stopped node", NULL },
5939 { "ban", control_ban, false, true,
5940 "ban a node", "<bantime>"},
5941 { "unban", control_unban, false, true,
5942 "unban a node", NULL },
5943 { "shutdown", control_shutdown, false, true,
5944 "shutdown ctdb daemon", NULL },
5945 { "recover", control_recover, false, true,
5946 "force recovery", NULL },
5947 { "sync", control_ipreallocate, false, true,
5948 "run ip reallocation (deprecated)", NULL },
5949 { "ipreallocate", control_ipreallocate, false, true,
5950 "run ip reallocation", NULL },
5951 { "isnotrecmaster", control_isnotrecmaster, false, false,
5952 "check if local node is the recmaster", NULL },
5953 { "gratarp", control_gratarp, false, true,
5954 "send a gratuitous arp", "<ip> <interface>" },
5955 { "tickle", control_tickle, true, false,
5956 "send a tcp tickle ack", "<srcip:port> <dstip:port>" },
5957 { "gettickles", control_gettickles, false, true,
5958 "get the list of tickles", "<ip> [<port>]" },
5959 { "addtickle", control_addtickle, false, true,
5960 "add a tickle", "<ip>:<port> <ip>:<port>" },
5961 { "deltickle", control_deltickle, false, true,
5962 "delete a tickle", "<ip>:<port> <ip>:<port>" },
5963 { "listnodes", control_listnodes, true, true,
5964 "list nodes in the cluster", NULL },
5965 { "reloadnodes", control_reloadnodes, false, false,
5966 "reload the nodes file all nodes", NULL },
5967 { "moveip", control_moveip, false, false,
5968 "move an ip address to another node", "<ip> <node>" },
5969 { "addip", control_addip, false, true,
5970 "add an ip address to a node", "<ip/mask> <iface>" },
5971 { "delip", control_delip, false, true,
5972 "delete an ip address from a node", "<ip>" },
5973 { "backupdb", control_backupdb, false, false,
5974 "backup a database into a file", "<dbname|dbid> <file>" },
5975 { "restoredb", control_restoredb, false, false,
5976 "restore a database from a file", "<file> [dbname]" },
5977 { "dumpdbbackup", control_dumpdbbackup, true, false,
5978 "dump database from a backup file", "<file>" },
5979 { "wipedb", control_wipedb, false, false,
5980 "wipe the contents of a database.", "<dbname|dbid>"},
5981 { "recmaster", control_recmaster, false, true,
5982 "show the pnn for the recovery master", NULL },
5983 { "event", control_event, false, false,
5984 "event and event script commands", NULL },
5985 { "scriptstatus", control_scriptstatus, false, false,
5986 "show event script status",
5987 "[init|setup|startup|monitor|takeip|releaseip|ipreallocated]" },
5988 { "natgw", control_natgw, false, false,
5989 "show natgw configuration", "master|list|status" },
5990 { "getreclock", control_getreclock, false, true,
5991 "get recovery lock file", NULL },
5992 { "setlmasterrole", control_setlmasterrole, false, true,
5993 "set LMASTER role", "on|off" },
5994 { "setrecmasterrole", control_setrecmasterrole, false, true,
5995 "set RECMASTER role", "on|off"},
5996 { "setdbreadonly", control_setdbreadonly, false, true,
5997 "enable readonly records", "<dbname|dbid>" },
5998 { "setdbsticky", control_setdbsticky, false, true,
5999 "enable sticky records", "<dbname|dbid>"},
6000 { "pfetch", control_pfetch, false, false,
6001 "fetch record from persistent database", "<dbname|dbid> <key> [<file>]" },
6002 { "pstore", control_pstore, false, false,
6003 "write record to persistent database", "<dbname|dbid> <key> <value>" },
6004 { "pdelete", control_pdelete, false, false,
6005 "delete record from persistent database", "<dbname|dbid> <key>" },
6006 { "ptrans", control_ptrans, false, false,
6007 "update a persistent database (from file or stdin)", "<dbname|dbid> [<file>]" },
6008 { "tfetch", control_tfetch, false, true,
6009 "fetch a record", "<tdb-file> <key> [<file>]" },
6010 { "tstore", control_tstore, false, true,
6011 "store a record", "<tdb-file> <key> <data> [<rsn> <dmaster> <flags>]" },
6012 { "readkey", control_readkey, false, false,
6013 "read value of a database key", "<dbname|dbid> <key> [readonly]" },
6014 { "writekey", control_writekey, false, false,
6015 "write value for a database key", "<dbname|dbid> <key> <value>" },
6016 { "deletekey", control_deletekey, false, false,
6017 "delete a database key", "<dbname|dbid> <key>" },
6018 { "checktcpport", control_checktcpport, true, false,
6019 "check if a service is bound to a specific tcp port or not", "<port>" },
6020 { "getdbseqnum", control_getdbseqnum, false, false,
6021 "get database sequence number", "<dbname|dbid>" },
6022 { "nodestatus", control_nodestatus, false, true,
6023 "show and return node status", "[all|<pnn-list>]" },
6024 { "dbstatistics", control_dbstatistics, false, true,
6025 "show database statistics", "<dbname|dbid>" },
6026 { "reloadips", control_reloadips, false, false,
6027 "reload the public addresses file", "[all|<pnn-list>]" },
6030 static const struct ctdb_cmd *match_command(const char *command)
6032 const struct ctdb_cmd *cmd;
6033 int i;
6035 for (i=0; i<ARRAY_SIZE(ctdb_commands); i++) {
6036 cmd = &ctdb_commands[i];
6037 if (strlen(command) == strlen(cmd->name) &&
6038 strncmp(command, cmd->name, strlen(command)) == 0) {
6039 return cmd;
6043 return NULL;
6048 * Show usage message
6050 static void usage_full(void)
6052 int i;
6054 poptPrintHelp(pc, stdout, 0);
6055 printf("\nCommands:\n");
6056 for (i=0; i<ARRAY_SIZE(ctdb_commands); i++) {
6057 printf(" %-15s %-27s %s\n",
6058 ctdb_commands[i].name,
6059 ctdb_commands[i].args ? ctdb_commands[i].args : "",
6060 ctdb_commands[i].msg);
6064 static void usage(const char *command)
6066 const struct ctdb_cmd *cmd;
6068 if (command == NULL) {
6069 usage_full();
6070 exit(1);
6073 cmd = match_command(command);
6074 if (cmd == NULL) {
6075 usage_full();
6076 } else {
6077 poptPrintUsage(pc, stdout, 0);
6078 printf("\nCommands:\n");
6079 printf(" %-15s %-27s %s\n",
6080 cmd->name, cmd->args ? cmd->args : "", cmd->msg);
6083 exit(1);
6086 struct poptOption cmdline_options[] = {
6087 POPT_AUTOHELP
6088 { "debug", 'd', POPT_ARG_STRING, &options.debuglevelstr, 0,
6089 "debug level"},
6090 { "timelimit", 't', POPT_ARG_INT, &options.timelimit, 0,
6091 "timelimit (in seconds)" },
6092 { "node", 'n', POPT_ARG_INT, &options.pnn, 0,
6093 "node specification - integer" },
6094 { NULL, 'Y', POPT_ARG_NONE, &options.machinereadable, 0,
6095 "enable machine readable output", NULL },
6096 { "separator", 'x', POPT_ARG_STRING, &options.sep, 0,
6097 "specify separator for machine readable output", "CHAR" },
6098 { NULL, 'X', POPT_ARG_NONE, &options.machineparsable, 0,
6099 "enable machine parsable output with separator |", NULL },
6100 { "verbose", 'v', POPT_ARG_NONE, &options.verbose, 0,
6101 "enable verbose output", NULL },
6102 { "maxruntime", 'T', POPT_ARG_INT, &options.maxruntime, 0,
6103 "die if runtime exceeds this limit (in seconds)" },
6104 POPT_TABLEEND
6107 static int process_command(const struct ctdb_cmd *cmd, int argc,
6108 const char **argv)
6110 TALLOC_CTX *tmp_ctx;
6111 struct ctdb_context *ctdb;
6112 const char *ctdb_socket;
6113 int ret;
6114 bool status;
6115 uint64_t srvid_offset;
6117 tmp_ctx = talloc_new(NULL);
6118 if (tmp_ctx == NULL) {
6119 fprintf(stderr, "Memory allocation error\n");
6120 goto fail;
6123 if (cmd->without_daemon) {
6124 if (options.pnn != -1) {
6125 fprintf(stderr,
6126 "Cannot specify node for command %s\n",
6127 cmd->name);
6128 goto fail;
6131 ret = cmd->fn(tmp_ctx, NULL, argc-1, argv+1);
6132 talloc_free(tmp_ctx);
6133 return ret;
6136 ctdb = talloc_zero(tmp_ctx, struct ctdb_context);
6137 if (ctdb == NULL) {
6138 fprintf(stderr, "Memory allocation error\n");
6139 goto fail;
6142 ctdb->ev = tevent_context_init(ctdb);
6143 if (ctdb->ev == NULL) {
6144 fprintf(stderr, "Failed to initialize tevent\n");
6145 goto fail;
6148 ctdb_socket = getenv("CTDB_SOCKET");
6149 if (ctdb_socket == NULL) {
6150 ctdb_socket = CTDB_SOCKET;
6153 ret = ctdb_client_init(ctdb, ctdb->ev, ctdb_socket, &ctdb->client);
6154 if (ret != 0) {
6155 fprintf(stderr, "Failed to connect to CTDB daemon (%s)\n",
6156 ctdb_socket);
6158 if (!find_node_xpnn(ctdb, NULL)) {
6159 fprintf(stderr, "Is this node part of CTDB cluster?\n");
6161 goto fail;
6164 ctdb->pnn = ctdb_client_pnn(ctdb->client);
6165 srvid_offset = getpid() & 0xFFFF;
6166 ctdb->srvid = SRVID_CTDB_TOOL | (srvid_offset << 16);
6168 if (options.pnn != -1) {
6169 status = verify_pnn(ctdb, options.pnn);
6170 if (! status) {
6171 goto fail;
6174 ctdb->cmd_pnn = options.pnn;
6175 } else {
6176 ctdb->cmd_pnn = ctdb->pnn;
6179 if (! cmd->remote && ctdb->pnn != ctdb->cmd_pnn) {
6180 fprintf(stderr, "Node cannot be specified for command %s\n",
6181 cmd->name);
6182 goto fail;
6185 ret = cmd->fn(tmp_ctx, ctdb, argc-1, argv+1);
6186 talloc_free(tmp_ctx);
6187 return ret;
6189 fail:
6190 talloc_free(tmp_ctx);
6191 return 1;
6194 static void signal_handler(int sig)
6196 fprintf(stderr, "Maximum runtime exceeded - exiting\n");
6199 static void alarm_handler(int sig)
6201 /* Kill any child processes */
6202 signal(SIGTERM, signal_handler);
6203 kill(0, SIGTERM);
6205 _exit(1);
6208 int main(int argc, const char *argv[])
6210 int opt;
6211 const char **extra_argv;
6212 int extra_argc;
6213 const struct ctdb_cmd *cmd;
6214 int loglevel;
6215 int ret;
6217 setlinebuf(stdout);
6219 /* Set default options */
6220 options.debuglevelstr = NULL;
6221 options.timelimit = 10;
6222 options.sep = "|";
6223 options.maxruntime = 0;
6224 options.pnn = -1;
6226 pc = poptGetContext(argv[0], argc, argv, cmdline_options,
6227 POPT_CONTEXT_KEEP_FIRST);
6228 while ((opt = poptGetNextOpt(pc)) != -1) {
6229 fprintf(stderr, "Invalid option %s: %s\n",
6230 poptBadOption(pc, 0), poptStrerror(opt));
6231 exit(1);
6234 if (options.maxruntime == 0) {
6235 const char *ctdb_timeout;
6237 ctdb_timeout = getenv("CTDB_TIMEOUT");
6238 if (ctdb_timeout != NULL) {
6239 options.maxruntime = strtoul(ctdb_timeout, NULL, 0);
6240 } else {
6241 options.maxruntime = 120;
6244 if (options.maxruntime <= 120) {
6245 /* default timeout is 120 seconds */
6246 options.maxruntime = 120;
6249 if (options.machineparsable) {
6250 options.machinereadable = 1;
6253 /* setup the remaining options for the commands */
6254 extra_argc = 0;
6255 extra_argv = poptGetArgs(pc);
6256 if (extra_argv) {
6257 extra_argv++;
6258 while (extra_argv[extra_argc]) extra_argc++;
6261 if (extra_argc < 1) {
6262 usage(NULL);
6265 cmd = match_command(extra_argv[0]);
6266 if (cmd == NULL) {
6267 fprintf(stderr, "Unknown command '%s'\n", extra_argv[0]);
6268 exit(1);
6271 /* Enable logging */
6272 setup_logging("ctdb", DEBUG_STDERR);
6273 if (debug_level_parse(options.debuglevelstr, &loglevel)) {
6274 DEBUGLEVEL = loglevel;
6275 } else {
6276 DEBUGLEVEL = DEBUG_ERR;
6279 signal(SIGALRM, alarm_handler);
6280 alarm(options.maxruntime);
6282 ret = process_command(cmd, extra_argc, extra_argv);
6283 if (ret == -1) {
6284 ret = 1;
6287 (void)poptFreeContext(pc);
6289 return ret;