s3:libsmb: Honor disable_netbios option in smbsock_connect_send
[Samba.git] / ctdb / tools / ctdb.c
blobeb4c684e8b083423ad427705b38c79f56a56c44f
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 "common/path.h"
41 #include "protocol/protocol.h"
42 #include "protocol/protocol_api.h"
43 #include "protocol/protocol_util.h"
44 #include "common/system_socket.h"
45 #include "client/client.h"
46 #include "client/client_sync.h"
48 #define TIMEOUT() timeval_current_ofs(options.timelimit, 0)
50 #define SRVID_CTDB_TOOL (CTDB_SRVID_TOOL_RANGE | 0x0001000000000000LL)
51 #define SRVID_CTDB_PUSHDB (CTDB_SRVID_TOOL_RANGE | 0x0002000000000000LL)
53 static struct {
54 const char *debuglevelstr;
55 int timelimit;
56 int pnn;
57 int machinereadable;
58 const char *sep;
59 int machineparsable;
60 int verbose;
61 int maxruntime;
62 int printemptyrecords;
63 int printdatasize;
64 int printlmaster;
65 int printhash;
66 int printrecordflags;
67 } options;
69 static poptContext pc;
71 struct ctdb_context {
72 struct tevent_context *ev;
73 struct ctdb_client_context *client;
74 struct ctdb_node_map *nodemap;
75 uint32_t pnn, cmd_pnn;
76 uint64_t srvid;
79 static void usage(const char *command);
82 * Utility Functions
85 static double timeval_delta(struct timeval *tv2, struct timeval *tv)
87 return (tv2->tv_sec - tv->tv_sec) +
88 (tv2->tv_usec - tv->tv_usec) * 1.0e-6;
91 static struct ctdb_node_and_flags *get_node_by_pnn(
92 struct ctdb_node_map *nodemap,
93 uint32_t pnn)
95 int i;
97 for (i=0; i<nodemap->num; i++) {
98 if (nodemap->node[i].pnn == pnn) {
99 return &nodemap->node[i];
102 return NULL;
105 static const char *pretty_print_flags(TALLOC_CTX *mem_ctx, uint32_t flags)
107 static const struct {
108 uint32_t flag;
109 const char *name;
110 } flag_names[] = {
111 { NODE_FLAGS_DISCONNECTED, "DISCONNECTED" },
112 { NODE_FLAGS_PERMANENTLY_DISABLED, "DISABLED" },
113 { NODE_FLAGS_BANNED, "BANNED" },
114 { NODE_FLAGS_UNHEALTHY, "UNHEALTHY" },
115 { NODE_FLAGS_DELETED, "DELETED" },
116 { NODE_FLAGS_STOPPED, "STOPPED" },
117 { NODE_FLAGS_INACTIVE, "INACTIVE" },
119 char *flags_str = NULL;
120 int i;
122 for (i=0; i<ARRAY_SIZE(flag_names); i++) {
123 if (flags & flag_names[i].flag) {
124 if (flags_str == NULL) {
125 flags_str = talloc_asprintf(mem_ctx,
126 "%s", flag_names[i].name);
127 } else {
128 flags_str = talloc_asprintf_append(flags_str,
129 "|%s", flag_names[i].name);
131 if (flags_str == NULL) {
132 return "OUT-OF-MEMORY";
136 if (flags_str == NULL) {
137 return "OK";
140 return flags_str;
143 static uint64_t next_srvid(struct ctdb_context *ctdb)
145 ctdb->srvid += 1;
146 return ctdb->srvid;
150 * Get consistent nodemap information.
152 * If nodemap is already cached, use that. If not get it.
153 * If the current node is BANNED, then get nodemap from "better" node.
155 static struct ctdb_node_map *get_nodemap(struct ctdb_context *ctdb, bool force)
157 TALLOC_CTX *tmp_ctx;
158 struct ctdb_node_map *nodemap;
159 struct ctdb_node_and_flags *node;
160 uint32_t current_node;
161 int ret;
163 if (force) {
164 TALLOC_FREE(ctdb->nodemap);
167 if (ctdb->nodemap != NULL) {
168 return ctdb->nodemap;
171 tmp_ctx = talloc_new(ctdb);
172 if (tmp_ctx == NULL) {
173 return false;
176 current_node = ctdb->pnn;
177 again:
178 ret = ctdb_ctrl_get_nodemap(tmp_ctx, ctdb->ev, ctdb->client,
179 current_node, TIMEOUT(), &nodemap);
180 if (ret != 0) {
181 fprintf(stderr, "Failed to get nodemap from node %u\n",
182 current_node);
183 goto failed;
186 node = get_node_by_pnn(nodemap, current_node);
187 if (node->flags & NODE_FLAGS_BANNED) {
188 /* Pick next node */
189 do {
190 current_node = (current_node + 1) % nodemap->num;
191 node = get_node_by_pnn(nodemap, current_node);
192 if (! (node->flags &
193 (NODE_FLAGS_DELETED|NODE_FLAGS_DISCONNECTED))) {
194 break;
196 } while (current_node != ctdb->pnn);
198 if (current_node == ctdb->pnn) {
199 /* Tried all nodes in the cluster */
200 fprintf(stderr, "Warning: All nodes are banned.\n");
201 goto failed;
204 goto again;
207 ctdb->nodemap = talloc_steal(ctdb, nodemap);
208 return nodemap;
210 failed:
211 talloc_free(tmp_ctx);
212 return NULL;
215 static bool verify_pnn(struct ctdb_context *ctdb, int pnn)
217 struct ctdb_node_map *nodemap;
218 bool found;
219 int i;
221 if (pnn == -1) {
222 return false;
225 nodemap = get_nodemap(ctdb, false);
226 if (nodemap == NULL) {
227 return false;
230 found = false;
231 for (i=0; i<nodemap->num; i++) {
232 if (nodemap->node[i].pnn == pnn) {
233 found = true;
234 break;
237 if (! found) {
238 fprintf(stderr, "Node %u does not exist\n", pnn);
239 return false;
242 if (nodemap->node[i].flags &
243 (NODE_FLAGS_DISCONNECTED|NODE_FLAGS_DELETED)) {
244 fprintf(stderr, "Node %u has status %s\n", pnn,
245 pretty_print_flags(ctdb, nodemap->node[i].flags));
246 return false;
249 return true;
252 static struct ctdb_node_map *talloc_nodemap(TALLOC_CTX *mem_ctx,
253 struct ctdb_node_map *nodemap)
255 struct ctdb_node_map *nodemap2;
257 nodemap2 = talloc_zero(mem_ctx, struct ctdb_node_map);
258 if (nodemap2 == NULL) {
259 return NULL;
262 nodemap2->node = talloc_array(nodemap2, struct ctdb_node_and_flags,
263 nodemap->num);
264 if (nodemap2->node == NULL) {
265 talloc_free(nodemap2);
266 return NULL;
269 return nodemap2;
273 * Get the number and the list of matching nodes
275 * nodestring := NULL | all | pnn,[pnn,...]
277 * If nodestring is NULL, use the current node.
279 static bool parse_nodestring(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
280 const char *nodestring,
281 struct ctdb_node_map **out)
283 struct ctdb_node_map *nodemap, *nodemap2;
284 struct ctdb_node_and_flags *node;
285 int i;
287 nodemap = get_nodemap(ctdb, false);
288 if (nodemap == NULL) {
289 return false;
292 nodemap2 = talloc_nodemap(mem_ctx, nodemap);
293 if (nodemap2 == NULL) {
294 return false;
297 if (nodestring == NULL) {
298 for (i=0; i<nodemap->num; i++) {
299 if (nodemap->node[i].pnn == ctdb->cmd_pnn) {
300 nodemap2->node[0] = nodemap->node[i];
301 break;
304 nodemap2->num = 1;
306 goto done;
309 if (strcmp(nodestring, "all") == 0) {
310 for (i=0; i<nodemap->num; i++) {
311 nodemap2->node[i] = nodemap->node[i];
313 nodemap2->num = nodemap->num;
315 goto done;
316 } else {
317 char *ns, *tok;
319 ns = talloc_strdup(mem_ctx, nodestring);
320 if (ns == NULL) {
321 return false;
324 tok = strtok(ns, ",");
325 while (tok != NULL) {
326 uint32_t pnn;
327 char *endptr;
329 pnn = (uint32_t)strtoul(tok, &endptr, 0);
330 if (pnn == 0 && tok == endptr) {
331 fprintf(stderr, "Invalid node %s\n", tok);
332 return false;
335 node = get_node_by_pnn(nodemap, pnn);
336 if (node == NULL) {
337 fprintf(stderr, "Node %u does not exist\n",
338 pnn);
339 return false;
342 nodemap2->node[nodemap2->num] = *node;
343 nodemap2->num += 1;
345 tok = strtok(NULL, ",");
349 done:
350 *out = nodemap2;
351 return true;
354 /* Compare IP address */
355 static bool ctdb_same_ip(ctdb_sock_addr *ip1, ctdb_sock_addr *ip2)
357 bool ret = false;
359 if (ip1->sa.sa_family != ip2->sa.sa_family) {
360 return false;
363 switch (ip1->sa.sa_family) {
364 case AF_INET:
365 ret = (memcmp(&ip1->ip.sin_addr, &ip2->ip.sin_addr,
366 sizeof(struct in_addr)) == 0);
367 break;
369 case AF_INET6:
370 ret = (memcmp(&ip1->ip6.sin6_addr, &ip2->ip6.sin6_addr,
371 sizeof(struct in6_addr)) == 0);
372 break;
375 return ret;
378 /* Append a node to a node map with given address and flags */
379 static bool node_map_add(struct ctdb_node_map *nodemap,
380 const char *nstr, uint32_t flags)
382 ctdb_sock_addr addr;
383 uint32_t num;
384 struct ctdb_node_and_flags *n;
385 int ret;
387 ret = ctdb_sock_addr_from_string(nstr, &addr, false);
388 if (ret != 0) {
389 fprintf(stderr, "Invalid IP address %s\n", nstr);
390 return false;
393 num = nodemap->num;
394 nodemap->node = talloc_realloc(nodemap, nodemap->node,
395 struct ctdb_node_and_flags, num+1);
396 if (nodemap->node == NULL) {
397 return false;
400 n = &nodemap->node[num];
401 n->addr = addr;
402 n->pnn = num;
403 n->flags = flags;
405 nodemap->num = num+1;
406 return true;
409 /* Read a nodes file into a node map */
410 static struct ctdb_node_map *ctdb_read_nodes_file(TALLOC_CTX *mem_ctx,
411 const char *nlist)
413 char **lines;
414 int nlines;
415 int i;
416 struct ctdb_node_map *nodemap;
418 nodemap = talloc_zero(mem_ctx, struct ctdb_node_map);
419 if (nodemap == NULL) {
420 return NULL;
423 lines = file_lines_load(nlist, &nlines, 0, mem_ctx);
424 if (lines == NULL) {
425 return NULL;
428 while (nlines > 0 && strcmp(lines[nlines-1], "") == 0) {
429 nlines--;
432 for (i=0; i<nlines; i++) {
433 char *node;
434 uint32_t flags;
435 size_t len;
437 node = lines[i];
438 /* strip leading spaces */
439 while((*node == ' ') || (*node == '\t')) {
440 node++;
443 len = strlen(node);
445 /* strip trailing spaces */
446 while ((len > 1) &&
447 ((node[len-1] == ' ') || (node[len-1] == '\t')))
449 node[len-1] = '\0';
450 len--;
453 if (len == 0) {
454 continue;
456 if (*node == '#') {
457 /* A "deleted" node is a node that is
458 commented out in the nodes file. This is
459 used instead of removing a line, which
460 would cause subsequent nodes to change
461 their PNN. */
462 flags = NODE_FLAGS_DELETED;
463 node = discard_const("0.0.0.0");
464 } else {
465 flags = 0;
467 if (! node_map_add(nodemap, node, flags)) {
468 talloc_free(lines);
469 TALLOC_FREE(nodemap);
470 return NULL;
474 talloc_free(lines);
475 return nodemap;
478 static struct ctdb_node_map *read_nodes_file(TALLOC_CTX *mem_ctx, uint32_t pnn)
480 struct ctdb_node_map *nodemap;
481 const char *nodes_list = NULL;
483 const char *basedir = getenv("CTDB_BASE");
484 if (basedir == NULL) {
485 basedir = CTDB_ETCDIR;
487 nodes_list = talloc_asprintf(mem_ctx, "%s/nodes", basedir);
488 if (nodes_list == NULL) {
489 fprintf(stderr, "Memory allocation error\n");
490 return NULL;
493 nodemap = ctdb_read_nodes_file(mem_ctx, nodes_list);
494 if (nodemap == NULL) {
495 fprintf(stderr, "Failed to read nodes file \"%s\"\n",
496 nodes_list);
497 return NULL;
500 return nodemap;
503 static struct ctdb_dbid *db_find(TALLOC_CTX *mem_ctx,
504 struct ctdb_context *ctdb,
505 struct ctdb_dbid_map *dbmap,
506 const char *db_name)
508 struct ctdb_dbid *db = NULL;
509 const char *name;
510 int ret, i;
512 for (i=0; i<dbmap->num; i++) {
513 ret = ctdb_ctrl_get_dbname(mem_ctx, ctdb->ev, ctdb->client,
514 ctdb->pnn, TIMEOUT(),
515 dbmap->dbs[i].db_id, &name);
516 if (ret != 0) {
517 return false;
520 if (strcmp(db_name, name) == 0) {
521 talloc_free(discard_const(name));
522 db = &dbmap->dbs[i];
523 break;
527 return db;
530 static bool db_exists(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
531 const char *db_arg, uint32_t *db_id,
532 const char **db_name, uint8_t *db_flags)
534 struct ctdb_dbid_map *dbmap;
535 struct ctdb_dbid *db = NULL;
536 uint32_t id = 0;
537 const char *name = NULL;
538 int ret, i;
540 ret = ctdb_ctrl_get_dbmap(mem_ctx, ctdb->ev, ctdb->client,
541 ctdb->pnn, TIMEOUT(), &dbmap);
542 if (ret != 0) {
543 return false;
546 if (strncmp(db_arg, "0x", 2) == 0) {
547 id = strtoul(db_arg, NULL, 0);
548 for (i=0; i<dbmap->num; i++) {
549 if (id == dbmap->dbs[i].db_id) {
550 db = &dbmap->dbs[i];
551 break;
554 } else {
555 name = db_arg;
556 db = db_find(mem_ctx, ctdb, dbmap, name);
559 if (db == NULL) {
560 fprintf(stderr, "No database matching '%s' found\n", db_arg);
561 return false;
564 if (name == NULL) {
565 ret = ctdb_ctrl_get_dbname(mem_ctx, ctdb->ev, ctdb->client,
566 ctdb->pnn, TIMEOUT(), id, &name);
567 if (ret != 0) {
568 return false;
572 if (db_id != NULL) {
573 *db_id = db->db_id;
575 if (db_name != NULL) {
576 *db_name = talloc_strdup(mem_ctx, name);
578 if (db_flags != NULL) {
579 *db_flags = db->flags;
581 return true;
584 static int h2i(char h)
586 if (h >= 'a' && h <= 'f') {
587 return h - 'a' + 10;
589 if (h >= 'A' && h <= 'F') {
590 return h - 'A' + 10;
592 return h - '0';
595 static int hex_to_data(const char *str, size_t len, TALLOC_CTX *mem_ctx,
596 TDB_DATA *out)
598 int i;
599 TDB_DATA data;
601 if (len & 0x01) {
602 fprintf(stderr, "Key (%s) contains odd number of hex digits\n",
603 str);
604 return EINVAL;
607 data.dsize = len / 2;
608 data.dptr = talloc_size(mem_ctx, data.dsize);
609 if (data.dptr == NULL) {
610 return ENOMEM;
613 for (i=0; i<data.dsize; i++) {
614 data.dptr[i] = h2i(str[i*2]) << 4 | h2i(str[i*2+1]);
617 *out = data;
618 return 0;
621 static int str_to_data(const char *str, size_t len, TALLOC_CTX *mem_ctx,
622 TDB_DATA *out)
624 TDB_DATA data;
625 int ret = 0;
627 if (strncmp(str, "0x", 2) == 0) {
628 ret = hex_to_data(str+2, len-2, mem_ctx, &data);
629 if (ret != 0) {
630 return ret;
632 } else {
633 data.dptr = talloc_memdup(mem_ctx, str, len);
634 if (data.dptr == NULL) {
635 return ENOMEM;
637 data.dsize = len;
640 *out = data;
641 return 0;
644 static int run_helper(TALLOC_CTX *mem_ctx, const char *command,
645 const char *path, int argc, const char **argv)
647 pid_t pid;
648 int save_errno, status, ret;
649 const char **new_argv;
650 int i;
652 new_argv = talloc_array(mem_ctx, const char *, argc + 2);
653 if (new_argv == NULL) {
654 return ENOMEM;
657 new_argv[0] = path;
658 for (i=0; i<argc; i++) {
659 new_argv[i+1] = argv[i];
661 new_argv[argc+1] = NULL;
663 pid = fork();
664 if (pid < 0) {
665 save_errno = errno;
666 talloc_free(new_argv);
667 fprintf(stderr, "Failed to fork %s (%s) - %s\n",
668 command, path, strerror(save_errno));
669 return save_errno;
672 if (pid == 0) {
673 ret = execv(path, discard_const(new_argv));
674 if (ret == -1) {
675 _exit(64+errno);
677 /* Should not happen */
678 _exit(64+ENOEXEC);
681 talloc_free(new_argv);
683 ret = waitpid(pid, &status, 0);
684 if (ret == -1) {
685 save_errno = errno;
686 fprintf(stderr, "waitpid() failed for %s - %s\n",
687 command, strerror(save_errno));
688 return save_errno;
691 if (WIFEXITED(status)) {
692 int pstatus = WEXITSTATUS(status);
693 if (WIFSIGNALED(status)) {
694 fprintf(stderr, "%s terminated with signal %d\n",
695 command, WTERMSIG(status));
696 ret = EINTR;
697 } else if (pstatus >= 64 && pstatus < 255) {
698 fprintf(stderr, "%s failed with error %d\n",
699 command, pstatus-64);
700 ret = pstatus - 64;
701 } else {
702 ret = pstatus;
704 return ret;
705 } else if (WIFSIGNALED(status)) {
706 fprintf(stderr, "%s terminated with signal %d\n",
707 command, WTERMSIG(status));
708 return EINTR;
711 return 0;
715 * Command Functions
718 static int control_version(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
719 int argc, const char **argv)
721 printf("%s\n", ctdb_version_string);
722 return 0;
725 static bool partially_online(TALLOC_CTX *mem_ctx,
726 struct ctdb_context *ctdb,
727 struct ctdb_node_and_flags *node)
729 struct ctdb_iface_list *iface_list;
730 int ret, i;
731 bool status = false;
733 if (node->flags != 0) {
734 return false;
737 ret = ctdb_ctrl_get_ifaces(mem_ctx, ctdb->ev, ctdb->client,
738 node->pnn, TIMEOUT(), &iface_list);
739 if (ret != 0) {
740 return false;
743 status = false;
744 for (i=0; i < iface_list->num; i++) {
745 if (iface_list->iface[i].link_state == 0) {
746 status = true;
747 break;
751 return status;
754 static void print_nodemap_machine(TALLOC_CTX *mem_ctx,
755 struct ctdb_context *ctdb,
756 struct ctdb_node_map *nodemap,
757 uint32_t mypnn)
759 struct ctdb_node_and_flags *node;
760 int i;
762 printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
763 options.sep,
764 "Node", options.sep,
765 "IP", options.sep,
766 "Disconnected", options.sep,
767 "Banned", options.sep,
768 "Disabled", options.sep,
769 "Unhealthy", options.sep,
770 "Stopped", options.sep,
771 "Inactive", options.sep,
772 "PartiallyOnline", options.sep,
773 "ThisNode", options.sep);
775 for (i=0; i<nodemap->num; i++) {
776 node = &nodemap->node[i];
777 if (node->flags & NODE_FLAGS_DELETED) {
778 continue;
781 printf("%s%u%s%s%s%d%s%d%s%d%s%d%s%d%s%d%s%d%s%c%s\n",
782 options.sep,
783 node->pnn, options.sep,
784 ctdb_sock_addr_to_string(mem_ctx, &node->addr, false),
785 options.sep,
786 !! (node->flags & NODE_FLAGS_DISCONNECTED), options.sep,
787 !! (node->flags & NODE_FLAGS_BANNED), options.sep,
788 !! (node->flags & NODE_FLAGS_PERMANENTLY_DISABLED),
789 options.sep,
790 !! (node->flags & NODE_FLAGS_UNHEALTHY), options.sep,
791 !! (node->flags & NODE_FLAGS_STOPPED), options.sep,
792 !! (node->flags & NODE_FLAGS_INACTIVE), options.sep,
793 partially_online(mem_ctx, ctdb, node), options.sep,
794 (node->pnn == mypnn)?'Y':'N', options.sep);
799 static void print_nodemap(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
800 struct ctdb_node_map *nodemap, uint32_t mypnn,
801 bool print_header)
803 struct ctdb_node_and_flags *node;
804 int num_deleted_nodes = 0;
805 int i;
807 for (i=0; i<nodemap->num; i++) {
808 if (nodemap->node[i].flags & NODE_FLAGS_DELETED) {
809 num_deleted_nodes++;
813 if (print_header) {
814 if (num_deleted_nodes == 0) {
815 printf("Number of nodes:%d\n", nodemap->num);
816 } else {
817 printf("Number of nodes:%d "
818 "(including %d deleted nodes)\n",
819 nodemap->num, num_deleted_nodes);
823 for (i=0; i<nodemap->num; i++) {
824 node = &nodemap->node[i];
825 if (node->flags & NODE_FLAGS_DELETED) {
826 continue;
829 printf("pnn:%u %-16s %s%s\n",
830 node->pnn,
831 ctdb_sock_addr_to_string(mem_ctx, &node->addr, false),
832 partially_online(mem_ctx, ctdb, node) ?
833 "PARTIALLYONLINE" :
834 pretty_print_flags(mem_ctx, node->flags),
835 node->pnn == mypnn ? " (THIS NODE)" : "");
839 static void print_status(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
840 struct ctdb_node_map *nodemap, uint32_t mypnn,
841 struct ctdb_vnn_map *vnnmap, int recmode,
842 uint32_t recmaster)
844 int i;
846 print_nodemap(mem_ctx, ctdb, nodemap, mypnn, true);
848 if (vnnmap->generation == INVALID_GENERATION) {
849 printf("Generation:INVALID\n");
850 } else {
851 printf("Generation:%u\n", vnnmap->generation);
853 printf("Size:%d\n", vnnmap->size);
854 for (i=0; i<vnnmap->size; i++) {
855 printf("hash:%d lmaster:%d\n", i, vnnmap->map[i]);
858 printf("Recovery mode:%s (%d)\n",
859 recmode == CTDB_RECOVERY_NORMAL ? "NORMAL" : "RECOVERY",
860 recmode);
861 printf("Recovery master:%d\n", recmaster);
864 static int control_status(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
865 int argc, const char **argv)
867 struct ctdb_node_map *nodemap;
868 struct ctdb_vnn_map *vnnmap;
869 int recmode;
870 uint32_t recmaster;
871 int ret;
873 if (argc != 0) {
874 usage("status");
877 nodemap = get_nodemap(ctdb, false);
878 if (nodemap == NULL) {
879 return 1;
882 if (options.machinereadable == 1) {
883 print_nodemap_machine(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn);
884 return 0;
887 ret = ctdb_ctrl_getvnnmap(mem_ctx, ctdb->ev, ctdb->client,
888 ctdb->cmd_pnn, TIMEOUT(), &vnnmap);
889 if (ret != 0) {
890 return ret;
893 ret = ctdb_ctrl_get_recmode(mem_ctx, ctdb->ev, ctdb->client,
894 ctdb->cmd_pnn, TIMEOUT(), &recmode);
895 if (ret != 0) {
896 return ret;
899 ret = ctdb_ctrl_get_recmaster(mem_ctx, ctdb->ev, ctdb->client,
900 ctdb->cmd_pnn, TIMEOUT(), &recmaster);
901 if (ret != 0) {
902 return ret;
905 print_status(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn, vnnmap,
906 recmode, recmaster);
907 return 0;
910 static int control_uptime(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
911 int argc, const char **argv)
913 struct ctdb_uptime *uptime;
914 int ret, tmp, days, hours, minutes, seconds;
916 ret = ctdb_ctrl_uptime(mem_ctx, ctdb->ev, ctdb->client,
917 ctdb->cmd_pnn, TIMEOUT(), &uptime);
918 if (ret != 0) {
919 return ret;
922 printf("Current time of node %-4u : %s",
923 ctdb->cmd_pnn, ctime(&uptime->current_time.tv_sec));
925 tmp = uptime->current_time.tv_sec - uptime->ctdbd_start_time.tv_sec;
926 seconds = tmp % 60; tmp /= 60;
927 minutes = tmp % 60; tmp /= 60;
928 hours = tmp % 24; tmp /= 24;
929 days = tmp;
931 printf("Ctdbd start time : (%03d %02d:%02d:%02d) %s",
932 days, hours, minutes, seconds,
933 ctime(&uptime->ctdbd_start_time.tv_sec));
935 tmp = uptime->current_time.tv_sec - uptime->last_recovery_finished.tv_sec;
936 seconds = tmp % 60; tmp /= 60;
937 minutes = tmp % 60; tmp /= 60;
938 hours = tmp % 24; tmp /= 24;
939 days = tmp;
941 printf("Time of last recovery/failover: (%03d %02d:%02d:%02d) %s",
942 days, hours, minutes, seconds,
943 ctime(&uptime->last_recovery_finished.tv_sec));
945 printf("Duration of last recovery/failover: %lf seconds\n",
946 timeval_delta(&uptime->last_recovery_finished,
947 &uptime->last_recovery_started));
949 return 0;
952 static int control_ping(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
953 int argc, const char **argv)
955 struct timeval tv;
956 int ret, num_clients;
958 tv = timeval_current();
959 ret = ctdb_ctrl_ping(mem_ctx, ctdb->ev, ctdb->client,
960 ctdb->cmd_pnn, TIMEOUT(), &num_clients);
961 if (ret != 0) {
962 return ret;
965 printf("response from %u time=%.6f sec (%d clients)\n",
966 ctdb->cmd_pnn, timeval_elapsed(&tv), num_clients);
967 return 0;
970 const char *runstate_to_string(enum ctdb_runstate runstate);
971 enum ctdb_runstate runstate_from_string(const char *runstate_str);
973 static int control_runstate(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
974 int argc, const char **argv)
976 enum ctdb_runstate runstate;
977 bool found;
978 int ret, i;
980 ret = ctdb_ctrl_get_runstate(mem_ctx, ctdb->ev, ctdb->client,
981 ctdb->cmd_pnn, TIMEOUT(), &runstate);
982 if (ret != 0) {
983 return ret;
986 found = true;
987 for (i=0; i<argc; i++) {
988 enum ctdb_runstate t;
990 found = false;
991 t = ctdb_runstate_from_string(argv[i]);
992 if (t == CTDB_RUNSTATE_UNKNOWN) {
993 printf("Invalid run state (%s)\n", argv[i]);
994 return 1;
997 if (t == runstate) {
998 found = true;
999 break;
1003 if (! found) {
1004 printf("CTDB not in required run state (got %s)\n",
1005 ctdb_runstate_to_string(runstate));
1006 return 1;
1009 printf("%s\n", ctdb_runstate_to_string(runstate));
1010 return 0;
1013 static int control_getvar(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1014 int argc, const char **argv)
1016 struct ctdb_var_list *tun_var_list;
1017 uint32_t value;
1018 int ret, i;
1019 bool found;
1021 if (argc != 1) {
1022 usage("getvar");
1025 ret = ctdb_ctrl_list_tunables(mem_ctx, ctdb->ev, ctdb->client,
1026 ctdb->cmd_pnn, TIMEOUT(), &tun_var_list);
1027 if (ret != 0) {
1028 fprintf(stderr,
1029 "Failed to get list of variables from node %u\n",
1030 ctdb->cmd_pnn);
1031 return ret;
1034 found = false;
1035 for (i=0; i<tun_var_list->count; i++) {
1036 if (strcasecmp(tun_var_list->var[i], argv[0]) == 0) {
1037 found = true;
1038 break;
1042 if (! found) {
1043 printf("No such tunable %s\n", argv[0]);
1044 return 1;
1047 ret = ctdb_ctrl_get_tunable(mem_ctx, ctdb->ev, ctdb->client,
1048 ctdb->cmd_pnn, TIMEOUT(), argv[0], &value);
1049 if (ret != 0) {
1050 return ret;
1053 printf("%-26s = %u\n", argv[0], value);
1054 return 0;
1057 static int control_setvar(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1058 int argc, const char **argv)
1060 struct ctdb_var_list *tun_var_list;
1061 struct ctdb_tunable tunable;
1062 int ret, i;
1063 bool found;
1065 if (argc != 2) {
1066 usage("setvar");
1069 ret = ctdb_ctrl_list_tunables(mem_ctx, ctdb->ev, ctdb->client,
1070 ctdb->cmd_pnn, TIMEOUT(), &tun_var_list);
1071 if (ret != 0) {
1072 fprintf(stderr,
1073 "Failed to get list of variables from node %u\n",
1074 ctdb->cmd_pnn);
1075 return ret;
1078 found = false;
1079 for (i=0; i<tun_var_list->count; i++) {
1080 if (strcasecmp(tun_var_list->var[i], argv[0]) == 0) {
1081 found = true;
1082 break;
1086 if (! found) {
1087 printf("No such tunable %s\n", argv[0]);
1088 return 1;
1091 tunable.name = argv[0];
1092 tunable.value = strtoul(argv[1], NULL, 0);
1094 ret = ctdb_ctrl_set_tunable(mem_ctx, ctdb->ev, ctdb->client,
1095 ctdb->cmd_pnn, TIMEOUT(), &tunable);
1096 if (ret != 0) {
1097 if (ret == 1) {
1098 fprintf(stderr,
1099 "Setting obsolete tunable variable '%s'\n",
1100 tunable.name);
1101 return 0;
1105 return ret;
1108 static int control_listvars(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1109 int argc, const char **argv)
1111 struct ctdb_var_list *tun_var_list;
1112 int ret, i;
1114 if (argc != 0) {
1115 usage("listvars");
1118 ret = ctdb_ctrl_list_tunables(mem_ctx, ctdb->ev, ctdb->client,
1119 ctdb->cmd_pnn, TIMEOUT(), &tun_var_list);
1120 if (ret != 0) {
1121 return ret;
1124 for (i=0; i<tun_var_list->count; i++) {
1125 control_getvar(mem_ctx, ctdb, 1, &tun_var_list->var[i]);
1128 return 0;
1131 const struct {
1132 const char *name;
1133 uint32_t offset;
1134 } stats_fields[] = {
1135 #define STATISTICS_FIELD(n) { #n, offsetof(struct ctdb_statistics, n) }
1136 STATISTICS_FIELD(num_clients),
1137 STATISTICS_FIELD(frozen),
1138 STATISTICS_FIELD(recovering),
1139 STATISTICS_FIELD(num_recoveries),
1140 STATISTICS_FIELD(client_packets_sent),
1141 STATISTICS_FIELD(client_packets_recv),
1142 STATISTICS_FIELD(node_packets_sent),
1143 STATISTICS_FIELD(node_packets_recv),
1144 STATISTICS_FIELD(keepalive_packets_sent),
1145 STATISTICS_FIELD(keepalive_packets_recv),
1146 STATISTICS_FIELD(node.req_call),
1147 STATISTICS_FIELD(node.reply_call),
1148 STATISTICS_FIELD(node.req_dmaster),
1149 STATISTICS_FIELD(node.reply_dmaster),
1150 STATISTICS_FIELD(node.reply_error),
1151 STATISTICS_FIELD(node.req_message),
1152 STATISTICS_FIELD(node.req_control),
1153 STATISTICS_FIELD(node.reply_control),
1154 STATISTICS_FIELD(node.req_tunnel),
1155 STATISTICS_FIELD(client.req_call),
1156 STATISTICS_FIELD(client.req_message),
1157 STATISTICS_FIELD(client.req_control),
1158 STATISTICS_FIELD(client.req_tunnel),
1159 STATISTICS_FIELD(timeouts.call),
1160 STATISTICS_FIELD(timeouts.control),
1161 STATISTICS_FIELD(timeouts.traverse),
1162 STATISTICS_FIELD(locks.num_calls),
1163 STATISTICS_FIELD(locks.num_current),
1164 STATISTICS_FIELD(locks.num_pending),
1165 STATISTICS_FIELD(locks.num_failed),
1166 STATISTICS_FIELD(total_calls),
1167 STATISTICS_FIELD(pending_calls),
1168 STATISTICS_FIELD(childwrite_calls),
1169 STATISTICS_FIELD(pending_childwrite_calls),
1170 STATISTICS_FIELD(memory_used),
1171 STATISTICS_FIELD(max_hop_count),
1172 STATISTICS_FIELD(total_ro_delegations),
1173 STATISTICS_FIELD(total_ro_revokes),
1176 #define LATENCY_AVG(v) ((v).num ? (v).total / (v).num : 0.0 )
1178 static void print_statistics_machine(struct ctdb_statistics *s,
1179 bool show_header)
1181 int i;
1183 if (show_header) {
1184 printf("CTDB version%s", options.sep);
1185 printf("Current time of statistics%s", options.sep);
1186 printf("Statistics collected since%s", options.sep);
1187 for (i=0; i<ARRAY_SIZE(stats_fields); i++) {
1188 printf("%s%s", stats_fields[i].name, options.sep);
1190 printf("num_reclock_ctdbd_latency%s", options.sep);
1191 printf("min_reclock_ctdbd_latency%s", options.sep);
1192 printf("avg_reclock_ctdbd_latency%s", options.sep);
1193 printf("max_reclock_ctdbd_latency%s", options.sep);
1195 printf("num_reclock_recd_latency%s", options.sep);
1196 printf("min_reclock_recd_latency%s", options.sep);
1197 printf("avg_reclock_recd_latency%s", options.sep);
1198 printf("max_reclock_recd_latency%s", options.sep);
1200 printf("num_call_latency%s", options.sep);
1201 printf("min_call_latency%s", options.sep);
1202 printf("avg_call_latency%s", options.sep);
1203 printf("max_call_latency%s", options.sep);
1205 printf("num_lockwait_latency%s", options.sep);
1206 printf("min_lockwait_latency%s", options.sep);
1207 printf("avg_lockwait_latency%s", options.sep);
1208 printf("max_lockwait_latency%s", options.sep);
1210 printf("num_childwrite_latency%s", options.sep);
1211 printf("min_childwrite_latency%s", options.sep);
1212 printf("avg_childwrite_latency%s", options.sep);
1213 printf("max_childwrite_latency%s", options.sep);
1214 printf("\n");
1217 printf("%u%s", CTDB_PROTOCOL, options.sep);
1218 printf("%u%s", (uint32_t)s->statistics_current_time.tv_sec, options.sep);
1219 printf("%u%s", (uint32_t)s->statistics_start_time.tv_sec, options.sep);
1220 for (i=0;i<ARRAY_SIZE(stats_fields);i++) {
1221 printf("%u%s",
1222 *(uint32_t *)(stats_fields[i].offset+(uint8_t *)s),
1223 options.sep);
1225 printf("%u%s", s->reclock.ctdbd.num, options.sep);
1226 printf("%.6f%s", s->reclock.ctdbd.min, options.sep);
1227 printf("%.6f%s", LATENCY_AVG(s->reclock.ctdbd), options.sep);
1228 printf("%.6f%s", s->reclock.ctdbd.max, options.sep);
1230 printf("%u%s", s->reclock.recd.num, options.sep);
1231 printf("%.6f%s", s->reclock.recd.min, options.sep);
1232 printf("%.6f%s", LATENCY_AVG(s->reclock.recd), options.sep);
1233 printf("%.6f%s", s->reclock.recd.max, options.sep);
1235 printf("%d%s", s->call_latency.num, options.sep);
1236 printf("%.6f%s", s->call_latency.min, options.sep);
1237 printf("%.6f%s", LATENCY_AVG(s->call_latency), options.sep);
1238 printf("%.6f%s", s->call_latency.max, options.sep);
1240 printf("%d%s", s->childwrite_latency.num, options.sep);
1241 printf("%.6f%s", s->childwrite_latency.min, options.sep);
1242 printf("%.6f%s", LATENCY_AVG(s->childwrite_latency), options.sep);
1243 printf("%.6f%s", s->childwrite_latency.max, options.sep);
1244 printf("\n");
1247 static void print_statistics(struct ctdb_statistics *s)
1249 int tmp, days, hours, minutes, seconds;
1250 int i;
1251 const char *prefix = NULL;
1252 int preflen = 0;
1254 tmp = s->statistics_current_time.tv_sec -
1255 s->statistics_start_time.tv_sec;
1256 seconds = tmp % 60; tmp /= 60;
1257 minutes = tmp % 60; tmp /= 60;
1258 hours = tmp % 24; tmp /= 24;
1259 days = tmp;
1261 printf("CTDB version %u\n", CTDB_PROTOCOL);
1262 printf("Current time of statistics : %s",
1263 ctime(&s->statistics_current_time.tv_sec));
1264 printf("Statistics collected since : (%03d %02d:%02d:%02d) %s",
1265 days, hours, minutes, seconds,
1266 ctime(&s->statistics_start_time.tv_sec));
1268 for (i=0; i<ARRAY_SIZE(stats_fields); i++) {
1269 if (strchr(stats_fields[i].name, '.') != NULL) {
1270 preflen = strcspn(stats_fields[i].name, ".") + 1;
1271 if (! prefix ||
1272 strncmp(prefix, stats_fields[i].name, preflen) != 0) {
1273 prefix = stats_fields[i].name;
1274 printf(" %*.*s\n", preflen-1, preflen-1,
1275 stats_fields[i].name);
1277 } else {
1278 preflen = 0;
1280 printf(" %*s%-22s%*s%10u\n", preflen ? 4 : 0, "",
1281 stats_fields[i].name+preflen, preflen ? 0 : 4, "",
1282 *(uint32_t *)(stats_fields[i].offset+(uint8_t *)s));
1285 printf(" hop_count_buckets:");
1286 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
1287 printf(" %d", s->hop_count_bucket[i]);
1289 printf("\n");
1290 printf(" lock_buckets:");
1291 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
1292 printf(" %d", s->locks.buckets[i]);
1294 printf("\n");
1295 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1296 "locks_latency MIN/AVG/MAX",
1297 s->locks.latency.min, LATENCY_AVG(s->locks.latency),
1298 s->locks.latency.max, s->locks.latency.num);
1300 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1301 "reclock_ctdbd MIN/AVG/MAX",
1302 s->reclock.ctdbd.min, LATENCY_AVG(s->reclock.ctdbd),
1303 s->reclock.ctdbd.max, s->reclock.ctdbd.num);
1305 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1306 "reclock_recd MIN/AVG/MAX",
1307 s->reclock.recd.min, LATENCY_AVG(s->reclock.recd),
1308 s->reclock.recd.max, s->reclock.recd.num);
1310 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1311 "call_latency MIN/AVG/MAX",
1312 s->call_latency.min, LATENCY_AVG(s->call_latency),
1313 s->call_latency.max, s->call_latency.num);
1315 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1316 "childwrite_latency MIN/AVG/MAX",
1317 s->childwrite_latency.min,
1318 LATENCY_AVG(s->childwrite_latency),
1319 s->childwrite_latency.max, s->childwrite_latency.num);
1322 static int control_statistics(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1323 int argc, const char **argv)
1325 struct ctdb_statistics *stats;
1326 int ret;
1328 if (argc != 0) {
1329 usage("statistics");
1332 ret = ctdb_ctrl_statistics(mem_ctx, ctdb->ev, ctdb->client,
1333 ctdb->cmd_pnn, TIMEOUT(), &stats);
1334 if (ret != 0) {
1335 return ret;
1338 if (options.machinereadable) {
1339 print_statistics_machine(stats, true);
1340 } else {
1341 print_statistics(stats);
1344 return 0;
1347 static int control_statistics_reset(TALLOC_CTX *mem_ctx,
1348 struct ctdb_context *ctdb,
1349 int argc, const char **argv)
1351 int ret;
1353 if (argc != 0) {
1354 usage("statisticsreset");
1357 ret = ctdb_ctrl_statistics_reset(mem_ctx, ctdb->ev, ctdb->client,
1358 ctdb->cmd_pnn, TIMEOUT());
1359 if (ret != 0) {
1360 return ret;
1363 return 0;
1366 static int control_stats(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1367 int argc, const char **argv)
1369 struct ctdb_statistics_list *slist;
1370 int ret, count = 0, i;
1371 bool show_header = true;
1373 if (argc > 1) {
1374 usage("stats");
1377 if (argc == 1) {
1378 count = atoi(argv[0]);
1381 ret = ctdb_ctrl_get_stat_history(mem_ctx, ctdb->ev, ctdb->client,
1382 ctdb->cmd_pnn, TIMEOUT(), &slist);
1383 if (ret != 0) {
1384 return ret;
1387 for (i=0; i<slist->num; i++) {
1388 if (slist->stats[i].statistics_start_time.tv_sec == 0) {
1389 continue;
1391 if (options.machinereadable == 1) {
1392 print_statistics_machine(&slist->stats[i],
1393 show_header);
1394 show_header = false;
1395 } else {
1396 print_statistics(&slist->stats[i]);
1398 if (count > 0 && i == count) {
1399 break;
1403 return 0;
1406 static int ctdb_public_ip_cmp(const void *a, const void *b)
1408 const struct ctdb_public_ip *ip_a = a;
1409 const struct ctdb_public_ip *ip_b = b;
1411 return ctdb_sock_addr_cmp(&ip_a->addr, &ip_b->addr);
1414 static void print_ip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1415 struct ctdb_public_ip_list *ips,
1416 struct ctdb_public_ip_info **ipinfo,
1417 bool all_nodes)
1419 int i, j;
1420 char *conf, *avail, *active;
1422 if (options.machinereadable == 1) {
1423 printf("%s%s%s%s%s", options.sep,
1424 "Public IP", options.sep,
1425 "Node", options.sep);
1426 if (options.verbose == 1) {
1427 printf("%s%s%s%s%s%s\n",
1428 "ActiveInterfaces", options.sep,
1429 "AvailableInterfaces", options.sep,
1430 "ConfiguredInterfaces", options.sep);
1431 } else {
1432 printf("\n");
1434 } else {
1435 if (all_nodes) {
1436 printf("Public IPs on ALL nodes\n");
1437 } else {
1438 printf("Public IPs on node %u\n", ctdb->cmd_pnn);
1442 for (i = 0; i < ips->num; i++) {
1444 if (options.machinereadable == 1) {
1445 printf("%s%s%s%d%s", options.sep,
1446 ctdb_sock_addr_to_string(
1447 mem_ctx, &ips->ip[i].addr, false),
1448 options.sep,
1449 (int)ips->ip[i].pnn, options.sep);
1450 } else {
1451 printf("%s", ctdb_sock_addr_to_string(
1452 mem_ctx, &ips->ip[i].addr, false));
1455 if (options.verbose == 0) {
1456 if (options.machinereadable == 1) {
1457 printf("\n");
1458 } else {
1459 printf(" %d\n", (int)ips->ip[i].pnn);
1461 continue;
1464 conf = NULL;
1465 avail = NULL;
1466 active = NULL;
1468 if (ipinfo[i] == NULL) {
1469 goto skip_ipinfo;
1472 for (j=0; j<ipinfo[i]->ifaces->num; j++) {
1473 struct ctdb_iface *iface;
1475 iface = &ipinfo[i]->ifaces->iface[j];
1476 if (conf == NULL) {
1477 conf = talloc_strdup(mem_ctx, iface->name);
1478 } else {
1479 conf = talloc_asprintf_append(
1480 conf, ",%s", iface->name);
1483 if (ipinfo[i]->active_idx == j) {
1484 active = iface->name;
1487 if (iface->link_state == 0) {
1488 continue;
1491 if (avail == NULL) {
1492 avail = talloc_strdup(mem_ctx, iface->name);
1493 } else {
1494 avail = talloc_asprintf_append(
1495 avail, ",%s", iface->name);
1499 skip_ipinfo:
1501 if (options.machinereadable == 1) {
1502 printf("%s%s%s%s%s%s\n",
1503 active ? active : "", options.sep,
1504 avail ? avail : "", options.sep,
1505 conf ? conf : "", options.sep);
1506 } else {
1507 printf(" node[%d] active[%s] available[%s]"
1508 " configured[%s]\n",
1509 (int)ips->ip[i].pnn, active ? active : "",
1510 avail ? avail : "", conf ? conf : "");
1515 static int collect_ips(uint8_t *keybuf, size_t keylen, uint8_t *databuf,
1516 size_t datalen, void *private_data)
1518 struct ctdb_public_ip_list *ips = talloc_get_type_abort(
1519 private_data, struct ctdb_public_ip_list);
1520 struct ctdb_public_ip *ip;
1522 ip = (struct ctdb_public_ip *)databuf;
1523 ips->ip[ips->num] = *ip;
1524 ips->num += 1;
1526 return 0;
1529 static int get_all_public_ips(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx,
1530 struct ctdb_public_ip_list **out)
1532 struct ctdb_node_map *nodemap;
1533 struct ctdb_public_ip_list *ips;
1534 struct db_hash_context *ipdb;
1535 uint32_t *pnn_list;
1536 int ret, count, i, j;
1538 nodemap = get_nodemap(ctdb, false);
1539 if (nodemap == NULL) {
1540 return 1;
1543 ret = db_hash_init(mem_ctx, "ips", 101, DB_HASH_COMPLEX, &ipdb);
1544 if (ret != 0) {
1545 goto failed;
1548 count = list_of_active_nodes(nodemap, CTDB_UNKNOWN_PNN, mem_ctx,
1549 &pnn_list);
1550 if (count <= 0) {
1551 goto failed;
1554 for (i=0; i<count; i++) {
1555 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
1556 pnn_list[i], TIMEOUT(),
1557 false, &ips);
1558 if (ret != 0) {
1559 goto failed;
1562 for (j=0; j<ips->num; j++) {
1563 struct ctdb_public_ip ip;
1565 ip.pnn = ips->ip[j].pnn;
1566 ip.addr = ips->ip[j].addr;
1568 if (pnn_list[i] == ip.pnn) {
1569 /* Node claims IP is hosted on it, so
1570 * save that information
1572 ret = db_hash_add(ipdb, (uint8_t *)&ip.addr,
1573 sizeof(ip.addr),
1574 (uint8_t *)&ip, sizeof(ip));
1575 if (ret != 0) {
1576 goto failed;
1578 } else {
1579 /* Node thinks IP is hosted elsewhere,
1580 * so overwrite with CTDB_UNKNOWN_PNN
1581 * if there's no existing entry
1583 ret = db_hash_exists(ipdb, (uint8_t *)&ip.addr,
1584 sizeof(ip.addr));
1585 if (ret == ENOENT) {
1586 ip.pnn = CTDB_UNKNOWN_PNN;
1587 ret = db_hash_add(ipdb,
1588 (uint8_t *)&ip.addr,
1589 sizeof(ip.addr),
1590 (uint8_t *)&ip,
1591 sizeof(ip));
1592 if (ret != 0) {
1593 goto failed;
1599 TALLOC_FREE(ips);
1602 talloc_free(pnn_list);
1604 ret = db_hash_traverse(ipdb, NULL, NULL, &count);
1605 if (ret != 0) {
1606 goto failed;
1609 ips = talloc_zero(mem_ctx, struct ctdb_public_ip_list);
1610 if (ips == NULL) {
1611 goto failed;
1614 ips->ip = talloc_array(ips, struct ctdb_public_ip, count);
1615 if (ips->ip == NULL) {
1616 goto failed;
1619 ret = db_hash_traverse(ipdb, collect_ips, ips, &count);
1620 if (ret != 0) {
1621 goto failed;
1624 if (count != ips->num) {
1625 goto failed;
1628 talloc_free(ipdb);
1630 *out = ips;
1631 return 0;
1633 failed:
1634 talloc_free(ipdb);
1635 return 1;
1638 static int control_ip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1639 int argc, const char **argv)
1641 struct ctdb_public_ip_list *ips;
1642 struct ctdb_public_ip_info **ipinfo;
1643 int ret, i;
1644 bool do_all = false;
1646 if (argc > 1) {
1647 usage("ip");
1650 if (argc == 1) {
1651 if (strcmp(argv[0], "all") == 0) {
1652 do_all = true;
1653 } else {
1654 usage("ip");
1658 if (do_all) {
1659 ret = get_all_public_ips(ctdb, mem_ctx, &ips);
1660 } else {
1661 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
1662 ctdb->cmd_pnn, TIMEOUT(),
1663 false, &ips);
1665 if (ret != 0) {
1666 return ret;
1669 qsort(ips->ip, ips->num, sizeof(struct ctdb_public_ip),
1670 ctdb_public_ip_cmp);
1672 ipinfo = talloc_array(mem_ctx, struct ctdb_public_ip_info *, ips->num);
1673 if (ipinfo == NULL) {
1674 return 1;
1677 for (i=0; i<ips->num; i++) {
1678 uint32_t pnn;
1679 if (do_all) {
1680 pnn = ips->ip[i].pnn;
1681 } else {
1682 pnn = ctdb->cmd_pnn;
1684 if (pnn == CTDB_UNKNOWN_PNN) {
1685 ipinfo[i] = NULL;
1686 continue;
1688 ret = ctdb_ctrl_get_public_ip_info(mem_ctx, ctdb->ev,
1689 ctdb->client, pnn,
1690 TIMEOUT(), &ips->ip[i].addr,
1691 &ipinfo[i]);
1692 if (ret != 0) {
1693 return ret;
1697 print_ip(mem_ctx, ctdb, ips, ipinfo, do_all);
1698 return 0;
1701 static int control_ipinfo(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1702 int argc, const char **argv)
1704 struct ctdb_public_ip_info *ipinfo;
1705 ctdb_sock_addr addr;
1706 int ret, i;
1708 if (argc != 1) {
1709 usage("ipinfo");
1712 ret = ctdb_sock_addr_from_string(argv[0], &addr, false);
1713 if (ret != 0) {
1714 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
1715 return 1;
1718 ret = ctdb_ctrl_get_public_ip_info(mem_ctx, ctdb->ev, ctdb->client,
1719 ctdb->cmd_pnn, TIMEOUT(), &addr,
1720 &ipinfo);
1721 if (ret != 0) {
1722 if (ret == -1) {
1723 printf("Node %u does not know about IP %s\n",
1724 ctdb->cmd_pnn, argv[0]);
1726 return ret;
1729 printf("Public IP[%s] info on node %u\n",
1730 ctdb_sock_addr_to_string(mem_ctx, &ipinfo->ip.addr, false),
1731 ctdb->cmd_pnn);
1733 printf("IP:%s\nCurrentNode:%u\nNumInterfaces:%u\n",
1734 ctdb_sock_addr_to_string(mem_ctx, &ipinfo->ip.addr, false),
1735 ipinfo->ip.pnn, ipinfo->ifaces->num);
1737 for (i=0; i<ipinfo->ifaces->num; i++) {
1738 struct ctdb_iface *iface;
1740 iface = &ipinfo->ifaces->iface[i];
1741 iface->name[CTDB_IFACE_SIZE] = '\0';
1742 printf("Interface[%u]: Name:%s Link:%s References:%u%s\n",
1743 i+1, iface->name,
1744 iface->link_state == 0 ? "down" : "up",
1745 iface->references,
1746 (i == ipinfo->active_idx) ? " (active)" : "");
1749 return 0;
1752 static int control_ifaces(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1753 int argc, const char **argv)
1755 struct ctdb_iface_list *ifaces;
1756 int ret, i;
1758 if (argc != 0) {
1759 usage("ifaces");
1762 ret = ctdb_ctrl_get_ifaces(mem_ctx, ctdb->ev, ctdb->client,
1763 ctdb->cmd_pnn, TIMEOUT(), &ifaces);
1764 if (ret != 0) {
1765 return ret;
1768 if (ifaces->num == 0) {
1769 printf("No interfaces configured on node %u\n",
1770 ctdb->cmd_pnn);
1771 return 0;
1774 if (options.machinereadable) {
1775 printf("%s%s%s%s%s%s%s\n", options.sep,
1776 "Name", options.sep,
1777 "LinkStatus", options.sep,
1778 "References", options.sep);
1779 } else {
1780 printf("Interfaces on node %u\n", ctdb->cmd_pnn);
1783 for (i=0; i<ifaces->num; i++) {
1784 if (options.machinereadable) {
1785 printf("%s%s%s%u%s%u%s\n", options.sep,
1786 ifaces->iface[i].name, options.sep,
1787 ifaces->iface[i].link_state, options.sep,
1788 ifaces->iface[i].references, options.sep);
1789 } else {
1790 printf("name:%s link:%s references:%u\n",
1791 ifaces->iface[i].name,
1792 ifaces->iface[i].link_state ? "up" : "down",
1793 ifaces->iface[i].references);
1797 return 0;
1800 static int control_setifacelink(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1801 int argc, const char **argv)
1803 struct ctdb_iface_list *ifaces;
1804 struct ctdb_iface *iface;
1805 int ret, i;
1807 if (argc != 2) {
1808 usage("setifacelink");
1811 if (strlen(argv[0]) > CTDB_IFACE_SIZE) {
1812 fprintf(stderr, "Interface name '%s' too long\n", argv[0]);
1813 return 1;
1816 ret = ctdb_ctrl_get_ifaces(mem_ctx, ctdb->ev, ctdb->client,
1817 ctdb->cmd_pnn, TIMEOUT(), &ifaces);
1818 if (ret != 0) {
1819 fprintf(stderr,
1820 "Failed to get interface information from node %u\n",
1821 ctdb->cmd_pnn);
1822 return ret;
1825 iface = NULL;
1826 for (i=0; i<ifaces->num; i++) {
1827 if (strcmp(ifaces->iface[i].name, argv[0]) == 0) {
1828 iface = &ifaces->iface[i];
1829 break;
1833 if (iface == NULL) {
1834 printf("Interface %s not configured on node %u\n",
1835 argv[0], ctdb->cmd_pnn);
1836 return 1;
1839 if (strcmp(argv[1], "up") == 0) {
1840 iface->link_state = 1;
1841 } else if (strcmp(argv[1], "down") == 0) {
1842 iface->link_state = 0;
1843 } else {
1844 usage("setifacelink");
1845 return 1;
1848 iface->references = 0;
1850 ret = ctdb_ctrl_set_iface_link_state(mem_ctx, ctdb->ev, ctdb->client,
1851 ctdb->cmd_pnn, TIMEOUT(), iface);
1852 if (ret != 0) {
1853 return ret;
1856 return 0;
1859 static int control_process_exists(TALLOC_CTX *mem_ctx,
1860 struct ctdb_context *ctdb,
1861 int argc, const char **argv)
1863 pid_t pid;
1864 uint64_t srvid = 0;
1865 int ret, status;
1867 if (argc != 1 && argc != 2) {
1868 usage("process-exists");
1871 pid = atoi(argv[0]);
1872 if (argc == 2) {
1873 srvid = strtoull(argv[1], NULL, 0);
1876 if (srvid == 0) {
1877 ret = ctdb_ctrl_process_exists(mem_ctx, ctdb->ev, ctdb->client,
1878 ctdb->cmd_pnn, TIMEOUT(), pid, &status);
1879 } else {
1880 struct ctdb_pid_srvid pid_srvid;
1882 pid_srvid.pid = pid;
1883 pid_srvid.srvid = srvid;
1885 ret = ctdb_ctrl_check_pid_srvid(mem_ctx, ctdb->ev,
1886 ctdb->client, ctdb->cmd_pnn,
1887 TIMEOUT(), &pid_srvid,
1888 &status);
1891 if (ret != 0) {
1892 return ret;
1895 if (srvid == 0) {
1896 printf("PID %d %s\n", pid,
1897 (status == 0 ? "exists" : "does not exist"));
1898 } else {
1899 printf("PID %d with SRVID 0x%"PRIx64" %s\n", pid, srvid,
1900 (status == 0 ? "exists" : "does not exist"));
1902 return status;
1905 static int control_getdbmap(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1906 int argc, const char **argv)
1908 struct ctdb_dbid_map *dbmap;
1909 int ret, i;
1911 if (argc != 0) {
1912 usage("getdbmap");
1915 ret = ctdb_ctrl_get_dbmap(mem_ctx, ctdb->ev, ctdb->client,
1916 ctdb->cmd_pnn, TIMEOUT(), &dbmap);
1917 if (ret != 0) {
1918 return ret;
1921 if (options.machinereadable == 1) {
1922 printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
1923 options.sep,
1924 "ID", options.sep,
1925 "Name", options.sep,
1926 "Path", options.sep,
1927 "Persistent", options.sep,
1928 "Sticky", options.sep,
1929 "Unhealthy", options.sep,
1930 "Readonly", options.sep,
1931 "Replicated", options.sep);
1932 } else {
1933 printf("Number of databases:%d\n", dbmap->num);
1936 for (i=0; i<dbmap->num; i++) {
1937 const char *name;
1938 const char *path;
1939 const char *health;
1940 bool persistent;
1941 bool readonly;
1942 bool sticky;
1943 bool replicated;
1944 uint32_t db_id;
1946 db_id = dbmap->dbs[i].db_id;
1948 ret = ctdb_ctrl_get_dbname(mem_ctx, ctdb->ev, ctdb->client,
1949 ctdb->cmd_pnn, TIMEOUT(), db_id,
1950 &name);
1951 if (ret != 0) {
1952 return ret;
1955 ret = ctdb_ctrl_getdbpath(mem_ctx, ctdb->ev, ctdb->client,
1956 ctdb->cmd_pnn, TIMEOUT(), db_id,
1957 &path);
1958 if (ret != 0) {
1959 return ret;
1962 ret = ctdb_ctrl_db_get_health(mem_ctx, ctdb->ev, ctdb->client,
1963 ctdb->cmd_pnn, TIMEOUT(), db_id,
1964 &health);
1965 if (ret != 0) {
1966 return ret;
1969 persistent = dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT;
1970 readonly = dbmap->dbs[i].flags & CTDB_DB_FLAGS_READONLY;
1971 sticky = dbmap->dbs[i].flags & CTDB_DB_FLAGS_STICKY;
1972 replicated = dbmap->dbs[i].flags & CTDB_DB_FLAGS_REPLICATED;
1974 if (options.machinereadable == 1) {
1975 printf("%s0x%08X%s%s%s%s%s%d%s%d%s%d%s%d%s%d%s\n",
1976 options.sep,
1977 db_id, options.sep,
1978 name, options.sep,
1979 path, options.sep,
1980 !! (persistent), options.sep,
1981 !! (sticky), options.sep,
1982 !! (health), options.sep,
1983 !! (readonly), options.sep,
1984 !! (replicated), options.sep);
1985 } else {
1986 printf("dbid:0x%08x name:%s path:%s%s%s%s%s%s\n",
1987 db_id, name, path,
1988 persistent ? " PERSISTENT" : "",
1989 sticky ? " STICKY" : "",
1990 readonly ? " READONLY" : "",
1991 replicated ? " REPLICATED" : "",
1992 health ? " UNHEALTHY" : "");
1995 talloc_free(discard_const(name));
1996 talloc_free(discard_const(path));
1997 talloc_free(discard_const(health));
2000 return 0;
2003 static int control_getdbstatus(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2004 int argc, const char **argv)
2006 uint32_t db_id;
2007 const char *db_name, *db_path, *db_health;
2008 uint8_t db_flags;
2009 int ret;
2011 if (argc != 1) {
2012 usage("getdbstatus");
2015 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
2016 return 1;
2019 ret = ctdb_ctrl_getdbpath(mem_ctx, ctdb->ev, ctdb->client,
2020 ctdb->cmd_pnn, TIMEOUT(), db_id,
2021 &db_path);
2022 if (ret != 0) {
2023 return ret;
2026 ret = ctdb_ctrl_db_get_health(mem_ctx, ctdb->ev, ctdb->client,
2027 ctdb->cmd_pnn, TIMEOUT(), db_id,
2028 &db_health);
2029 if (ret != 0) {
2030 return ret;
2033 printf("dbid: 0x%08x\nname: %s\npath: %s\n", db_id, db_name, db_path);
2034 printf("PERSISTENT: %s\nREPLICATED: %s\nSTICKY: %s\nREADONLY: %s\n",
2035 (db_flags & CTDB_DB_FLAGS_PERSISTENT ? "yes" : "no"),
2036 (db_flags & CTDB_DB_FLAGS_REPLICATED ? "yes" : "no"),
2037 (db_flags & CTDB_DB_FLAGS_STICKY ? "yes" : "no"),
2038 (db_flags & CTDB_DB_FLAGS_READONLY ? "yes" : "no"));
2039 printf("HEALTH: %s\n", (db_health ? db_health : "OK"));
2040 return 0;
2043 struct dump_record_state {
2044 uint32_t count;
2047 #define ISASCII(x) (isprint(x) && ! strchr("\"\\", (x)))
2049 static void dump_tdb_data(const char *name, TDB_DATA val)
2051 int i;
2053 fprintf(stdout, "%s(%zu) = \"", name, val.dsize);
2054 for (i=0; i<val.dsize; i++) {
2055 if (ISASCII(val.dptr[i])) {
2056 fprintf(stdout, "%c", val.dptr[i]);
2057 } else {
2058 fprintf(stdout, "\\%02X", val.dptr[i]);
2061 fprintf(stdout, "\"\n");
2064 static void dump_ltdb_header(struct ctdb_ltdb_header *header)
2066 fprintf(stdout, "dmaster: %u\n", header->dmaster);
2067 fprintf(stdout, "rsn: %" PRIu64 "\n", header->rsn);
2068 fprintf(stdout, "flags: 0x%08x", header->flags);
2069 if (header->flags & CTDB_REC_FLAG_MIGRATED_WITH_DATA) {
2070 fprintf(stdout, " MIGRATED_WITH_DATA");
2072 if (header->flags & CTDB_REC_FLAG_VACUUM_MIGRATED) {
2073 fprintf(stdout, " VACUUM_MIGRATED");
2075 if (header->flags & CTDB_REC_FLAG_AUTOMATIC) {
2076 fprintf(stdout, " AUTOMATIC");
2078 if (header->flags & CTDB_REC_RO_HAVE_DELEGATIONS) {
2079 fprintf(stdout, " RO_HAVE_DELEGATIONS");
2081 if (header->flags & CTDB_REC_RO_HAVE_READONLY) {
2082 fprintf(stdout, " RO_HAVE_READONLY");
2084 if (header->flags & CTDB_REC_RO_REVOKING_READONLY) {
2085 fprintf(stdout, " RO_REVOKING_READONLY");
2087 if (header->flags & CTDB_REC_RO_REVOKE_COMPLETE) {
2088 fprintf(stdout, " RO_REVOKE_COMPLETE");
2090 fprintf(stdout, "\n");
2094 static int dump_record(uint32_t reqid, struct ctdb_ltdb_header *header,
2095 TDB_DATA key, TDB_DATA data, void *private_data)
2097 struct dump_record_state *state =
2098 (struct dump_record_state *)private_data;
2100 state->count += 1;
2102 dump_tdb_data("key", key);
2103 dump_ltdb_header(header);
2104 dump_tdb_data("data", data);
2105 fprintf(stdout, "\n");
2107 return 0;
2110 static int control_catdb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2111 int argc, const char **argv)
2113 struct ctdb_db_context *db;
2114 const char *db_name;
2115 uint32_t db_id;
2116 uint8_t db_flags;
2117 struct dump_record_state state;
2118 int ret;
2120 if (argc != 1) {
2121 usage("catdb");
2124 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
2125 return 1;
2128 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
2129 db_flags, &db);
2130 if (ret != 0) {
2131 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
2132 return ret;
2135 state.count = 0;
2137 ret = ctdb_db_traverse(mem_ctx, ctdb->ev, ctdb->client, db,
2138 ctdb->cmd_pnn, TIMEOUT(),
2139 dump_record, &state);
2141 printf("Dumped %u records\n", state.count);
2143 return ret;
2146 static int control_cattdb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2147 int argc, const char **argv)
2149 struct ctdb_db_context *db;
2150 const char *db_name;
2151 uint32_t db_id;
2152 uint8_t db_flags;
2153 struct dump_record_state state;
2154 int ret;
2156 if (argc != 1) {
2157 usage("catdb");
2160 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
2161 return 1;
2164 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
2165 db_flags, &db);
2166 if (ret != 0) {
2167 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
2168 return ret;
2171 state.count = 0;
2172 ret = ctdb_db_traverse_local(db, true, true, dump_record, &state);
2174 printf("Dumped %u record(s)\n", state.count);
2176 return ret;
2179 static int control_getcapabilities(TALLOC_CTX *mem_ctx,
2180 struct ctdb_context *ctdb,
2181 int argc, const char **argv)
2183 uint32_t caps;
2184 int ret;
2186 if (argc != 0) {
2187 usage("getcapabilities");
2190 ret = ctdb_ctrl_get_capabilities(mem_ctx, ctdb->ev, ctdb->client,
2191 ctdb->cmd_pnn, TIMEOUT(), &caps);
2192 if (ret != 0) {
2193 return ret;
2196 if (options.machinereadable == 1) {
2197 printf("%s%s%s%s%s\n",
2198 options.sep,
2199 "RECMASTER", options.sep,
2200 "LMASTER", options.sep);
2201 printf("%s%d%s%d%s\n", options.sep,
2202 !! (caps & CTDB_CAP_RECMASTER), options.sep,
2203 !! (caps & CTDB_CAP_LMASTER), options.sep);
2204 } else {
2205 printf("RECMASTER: %s\n",
2206 (caps & CTDB_CAP_RECMASTER) ? "YES" : "NO");
2207 printf("LMASTER: %s\n",
2208 (caps & CTDB_CAP_LMASTER) ? "YES" : "NO");
2211 return 0;
2214 static int control_pnn(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2215 int argc, const char **argv)
2217 printf("%u\n", ctdb_client_pnn(ctdb->client));
2218 return 0;
2221 static int control_lvs(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2222 int argc, const char **argv)
2224 char *t, *lvs_helper = NULL;
2226 if (argc != 1) {
2227 usage("lvs");
2230 t = getenv("CTDB_LVS_HELPER");
2231 if (t != NULL) {
2232 lvs_helper = talloc_strdup(mem_ctx, t);
2233 } else {
2234 lvs_helper = talloc_asprintf(mem_ctx, "%s/ctdb_lvs",
2235 CTDB_HELPER_BINDIR);
2238 if (lvs_helper == NULL) {
2239 fprintf(stderr, "Unable to set LVS helper\n");
2240 return 1;
2243 return run_helper(mem_ctx, "LVS helper", lvs_helper, argc, argv);
2246 static int control_setdebug(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2247 int argc, const char **argv)
2249 int log_level;
2250 int ret;
2251 bool found;
2253 if (argc != 1) {
2254 usage("setdebug");
2257 found = debug_level_parse(argv[0], &log_level);
2258 if (! found) {
2259 fprintf(stderr,
2260 "Invalid debug level '%s'. Valid levels are:\n",
2261 argv[0]);
2262 fprintf(stderr, "\tERROR | WARNING | NOTICE | INFO | DEBUG\n");
2263 return 1;
2266 ret = ctdb_ctrl_setdebug(mem_ctx, ctdb->ev, ctdb->client,
2267 ctdb->cmd_pnn, TIMEOUT(), log_level);
2268 if (ret != 0) {
2269 return ret;
2272 return 0;
2275 static int control_getdebug(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2276 int argc, const char **argv)
2278 int loglevel;
2279 const char *log_str;
2280 int ret;
2282 if (argc != 0) {
2283 usage("getdebug");
2286 ret = ctdb_ctrl_getdebug(mem_ctx, ctdb->ev, ctdb->client,
2287 ctdb->cmd_pnn, TIMEOUT(), &loglevel);
2288 if (ret != 0) {
2289 return ret;
2292 log_str = debug_level_to_string(loglevel);
2293 printf("%s\n", log_str);
2295 return 0;
2298 static int control_attach(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2299 int argc, const char **argv)
2301 const char *db_name;
2302 uint8_t db_flags = 0;
2303 int ret;
2305 if (argc < 1 || argc > 2) {
2306 usage("attach");
2309 db_name = argv[0];
2310 if (argc == 2) {
2311 if (strcmp(argv[1], "persistent") == 0) {
2312 db_flags = CTDB_DB_FLAGS_PERSISTENT;
2313 } else if (strcmp(argv[1], "readonly") == 0) {
2314 db_flags = CTDB_DB_FLAGS_READONLY;
2315 } else if (strcmp(argv[1], "sticky") == 0) {
2316 db_flags = CTDB_DB_FLAGS_STICKY;
2317 } else if (strcmp(argv[1], "replicated") == 0) {
2318 db_flags = CTDB_DB_FLAGS_REPLICATED;
2319 } else {
2320 usage("attach");
2324 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
2325 db_flags, NULL);
2326 if (ret != 0) {
2327 return ret;
2330 return 0;
2333 static int control_detach(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2334 int argc, const char **argv)
2336 const char *db_name;
2337 uint32_t db_id;
2338 uint8_t db_flags;
2339 struct ctdb_node_map *nodemap;
2340 int recmode;
2341 int ret, ret2, i;
2343 if (argc < 1) {
2344 usage("detach");
2347 ret = ctdb_ctrl_get_recmode(mem_ctx, ctdb->ev, ctdb->client,
2348 ctdb->cmd_pnn, TIMEOUT(), &recmode);
2349 if (ret != 0) {
2350 return ret;
2353 if (recmode == CTDB_RECOVERY_ACTIVE) {
2354 fprintf(stderr, "Database cannot be detached"
2355 " when recovery is active\n");
2356 return 1;
2359 nodemap = get_nodemap(ctdb, false);
2360 if (nodemap == NULL) {
2361 return 1;
2364 for (i=0; i<nodemap->num; i++) {
2365 uint32_t value;
2367 if (nodemap->node[i].flags & NODE_FLAGS_DISCONNECTED) {
2368 continue;
2370 if (nodemap->node[i].flags & NODE_FLAGS_DELETED) {
2371 continue;
2373 if (nodemap->node[i].flags & NODE_FLAGS_INACTIVE) {
2374 fprintf(stderr, "Database cannot be detached on"
2375 " inactive (stopped or banned) node %u\n",
2376 nodemap->node[i].pnn);
2377 return 1;
2380 ret = ctdb_ctrl_get_tunable(mem_ctx, ctdb->ev, ctdb->client,
2381 nodemap->node[i].pnn, TIMEOUT(),
2382 "AllowClientDBAttach", &value);
2383 if (ret != 0) {
2384 fprintf(stderr,
2385 "Unable to get tunable AllowClientDBAttach"
2386 " from node %u\n", nodemap->node[i].pnn);
2387 return ret;
2390 if (value == 1) {
2391 fprintf(stderr,
2392 "Database access is still active on node %u."
2393 " Set AllowclientDBAttach=0 on all nodes.\n",
2394 nodemap->node[i].pnn);
2395 return 1;
2399 ret2 = 0;
2400 for (i=0; i<argc; i++) {
2401 if (! db_exists(mem_ctx, ctdb, argv[i], &db_id, &db_name,
2402 &db_flags)) {
2403 continue;
2406 if (db_flags &
2407 (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
2408 fprintf(stderr,
2409 "Only volatile databases can be detached\n");
2410 return 1;
2413 ret = ctdb_detach(ctdb->ev, ctdb->client, TIMEOUT(), db_id);
2414 if (ret != 0) {
2415 fprintf(stderr, "Database %s detach failed\n", db_name);
2416 ret2 = ret;
2420 return ret2;
2423 static int control_dumpmemory(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2424 int argc, const char **argv)
2426 const char *mem_str;
2427 ssize_t n;
2428 int ret;
2430 ret = ctdb_ctrl_dump_memory(mem_ctx, ctdb->ev, ctdb->client,
2431 ctdb->cmd_pnn, TIMEOUT(), &mem_str);
2432 if (ret != 0) {
2433 return ret;
2436 n = write(1, mem_str, strlen(mem_str)+1);
2437 if (n < 0 || n != strlen(mem_str)+1) {
2438 fprintf(stderr, "Failed to write talloc summary\n");
2439 return 1;
2442 return 0;
2445 static void dump_memory(uint64_t srvid, TDB_DATA data, void *private_data)
2447 bool *done = (bool *)private_data;
2448 ssize_t n;
2450 n = write(1, data.dptr, data.dsize);
2451 if (n < 0 || n != data.dsize) {
2452 fprintf(stderr, "Failed to write talloc summary\n");
2455 *done = true;
2458 static int control_rddumpmemory(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2459 int argc, const char **argv)
2461 struct ctdb_srvid_message msg = { 0 };
2462 int ret;
2463 bool done = false;
2465 msg.pnn = ctdb->pnn;
2466 msg.srvid = next_srvid(ctdb);
2468 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
2469 msg.srvid, dump_memory, &done);
2470 if (ret != 0) {
2471 return ret;
2474 ret = ctdb_message_mem_dump(mem_ctx, ctdb->ev, ctdb->client,
2475 ctdb->cmd_pnn, &msg);
2476 if (ret != 0) {
2477 return ret;
2480 ctdb_client_wait(ctdb->ev, &done);
2481 return 0;
2484 static int control_getpid(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2485 int argc, const char **argv)
2487 pid_t pid;
2488 int ret;
2490 ret = ctdb_ctrl_get_pid(mem_ctx, ctdb->ev, ctdb->client,
2491 ctdb->cmd_pnn, TIMEOUT(), &pid);
2492 if (ret != 0) {
2493 return ret;
2496 printf("%u\n", pid);
2497 return 0;
2500 static int check_flags(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2501 const char *desc, uint32_t flag, bool set_flag)
2503 struct ctdb_node_map *nodemap;
2504 bool flag_is_set;
2506 nodemap = get_nodemap(ctdb, false);
2507 if (nodemap == NULL) {
2508 return 1;
2511 flag_is_set = nodemap->node[ctdb->cmd_pnn].flags & flag;
2512 if (set_flag == flag_is_set) {
2513 if (set_flag) {
2514 fprintf(stderr, "Node %u is already %s\n",
2515 ctdb->cmd_pnn, desc);
2516 } else {
2517 fprintf(stderr, "Node %u is not %s\n",
2518 ctdb->cmd_pnn, desc);
2520 return 0;
2523 return 1;
2526 static void wait_for_flags(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2527 uint32_t flag, bool set_flag)
2529 struct ctdb_node_map *nodemap;
2530 bool flag_is_set;
2532 while (1) {
2533 nodemap = get_nodemap(ctdb, true);
2534 if (nodemap == NULL) {
2535 fprintf(stderr,
2536 "Failed to get nodemap, trying again\n");
2537 sleep(1);
2538 continue;
2541 flag_is_set = nodemap->node[ctdb->cmd_pnn].flags & flag;
2542 if (flag_is_set == set_flag) {
2543 break;
2546 sleep(1);
2550 static int ctdb_ctrl_modflags(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
2551 struct ctdb_client_context *client,
2552 uint32_t destnode, struct timeval timeout,
2553 uint32_t set, uint32_t clear)
2555 struct ctdb_node_map *nodemap;
2556 struct ctdb_node_flag_change flag_change;
2557 struct ctdb_req_control request;
2558 uint32_t *pnn_list;
2559 int ret, count;
2561 ret = ctdb_ctrl_get_nodemap(mem_ctx, ev, client, destnode,
2562 tevent_timeval_zero(), &nodemap);
2563 if (ret != 0) {
2564 return ret;
2567 flag_change.pnn = destnode;
2568 flag_change.old_flags = nodemap->node[destnode].flags;
2569 flag_change.new_flags = flag_change.old_flags | set;
2570 flag_change.new_flags &= ~clear;
2572 count = list_of_connected_nodes(nodemap, -1, mem_ctx, &pnn_list);
2573 if (count == -1) {
2574 return ENOMEM;
2577 ctdb_req_control_modify_flags(&request, &flag_change);
2578 ret = ctdb_client_control_multi(mem_ctx, ev, client, pnn_list, count,
2579 tevent_timeval_zero(), &request,
2580 NULL, NULL);
2581 return ret;
2584 struct ipreallocate_state {
2585 int status;
2586 bool done;
2589 static void ipreallocate_handler(uint64_t srvid, TDB_DATA data,
2590 void *private_data)
2592 struct ipreallocate_state *state =
2593 (struct ipreallocate_state *)private_data;
2595 if (data.dsize != sizeof(int)) {
2596 /* Ignore packet */
2597 return;
2600 state->status = *(int *)data.dptr;
2601 state->done = true;
2604 static int ipreallocate(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb)
2606 struct ctdb_srvid_message msg = { 0 };
2607 struct ipreallocate_state state;
2608 int ret;
2610 msg.pnn = ctdb->pnn;
2611 msg.srvid = next_srvid(ctdb);
2613 state.done = false;
2614 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
2615 msg.srvid,
2616 ipreallocate_handler, &state);
2617 if (ret != 0) {
2618 return ret;
2621 while (true) {
2622 ret = ctdb_message_takeover_run(mem_ctx, ctdb->ev,
2623 ctdb->client,
2624 CTDB_BROADCAST_CONNECTED,
2625 &msg);
2626 if (ret != 0) {
2627 goto fail;
2630 ret = ctdb_client_wait_timeout(ctdb->ev, &state.done,
2631 TIMEOUT());
2632 if (ret != 0) {
2633 continue;
2636 if (state.status >= 0) {
2637 ret = 0;
2638 } else {
2639 ret = state.status;
2641 break;
2644 fail:
2645 ctdb_client_remove_message_handler(ctdb->ev, ctdb->client,
2646 msg.srvid, &state);
2647 return ret;
2650 static int control_disable(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2651 int argc, const char **argv)
2653 int ret;
2655 if (argc != 0) {
2656 usage("disable");
2659 ret = check_flags(mem_ctx, ctdb, "disabled",
2660 NODE_FLAGS_PERMANENTLY_DISABLED, true);
2661 if (ret == 0) {
2662 return 0;
2665 ret = ctdb_ctrl_modflags(mem_ctx, ctdb->ev, ctdb->client,
2666 ctdb->cmd_pnn, TIMEOUT(),
2667 NODE_FLAGS_PERMANENTLY_DISABLED, 0);
2668 if (ret != 0) {
2669 fprintf(stderr,
2670 "Failed to set DISABLED flag on node %u\n",
2671 ctdb->cmd_pnn);
2672 return ret;
2675 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_PERMANENTLY_DISABLED, true);
2676 return ipreallocate(mem_ctx, ctdb);
2679 static int control_enable(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2680 int argc, const char **argv)
2682 int ret;
2684 if (argc != 0) {
2685 usage("enable");
2688 ret = check_flags(mem_ctx, ctdb, "disabled",
2689 NODE_FLAGS_PERMANENTLY_DISABLED, false);
2690 if (ret == 0) {
2691 return 0;
2694 ret = ctdb_ctrl_modflags(mem_ctx, ctdb->ev, ctdb->client,
2695 ctdb->cmd_pnn, TIMEOUT(),
2696 0, NODE_FLAGS_PERMANENTLY_DISABLED);
2697 if (ret != 0) {
2698 fprintf(stderr, "Failed to reset DISABLED flag on node %u\n",
2699 ctdb->cmd_pnn);
2700 return ret;
2703 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_PERMANENTLY_DISABLED, false);
2704 return ipreallocate(mem_ctx, ctdb);
2707 static int control_stop(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2708 int argc, const char **argv)
2710 int ret;
2712 if (argc != 0) {
2713 usage("stop");
2716 ret = check_flags(mem_ctx, ctdb, "stopped",
2717 NODE_FLAGS_STOPPED, true);
2718 if (ret == 0) {
2719 return 0;
2722 ret = ctdb_ctrl_stop_node(mem_ctx, ctdb->ev, ctdb->client,
2723 ctdb->cmd_pnn, TIMEOUT());
2724 if (ret != 0) {
2725 fprintf(stderr, "Failed to stop node %u\n", ctdb->cmd_pnn);
2726 return ret;
2729 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_STOPPED, true);
2730 return ipreallocate(mem_ctx, ctdb);
2733 static int control_continue(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2734 int argc, const char **argv)
2736 int ret;
2738 if (argc != 0) {
2739 usage("continue");
2742 ret = check_flags(mem_ctx, ctdb, "stopped",
2743 NODE_FLAGS_STOPPED, false);
2744 if (ret == 0) {
2745 return 0;
2748 ret = ctdb_ctrl_continue_node(mem_ctx, ctdb->ev, ctdb->client,
2749 ctdb->cmd_pnn, TIMEOUT());
2750 if (ret != 0) {
2751 fprintf(stderr, "Failed to continue stopped node %u\n",
2752 ctdb->cmd_pnn);
2753 return ret;
2756 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_STOPPED, false);
2757 return ipreallocate(mem_ctx, ctdb);
2760 static int control_ban(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2761 int argc, const char **argv)
2763 struct ctdb_ban_state ban_state;
2764 int ret;
2766 if (argc != 1) {
2767 usage("ban");
2770 ret = check_flags(mem_ctx, ctdb, "banned",
2771 NODE_FLAGS_BANNED, true);
2772 if (ret == 0) {
2773 return 0;
2776 ban_state.pnn = ctdb->cmd_pnn;
2777 ban_state.time = strtoul(argv[0], NULL, 0);
2779 if (ban_state.time == 0) {
2780 fprintf(stderr, "Ban time cannot be zero\n");
2781 return EINVAL;
2784 ret = ctdb_ctrl_set_ban_state(mem_ctx, ctdb->ev, ctdb->client,
2785 ctdb->cmd_pnn, TIMEOUT(), &ban_state);
2786 if (ret != 0) {
2787 fprintf(stderr, "Failed to ban node %u\n", ctdb->cmd_pnn);
2788 return ret;
2791 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_BANNED, true);
2792 return ipreallocate(mem_ctx, ctdb);
2796 static int control_unban(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2797 int argc, const char **argv)
2799 struct ctdb_ban_state ban_state;
2800 int ret;
2802 if (argc != 0) {
2803 usage("unban");
2806 ret = check_flags(mem_ctx, ctdb, "banned",
2807 NODE_FLAGS_BANNED, false);
2808 if (ret == 0) {
2809 return 0;
2812 ban_state.pnn = ctdb->cmd_pnn;
2813 ban_state.time = 0;
2815 ret = ctdb_ctrl_set_ban_state(mem_ctx, ctdb->ev, ctdb->client,
2816 ctdb->cmd_pnn, TIMEOUT(), &ban_state);
2817 if (ret != 0) {
2818 fprintf(stderr, "Failed to unban node %u\n", ctdb->cmd_pnn);
2819 return ret;
2822 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_BANNED, false);
2823 return ipreallocate(mem_ctx, ctdb);
2827 static void wait_for_shutdown(void *private_data)
2829 bool *done = (bool *)private_data;
2831 *done = true;
2834 static int control_shutdown(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2835 int argc, const char **argv)
2837 int ret;
2838 bool done = false;
2840 if (argc != 0) {
2841 usage("shutdown");
2844 if (ctdb->pnn == ctdb->cmd_pnn) {
2845 ctdb_client_set_disconnect_callback(ctdb->client,
2846 wait_for_shutdown,
2847 &done);
2850 ret = ctdb_ctrl_shutdown(mem_ctx, ctdb->ev, ctdb->client,
2851 ctdb->cmd_pnn, TIMEOUT());
2852 if (ret != 0) {
2853 fprintf(stderr, "Unable to shutdown node %u\n", ctdb->cmd_pnn);
2854 return ret;
2857 if (ctdb->pnn == ctdb->cmd_pnn) {
2858 ctdb_client_wait(ctdb->ev, &done);
2861 return 0;
2864 static int get_generation(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2865 uint32_t *generation)
2867 uint32_t recmaster;
2868 int recmode;
2869 struct ctdb_vnn_map *vnnmap;
2870 int ret;
2872 again:
2873 ret = ctdb_ctrl_get_recmaster(mem_ctx, ctdb->ev, ctdb->client,
2874 ctdb->cmd_pnn, TIMEOUT(), &recmaster);
2875 if (ret != 0) {
2876 fprintf(stderr, "Failed to find recovery master\n");
2877 return ret;
2880 ret = ctdb_ctrl_get_recmode(mem_ctx, ctdb->ev, ctdb->client,
2881 recmaster, TIMEOUT(), &recmode);
2882 if (ret != 0) {
2883 fprintf(stderr, "Failed to get recovery mode from node %u\n",
2884 recmaster);
2885 return ret;
2888 if (recmode == CTDB_RECOVERY_ACTIVE) {
2889 sleep(1);
2890 goto again;
2893 ret = ctdb_ctrl_getvnnmap(mem_ctx, ctdb->ev, ctdb->client,
2894 recmaster, TIMEOUT(), &vnnmap);
2895 if (ret != 0) {
2896 fprintf(stderr, "Failed to get generation from node %u\n",
2897 recmaster);
2898 return ret;
2901 if (vnnmap->generation == INVALID_GENERATION) {
2902 talloc_free(vnnmap);
2903 sleep(1);
2904 goto again;
2907 *generation = vnnmap->generation;
2908 talloc_free(vnnmap);
2909 return 0;
2913 static int control_recover(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2914 int argc, const char **argv)
2916 uint32_t generation, next_generation;
2917 int ret;
2919 if (argc != 0) {
2920 usage("recover");
2923 ret = get_generation(mem_ctx, ctdb, &generation);
2924 if (ret != 0) {
2925 return ret;
2928 ret = ctdb_ctrl_set_recmode(mem_ctx, ctdb->ev, ctdb->client,
2929 ctdb->cmd_pnn, TIMEOUT(),
2930 CTDB_RECOVERY_ACTIVE);
2931 if (ret != 0) {
2932 fprintf(stderr, "Failed to set recovery mode active\n");
2933 return ret;
2936 while (1) {
2937 ret = get_generation(mem_ctx, ctdb, &next_generation);
2938 if (ret != 0) {
2939 fprintf(stderr,
2940 "Failed to confirm end of recovery\n");
2941 return ret;
2944 if (next_generation != generation) {
2945 break;
2948 sleep (1);
2951 return 0;
2954 static int control_ipreallocate(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2955 int argc, const char **argv)
2957 if (argc != 0) {
2958 usage("ipreallocate");
2961 return ipreallocate(mem_ctx, ctdb);
2964 static int control_isnotrecmaster(TALLOC_CTX *mem_ctx,
2965 struct ctdb_context *ctdb,
2966 int argc, const char **argv)
2968 uint32_t recmaster;
2969 int ret;
2971 if (argc != 0) {
2972 usage("isnotrecmaster");
2975 ret = ctdb_ctrl_get_recmaster(mem_ctx, ctdb->ev, ctdb->client,
2976 ctdb->pnn, TIMEOUT(), &recmaster);
2977 if (ret != 0) {
2978 fprintf(stderr, "Failed to get recmaster\n");
2979 return ret;
2982 if (recmaster != ctdb->pnn) {
2983 printf("this node is not the recmaster\n");
2984 return 1;
2987 printf("this node is the recmaster\n");
2988 return 0;
2991 static int control_gratarp(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2992 int argc, const char **argv)
2994 struct ctdb_addr_info addr_info;
2995 int ret;
2997 if (argc != 2) {
2998 usage("gratarp");
3001 ret = ctdb_sock_addr_from_string(argv[0], &addr_info.addr, false);
3002 if (ret != 0) {
3003 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3004 return 1;
3006 addr_info.iface = argv[1];
3008 ret = ctdb_ctrl_send_gratuitous_arp(mem_ctx, ctdb->ev, ctdb->client,
3009 ctdb->cmd_pnn, TIMEOUT(),
3010 &addr_info);
3011 if (ret != 0) {
3012 fprintf(stderr, "Unable to send gratuitous arp from node %u\n",
3013 ctdb->cmd_pnn);
3014 return ret;
3017 return 0;
3020 static int control_tickle(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3021 int argc, const char **argv)
3023 ctdb_sock_addr src, dst;
3024 int ret;
3026 if (argc != 0 && argc != 2) {
3027 usage("tickle");
3030 if (argc == 0) {
3031 struct ctdb_connection_list *clist;
3032 int i;
3033 unsigned int num_failed;
3035 /* Client first but the src/dst logic is confused */
3036 ret = ctdb_connection_list_read(mem_ctx, 0, false, &clist);
3037 if (ret != 0) {
3038 return ret;
3041 num_failed = 0;
3042 for (i = 0; i < clist->num; i++) {
3043 ret = ctdb_sys_send_tcp(&clist->conn[i].src,
3044 &clist->conn[i].dst,
3045 0, 0, 0);
3046 if (ret != 0) {
3047 num_failed += 1;
3051 TALLOC_FREE(clist);
3053 if (num_failed > 0) {
3054 fprintf(stderr, "Failed to send %d tickles\n",
3055 num_failed);
3056 return 1;
3059 return 0;
3063 ret = ctdb_sock_addr_from_string(argv[0], &src, true);
3064 if (ret != 0) {
3065 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3066 return 1;
3069 ret = ctdb_sock_addr_from_string(argv[1], &dst, true);
3070 if (ret != 0) {
3071 fprintf(stderr, "Invalid IP address %s\n", argv[1]);
3072 return 1;
3075 ret = ctdb_sys_send_tcp(&src, &dst, 0, 0, 0);
3076 if (ret != 0) {
3077 fprintf(stderr, "Failed to send tickle ack\n");
3078 return ret;
3081 return 0;
3084 static int control_gettickles(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3085 int argc, const char **argv)
3087 ctdb_sock_addr addr;
3088 struct ctdb_tickle_list *tickles;
3089 unsigned port = 0;
3090 int ret, i;
3092 if (argc < 1 || argc > 2) {
3093 usage("gettickles");
3096 if (argc == 2) {
3097 port = strtoul(argv[1], NULL, 10);
3100 ret = ctdb_sock_addr_from_string(argv[0], &addr, false);
3101 if (ret != 0) {
3102 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3103 return 1;
3105 ctdb_sock_addr_set_port(&addr, port);
3107 ret = ctdb_ctrl_get_tcp_tickle_list(mem_ctx, ctdb->ev, ctdb->client,
3108 ctdb->cmd_pnn, TIMEOUT(), &addr,
3109 &tickles);
3110 if (ret != 0) {
3111 fprintf(stderr, "Failed to get list of connections\n");
3112 return ret;
3115 if (options.machinereadable) {
3116 printf("%s%s%s%s%s%s%s%s%s\n",
3117 options.sep,
3118 "Source IP", options.sep,
3119 "Port", options.sep,
3120 "Destiation IP", options.sep,
3121 "Port", options.sep);
3122 for (i=0; i<tickles->num; i++) {
3123 printf("%s%s%s%u%s%s%s%u%s\n", options.sep,
3124 ctdb_sock_addr_to_string(
3125 mem_ctx, &tickles->conn[i].src, false),
3126 options.sep,
3127 ntohs(tickles->conn[i].src.ip.sin_port),
3128 options.sep,
3129 ctdb_sock_addr_to_string(
3130 mem_ctx, &tickles->conn[i].dst, false),
3131 options.sep,
3132 ntohs(tickles->conn[i].dst.ip.sin_port),
3133 options.sep);
3135 } else {
3136 printf("Connections for IP: %s\n",
3137 ctdb_sock_addr_to_string(mem_ctx,
3138 &tickles->addr, false));
3139 printf("Num connections: %u\n", tickles->num);
3140 for (i=0; i<tickles->num; i++) {
3141 printf("SRC: %s DST: %s\n",
3142 ctdb_sock_addr_to_string(
3143 mem_ctx, &tickles->conn[i].src, true),
3144 ctdb_sock_addr_to_string(
3145 mem_ctx, &tickles->conn[i].dst, true));
3149 talloc_free(tickles);
3150 return 0;
3153 typedef void (*clist_request_func)(struct ctdb_req_control *request,
3154 struct ctdb_connection *conn);
3156 typedef int (*clist_reply_func)(struct ctdb_reply_control *reply);
3158 struct process_clist_state {
3159 struct ctdb_connection_list *clist;
3160 int count;
3161 int num_failed, num_total;
3162 clist_reply_func reply_func;
3165 static void process_clist_done(struct tevent_req *subreq);
3167 static struct tevent_req *process_clist_send(
3168 TALLOC_CTX *mem_ctx,
3169 struct ctdb_context *ctdb,
3170 struct ctdb_connection_list *clist,
3171 clist_request_func request_func,
3172 clist_reply_func reply_func)
3174 struct tevent_req *req, *subreq;
3175 struct process_clist_state *state;
3176 struct ctdb_req_control request;
3177 int i;
3179 req = tevent_req_create(mem_ctx, &state, struct process_clist_state);
3180 if (req == NULL) {
3181 return NULL;
3184 state->clist = clist;
3185 state->reply_func = reply_func;
3187 for (i = 0; i < clist->num; i++) {
3188 request_func(&request, &clist->conn[i]);
3189 subreq = ctdb_client_control_send(state, ctdb->ev,
3190 ctdb->client, ctdb->cmd_pnn,
3191 TIMEOUT(), &request);
3192 if (tevent_req_nomem(subreq, req)) {
3193 return tevent_req_post(req, ctdb->ev);
3195 tevent_req_set_callback(subreq, process_clist_done, req);
3198 return req;
3201 static void process_clist_done(struct tevent_req *subreq)
3203 struct tevent_req *req = tevent_req_callback_data(
3204 subreq, struct tevent_req);
3205 struct process_clist_state *state = tevent_req_data(
3206 req, struct process_clist_state);
3207 struct ctdb_reply_control *reply;
3208 int ret;
3209 bool status;
3211 status = ctdb_client_control_recv(subreq, NULL, state, &reply);
3212 TALLOC_FREE(subreq);
3213 if (! status) {
3214 state->num_failed += 1;
3215 goto done;
3218 ret = state->reply_func(reply);
3219 if (ret != 0) {
3220 state->num_failed += 1;
3221 goto done;
3224 done:
3225 state->num_total += 1;
3226 if (state->num_total == state->clist->num) {
3227 tevent_req_done(req);
3231 static int process_clist_recv(struct tevent_req *req)
3233 struct process_clist_state *state = tevent_req_data(
3234 req, struct process_clist_state);
3236 return state->num_failed;
3239 static int control_addtickle(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3240 int argc, const char **argv)
3242 struct ctdb_connection conn;
3243 int ret;
3245 if (argc != 0 && argc != 2) {
3246 usage("addtickle");
3249 if (argc == 0) {
3250 struct ctdb_connection_list *clist;
3251 struct tevent_req *req;
3253 /* Client first but the src/dst logic is confused */
3254 ret = ctdb_connection_list_read(mem_ctx, 0, false, &clist);
3255 if (ret != 0) {
3256 return ret;
3258 if (clist->num == 0) {
3259 return 0;
3262 req = process_clist_send(mem_ctx, ctdb, clist,
3263 ctdb_req_control_tcp_add_delayed_update,
3264 ctdb_reply_control_tcp_add_delayed_update);
3265 if (req == NULL) {
3266 talloc_free(clist);
3267 return ENOMEM;
3270 tevent_req_poll(req, ctdb->ev);
3271 talloc_free(clist);
3273 ret = process_clist_recv(req);
3274 if (ret != 0) {
3275 fprintf(stderr, "Failed to add %d tickles\n", ret);
3276 return 1;
3279 return 0;
3282 ret = ctdb_sock_addr_from_string(argv[0], &conn.src, true);
3283 if (ret != 0) {
3284 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3285 return 1;
3287 ret = ctdb_sock_addr_from_string(argv[1], &conn.dst, true);
3288 if (ret != 0) {
3289 fprintf(stderr, "Invalid IP address %s\n", argv[1]);
3290 return 1;
3293 ret = ctdb_ctrl_tcp_add_delayed_update(mem_ctx, ctdb->ev,
3294 ctdb->client, ctdb->cmd_pnn,
3295 TIMEOUT(), &conn);
3296 if (ret != 0) {
3297 fprintf(stderr, "Failed to register connection\n");
3298 return ret;
3301 return 0;
3304 static int control_deltickle(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3305 int argc, const char **argv)
3307 struct ctdb_connection conn;
3308 int ret;
3310 if (argc != 0 && argc != 2) {
3311 usage("deltickle");
3314 if (argc == 0) {
3315 struct ctdb_connection_list *clist;
3316 struct tevent_req *req;
3318 /* Client first but the src/dst logic is confused */
3319 ret = ctdb_connection_list_read(mem_ctx, 0, false, &clist);
3320 if (ret != 0) {
3321 return ret;
3323 if (clist->num == 0) {
3324 return 0;
3327 req = process_clist_send(mem_ctx, ctdb, clist,
3328 ctdb_req_control_tcp_remove,
3329 ctdb_reply_control_tcp_remove);
3330 if (req == NULL) {
3331 talloc_free(clist);
3332 return ENOMEM;
3335 tevent_req_poll(req, ctdb->ev);
3336 talloc_free(clist);
3338 ret = process_clist_recv(req);
3339 if (ret != 0) {
3340 fprintf(stderr, "Failed to remove %d tickles\n", ret);
3341 return 1;
3344 return 0;
3347 ret = ctdb_sock_addr_from_string(argv[0], &conn.src, true);
3348 if (ret != 0) {
3349 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3350 return 1;
3352 ret = ctdb_sock_addr_from_string(argv[1], &conn.dst, true);
3353 if (ret != 0) {
3354 fprintf(stderr, "Invalid IP address %s\n", argv[1]);
3355 return 1;
3358 ret = ctdb_ctrl_tcp_remove(mem_ctx, ctdb->ev, ctdb->client,
3359 ctdb->cmd_pnn, TIMEOUT(), &conn);
3360 if (ret != 0) {
3361 fprintf(stderr, "Failed to unregister connection\n");
3362 return ret;
3365 return 0;
3368 static int control_listnodes(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3369 int argc, const char **argv)
3371 struct ctdb_node_map *nodemap;
3372 int i;
3374 if (argc != 0) {
3375 usage("listnodes");
3378 nodemap = read_nodes_file(mem_ctx, CTDB_UNKNOWN_PNN);
3379 if (nodemap == NULL) {
3380 return 1;
3383 for (i=0; i<nodemap->num; i++) {
3384 if (nodemap->node[i].flags & NODE_FLAGS_DELETED) {
3385 continue;
3388 if (options.machinereadable) {
3389 printf("%s%u%s%s%s\n", options.sep,
3390 nodemap->node[i].pnn, options.sep,
3391 ctdb_sock_addr_to_string(
3392 mem_ctx, &nodemap->node[i].addr, false),
3393 options.sep);
3394 } else {
3395 printf("%s\n",
3396 ctdb_sock_addr_to_string(
3397 mem_ctx, &nodemap->node[i].addr, false));
3401 return 0;
3404 static bool nodemap_identical(struct ctdb_node_map *nodemap1,
3405 struct ctdb_node_map *nodemap2)
3407 int i;
3409 if (nodemap1->num != nodemap2->num) {
3410 return false;
3413 for (i=0; i<nodemap1->num; i++) {
3414 struct ctdb_node_and_flags *n1, *n2;
3416 n1 = &nodemap1->node[i];
3417 n2 = &nodemap2->node[i];
3419 if ((n1->pnn != n2->pnn) ||
3420 (n1->flags != n2->flags) ||
3421 ! ctdb_sock_addr_same_ip(&n1->addr, &n2->addr)) {
3422 return false;
3426 return true;
3429 static int check_node_file_changes(TALLOC_CTX *mem_ctx,
3430 struct ctdb_node_map *nm,
3431 struct ctdb_node_map *fnm,
3432 bool *reload)
3434 int i;
3435 bool check_failed = false;
3437 *reload = false;
3439 for (i=0; i<nm->num; i++) {
3440 if (i >= fnm->num) {
3441 fprintf(stderr,
3442 "Node %u (%s) missing from nodes file\n",
3443 nm->node[i].pnn,
3444 ctdb_sock_addr_to_string(
3445 mem_ctx, &nm->node[i].addr, false));
3446 check_failed = true;
3447 continue;
3449 if (nm->node[i].flags & NODE_FLAGS_DELETED &&
3450 fnm->node[i].flags & NODE_FLAGS_DELETED) {
3451 /* Node remains deleted */
3452 continue;
3455 if (! (nm->node[i].flags & NODE_FLAGS_DELETED) &&
3456 ! (fnm->node[i].flags & NODE_FLAGS_DELETED)) {
3457 /* Node not newly nor previously deleted */
3458 if (! ctdb_same_ip(&nm->node[i].addr,
3459 &fnm->node[i].addr)) {
3460 fprintf(stderr,
3461 "Node %u has changed IP address"
3462 " (was %s, now %s)\n",
3463 nm->node[i].pnn,
3464 ctdb_sock_addr_to_string(
3465 mem_ctx,
3466 &nm->node[i].addr, false),
3467 ctdb_sock_addr_to_string(
3468 mem_ctx,
3469 &fnm->node[i].addr, false));
3470 check_failed = true;
3471 } else {
3472 if (nm->node[i].flags & NODE_FLAGS_DISCONNECTED) {
3473 fprintf(stderr,
3474 "WARNING: Node %u is disconnected."
3475 " You MUST fix this node manually!\n",
3476 nm->node[i].pnn);
3479 continue;
3482 if (fnm->node[i].flags & NODE_FLAGS_DELETED) {
3483 /* Node is being deleted */
3484 printf("Node %u is DELETED\n", nm->node[i].pnn);
3485 *reload = true;
3486 if (! (nm->node[i].flags & NODE_FLAGS_DISCONNECTED)) {
3487 fprintf(stderr,
3488 "ERROR: Node %u is still connected\n",
3489 nm->node[i].pnn);
3490 check_failed = true;
3492 continue;
3495 if (nm->node[i].flags & NODE_FLAGS_DELETED) {
3496 /* Node was previously deleted */
3497 printf("Node %u is UNDELETED\n", nm->node[i].pnn);
3498 *reload = true;
3502 if (check_failed) {
3503 fprintf(stderr,
3504 "ERROR: Nodes will not be reloaded due to previous error\n");
3505 return 1;
3508 /* Leftover nodes in file are NEW */
3509 for (; i < fnm->num; i++) {
3510 printf("Node %u is NEW\n", fnm->node[i].pnn);
3511 *reload = true;
3514 return 0;
3517 struct disable_recoveries_state {
3518 uint32_t *pnn_list;
3519 int node_count;
3520 bool *reply;
3521 int status;
3522 bool done;
3525 static void disable_recoveries_handler(uint64_t srvid, TDB_DATA data,
3526 void *private_data)
3528 struct disable_recoveries_state *state =
3529 (struct disable_recoveries_state *)private_data;
3530 int ret, i;
3532 if (data.dsize != sizeof(int)) {
3533 /* Ignore packet */
3534 return;
3537 /* ret will be a PNN (i.e. >=0) on success, or negative on error */
3538 ret = *(int *)data.dptr;
3539 if (ret < 0) {
3540 state->status = ret;
3541 state->done = true;
3542 return;
3544 for (i=0; i<state->node_count; i++) {
3545 if (state->pnn_list[i] == ret) {
3546 state->reply[i] = true;
3547 break;
3551 state->done = true;
3552 for (i=0; i<state->node_count; i++) {
3553 if (! state->reply[i]) {
3554 state->done = false;
3555 break;
3560 static int disable_recoveries(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3561 uint32_t timeout, uint32_t *pnn_list, int count)
3563 struct ctdb_disable_message disable = { 0 };
3564 struct disable_recoveries_state state;
3565 int ret, i;
3567 disable.pnn = ctdb->pnn;
3568 disable.srvid = next_srvid(ctdb);
3569 disable.timeout = timeout;
3571 state.pnn_list = pnn_list;
3572 state.node_count = count;
3573 state.done = false;
3574 state.status = 0;
3575 state.reply = talloc_zero_array(mem_ctx, bool, count);
3576 if (state.reply == NULL) {
3577 return ENOMEM;
3580 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
3581 disable.srvid,
3582 disable_recoveries_handler,
3583 &state);
3584 if (ret != 0) {
3585 return ret;
3588 for (i=0; i<count; i++) {
3589 ret = ctdb_message_disable_recoveries(mem_ctx, ctdb->ev,
3590 ctdb->client,
3591 pnn_list[i],
3592 &disable);
3593 if (ret != 0) {
3594 goto fail;
3598 ret = ctdb_client_wait_timeout(ctdb->ev, &state.done, TIMEOUT());
3599 if (ret == ETIME) {
3600 fprintf(stderr, "Timed out waiting to disable recoveries\n");
3601 } else {
3602 ret = (state.status >= 0 ? 0 : 1);
3605 fail:
3606 ctdb_client_remove_message_handler(ctdb->ev, ctdb->client,
3607 disable.srvid, &state);
3608 return ret;
3611 static int control_reloadnodes(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3612 int argc, const char **argv)
3614 struct ctdb_node_map *nodemap = NULL;
3615 struct ctdb_node_map *file_nodemap;
3616 struct ctdb_node_map *remote_nodemap;
3617 struct ctdb_req_control request;
3618 struct ctdb_reply_control **reply;
3619 bool reload;
3620 int ret, i;
3621 uint32_t *pnn_list;
3622 int count;
3624 nodemap = get_nodemap(ctdb, false);
3625 if (nodemap == NULL) {
3626 return 1;
3629 file_nodemap = read_nodes_file(mem_ctx, ctdb->pnn);
3630 if (file_nodemap == NULL) {
3631 return 1;
3634 for (i=0; i<nodemap->num; i++) {
3635 if (nodemap->node[i].flags & NODE_FLAGS_DISCONNECTED) {
3636 continue;
3639 ret = ctdb_ctrl_get_nodes_file(mem_ctx, ctdb->ev, ctdb->client,
3640 nodemap->node[i].pnn, TIMEOUT(),
3641 &remote_nodemap);
3642 if (ret != 0) {
3643 fprintf(stderr,
3644 "ERROR: Failed to get nodes file from node %u\n",
3645 nodemap->node[i].pnn);
3646 return ret;
3649 if (! nodemap_identical(file_nodemap, remote_nodemap)) {
3650 fprintf(stderr,
3651 "ERROR: Nodes file on node %u differs"
3652 " from current node (%u)\n",
3653 nodemap->node[i].pnn, ctdb->pnn);
3654 return 1;
3658 ret = check_node_file_changes(mem_ctx, nodemap, file_nodemap, &reload);
3659 if (ret != 0) {
3660 return ret;
3663 if (! reload) {
3664 fprintf(stderr, "No change in nodes file,"
3665 " skipping unnecessary reload\n");
3666 return 0;
3669 count = list_of_connected_nodes(nodemap, CTDB_UNKNOWN_PNN,
3670 mem_ctx, &pnn_list);
3671 if (count <= 0) {
3672 fprintf(stderr, "Memory allocation error\n");
3673 return 1;
3676 ret = disable_recoveries(mem_ctx, ctdb, 2*options.timelimit,
3677 pnn_list, count);
3678 if (ret != 0) {
3679 fprintf(stderr, "Failed to disable recoveries\n");
3680 return ret;
3683 ctdb_req_control_reload_nodes_file(&request);
3684 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
3685 pnn_list, count, TIMEOUT(),
3686 &request, NULL, &reply);
3687 if (ret != 0) {
3688 bool failed = false;
3690 for (i=0; i<count; i++) {
3691 ret = ctdb_reply_control_reload_nodes_file(reply[i]);
3692 if (ret != 0) {
3693 fprintf(stderr,
3694 "Node %u failed to reload nodes\n",
3695 pnn_list[i]);
3696 failed = true;
3699 if (failed) {
3700 fprintf(stderr,
3701 "You MUST fix failed nodes manually!\n");
3705 ret = disable_recoveries(mem_ctx, ctdb, 0, pnn_list, count);
3706 if (ret != 0) {
3707 fprintf(stderr, "Failed to enable recoveries\n");
3708 return ret;
3711 return 0;
3714 static int moveip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3715 ctdb_sock_addr *addr, uint32_t pnn)
3717 struct ctdb_public_ip_list *pubip_list;
3718 struct ctdb_public_ip pubip;
3719 struct ctdb_node_map *nodemap;
3720 struct ctdb_req_control request;
3721 uint32_t *pnn_list;
3722 int ret, i, count;
3724 ret = ctdb_message_disable_ip_check(mem_ctx, ctdb->ev, ctdb->client,
3725 CTDB_BROADCAST_CONNECTED,
3726 2*options.timelimit);
3727 if (ret != 0) {
3728 fprintf(stderr, "Failed to disable IP check\n");
3729 return ret;
3732 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
3733 pnn, TIMEOUT(), false, &pubip_list);
3734 if (ret != 0) {
3735 fprintf(stderr, "Failed to get Public IPs from node %u\n",
3736 pnn);
3737 return ret;
3740 for (i=0; i<pubip_list->num; i++) {
3741 if (ctdb_same_ip(addr, &pubip_list->ip[i].addr)) {
3742 break;
3746 if (i == pubip_list->num) {
3747 fprintf(stderr, "Node %u CANNOT host IP address %s\n",
3748 pnn, ctdb_sock_addr_to_string(mem_ctx, addr, false));
3749 return 1;
3752 nodemap = get_nodemap(ctdb, false);
3753 if (nodemap == NULL) {
3754 return 1;
3757 count = list_of_active_nodes(nodemap, pnn, mem_ctx, &pnn_list);
3758 if (count <= 0) {
3759 fprintf(stderr, "Memory allocation error\n");
3760 return 1;
3763 pubip.pnn = pnn;
3764 pubip.addr = *addr;
3765 ctdb_req_control_release_ip(&request, &pubip);
3767 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
3768 pnn_list, count, TIMEOUT(),
3769 &request, NULL, NULL);
3770 if (ret != 0) {
3771 fprintf(stderr, "Failed to release IP on nodes\n");
3772 return ret;
3775 ret = ctdb_ctrl_takeover_ip(mem_ctx, ctdb->ev, ctdb->client,
3776 pnn, TIMEOUT(), &pubip);
3777 if (ret != 0) {
3778 fprintf(stderr, "Failed to takeover IP on node %u\n", pnn);
3779 return ret;
3782 return 0;
3785 static int control_moveip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3786 int argc, const char **argv)
3788 ctdb_sock_addr addr;
3789 uint32_t pnn;
3790 int ret, retries = 0;
3792 if (argc != 2) {
3793 usage("moveip");
3796 ret = ctdb_sock_addr_from_string(argv[0], &addr, false);
3797 if (ret != 0) {
3798 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3799 return 1;
3802 pnn = strtoul(argv[1], NULL, 10);
3803 if (pnn == CTDB_UNKNOWN_PNN) {
3804 fprintf(stderr, "Invalid PNN %s\n", argv[1]);
3805 return 1;
3808 while (retries < 5) {
3809 ret = moveip(mem_ctx, ctdb, &addr, pnn);
3810 if (ret == 0) {
3811 break;
3814 sleep(3);
3815 retries++;
3818 if (ret != 0) {
3819 fprintf(stderr, "Failed to move IP %s to node %u\n",
3820 argv[0], pnn);
3821 return ret;
3824 return 0;
3827 static int rebalancenode(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3828 uint32_t pnn)
3830 int ret;
3832 ret = ctdb_message_rebalance_node(mem_ctx, ctdb->ev, ctdb->client,
3833 CTDB_BROADCAST_CONNECTED, pnn);
3834 if (ret != 0) {
3835 fprintf(stderr,
3836 "Failed to ask recovery master to distribute IPs\n");
3837 return ret;
3840 return 0;
3843 static int control_addip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3844 int argc, const char **argv)
3846 ctdb_sock_addr addr;
3847 struct ctdb_public_ip_list *pubip_list;
3848 struct ctdb_addr_info addr_info;
3849 unsigned int mask;
3850 int ret, i, retries = 0;
3852 if (argc != 2) {
3853 usage("addip");
3856 ret = ctdb_sock_addr_mask_from_string(argv[0], &addr, &mask);
3857 if (ret != 0) {
3858 fprintf(stderr, "Invalid IP/Mask %s\n", argv[0]);
3859 return 1;
3862 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
3863 ctdb->cmd_pnn, TIMEOUT(),
3864 false, &pubip_list);
3865 if (ret != 0) {
3866 fprintf(stderr, "Failed to get Public IPs from node %u\n",
3867 ctdb->cmd_pnn);
3868 return 1;
3871 for (i=0; i<pubip_list->num; i++) {
3872 if (ctdb_same_ip(&addr, &pubip_list->ip[i].addr)) {
3873 fprintf(stderr, "Node already knows about IP %s\n",
3874 ctdb_sock_addr_to_string(mem_ctx,
3875 &addr, false));
3876 return 0;
3880 addr_info.addr = addr;
3881 addr_info.mask = mask;
3882 addr_info.iface = argv[1];
3884 while (retries < 5) {
3885 ret = ctdb_ctrl_add_public_ip(mem_ctx, ctdb->ev, ctdb->client,
3886 ctdb->cmd_pnn, TIMEOUT(),
3887 &addr_info);
3888 if (ret == 0) {
3889 break;
3892 sleep(3);
3893 retries++;
3896 if (ret != 0) {
3897 fprintf(stderr, "Failed to add public IP to node %u."
3898 " Giving up\n", ctdb->cmd_pnn);
3899 return ret;
3902 ret = rebalancenode(mem_ctx, ctdb, ctdb->cmd_pnn);
3903 if (ret != 0) {
3904 return ret;
3907 return 0;
3910 static int control_delip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3911 int argc, const char **argv)
3913 ctdb_sock_addr addr;
3914 struct ctdb_public_ip_list *pubip_list;
3915 struct ctdb_addr_info addr_info;
3916 int ret, i;
3918 if (argc != 1) {
3919 usage("delip");
3922 ret = ctdb_sock_addr_from_string(argv[0], &addr, false);
3923 if (ret != 0) {
3924 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3925 return 1;
3928 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
3929 ctdb->cmd_pnn, TIMEOUT(),
3930 false, &pubip_list);
3931 if (ret != 0) {
3932 fprintf(stderr, "Failed to get Public IPs from node %u\n",
3933 ctdb->cmd_pnn);
3934 return 1;
3937 for (i=0; i<pubip_list->num; i++) {
3938 if (ctdb_same_ip(&addr, &pubip_list->ip[i].addr)) {
3939 break;
3943 if (i == pubip_list->num) {
3944 fprintf(stderr, "Node does not know about IP address %s\n",
3945 ctdb_sock_addr_to_string(mem_ctx, &addr, false));
3946 return 0;
3949 addr_info.addr = addr;
3950 addr_info.mask = 0;
3951 addr_info.iface = NULL;
3953 ret = ctdb_ctrl_del_public_ip(mem_ctx, ctdb->ev, ctdb->client,
3954 ctdb->cmd_pnn, TIMEOUT(), &addr_info);
3955 if (ret != 0) {
3956 fprintf(stderr, "Failed to delete public IP from node %u\n",
3957 ctdb->cmd_pnn);
3958 return ret;
3961 return 0;
3964 #define DB_VERSION 3
3965 #define MAX_DB_NAME 64
3966 #define MAX_REC_BUFFER_SIZE (100*1000)
3968 struct db_header {
3969 unsigned long version;
3970 time_t timestamp;
3971 unsigned long flags;
3972 unsigned long nbuf;
3973 unsigned long nrec;
3974 char name[MAX_DB_NAME];
3977 struct backup_state {
3978 TALLOC_CTX *mem_ctx;
3979 struct ctdb_rec_buffer *recbuf;
3980 uint32_t db_id;
3981 int fd;
3982 unsigned int nbuf, nrec;
3985 static int backup_handler(uint32_t reqid, struct ctdb_ltdb_header *header,
3986 TDB_DATA key, TDB_DATA data, void *private_data)
3988 struct backup_state *state = (struct backup_state *)private_data;
3989 size_t len;
3990 int ret;
3992 if (state->recbuf == NULL) {
3993 state->recbuf = ctdb_rec_buffer_init(state->mem_ctx,
3994 state->db_id);
3995 if (state->recbuf == NULL) {
3996 return ENOMEM;
4000 ret = ctdb_rec_buffer_add(state->recbuf, state->recbuf, reqid,
4001 header, key, data);
4002 if (ret != 0) {
4003 return ret;
4006 len = ctdb_rec_buffer_len(state->recbuf);
4007 if (len < MAX_REC_BUFFER_SIZE) {
4008 return 0;
4011 ret = ctdb_rec_buffer_write(state->recbuf, state->fd);
4012 if (ret != 0) {
4013 fprintf(stderr, "Failed to write records to backup file\n");
4014 return ret;
4017 state->nbuf += 1;
4018 state->nrec += state->recbuf->count;
4019 TALLOC_FREE(state->recbuf);
4021 return 0;
4024 static int control_backupdb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4025 int argc, const char **argv)
4027 const char *db_name;
4028 struct ctdb_db_context *db;
4029 uint32_t db_id;
4030 uint8_t db_flags;
4031 struct backup_state state;
4032 struct db_header db_hdr;
4033 int fd, ret;
4035 if (argc != 2) {
4036 usage("backupdb");
4039 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
4040 return 1;
4043 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4044 db_flags, &db);
4045 if (ret != 0) {
4046 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4047 return ret;
4050 fd = open(argv[1], O_RDWR|O_CREAT, 0600);
4051 if (fd == -1) {
4052 ret = errno;
4053 fprintf(stderr, "Failed to open file %s for writing\n",
4054 argv[1]);
4055 return ret;
4058 /* Write empty header first */
4059 ZERO_STRUCT(db_hdr);
4060 ret = write(fd, &db_hdr, sizeof(struct db_header));
4061 if (ret == -1) {
4062 ret = errno;
4063 close(fd);
4064 fprintf(stderr, "Failed to write header to file %s\n", argv[1]);
4065 return ret;
4068 state.mem_ctx = mem_ctx;
4069 state.recbuf = NULL;
4070 state.fd = fd;
4071 state.nbuf = 0;
4072 state.nrec = 0;
4074 ret = ctdb_db_traverse_local(db, true, false, backup_handler, &state);
4075 if (ret != 0) {
4076 fprintf(stderr, "Failed to collect records from DB %s\n",
4077 db_name);
4078 close(fd);
4079 return ret;
4082 if (state.recbuf != NULL) {
4083 ret = ctdb_rec_buffer_write(state.recbuf, state.fd);
4084 if (ret != 0) {
4085 fprintf(stderr,
4086 "Failed to write records to backup file\n");
4087 close(fd);
4088 return ret;
4091 state.nbuf += 1;
4092 state.nrec += state.recbuf->count;
4093 TALLOC_FREE(state.recbuf);
4096 db_hdr.version = DB_VERSION;
4097 db_hdr.timestamp = time(NULL);
4098 db_hdr.flags = db_flags;
4099 db_hdr.nbuf = state.nbuf;
4100 db_hdr.nrec = state.nrec;
4101 strncpy(db_hdr.name, db_name, MAX_DB_NAME-1);
4103 lseek(fd, 0, SEEK_SET);
4104 ret = write(fd, &db_hdr, sizeof(struct db_header));
4105 if (ret == -1) {
4106 ret = errno;
4107 close(fd);
4108 fprintf(stderr, "Failed to write header to file %s\n", argv[1]);
4109 return ret;
4112 close(fd);
4113 printf("Database backed up to %s\n", argv[1]);
4114 return 0;
4117 static int control_restoredb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4118 int argc, const char **argv)
4120 const char *db_name = NULL;
4121 struct ctdb_db_context *db;
4122 struct db_header db_hdr;
4123 struct ctdb_node_map *nodemap;
4124 struct ctdb_req_control request;
4125 struct ctdb_reply_control **reply;
4126 struct ctdb_transdb wipedb;
4127 struct ctdb_pulldb_ext pulldb;
4128 struct ctdb_rec_buffer *recbuf;
4129 uint32_t generation;
4130 uint32_t *pnn_list;
4131 char timebuf[128];
4132 ssize_t n;
4133 int fd, i;
4134 int count, ret;
4135 uint8_t db_flags;
4137 if (argc < 1 || argc > 2) {
4138 usage("restoredb");
4141 fd = open(argv[0], O_RDONLY, 0600);
4142 if (fd == -1) {
4143 ret = errno;
4144 fprintf(stderr, "Failed to open file %s for reading\n",
4145 argv[0]);
4146 return ret;
4149 if (argc == 2) {
4150 db_name = argv[1];
4153 n = read(fd, &db_hdr, sizeof(struct db_header));
4154 if (n == -1) {
4155 ret = errno;
4156 close(fd);
4157 fprintf(stderr, "Failed to read db header from file %s\n",
4158 argv[0]);
4159 return ret;
4161 db_hdr.name[sizeof(db_hdr.name)-1] = '\0';
4163 if (db_hdr.version != DB_VERSION) {
4164 fprintf(stderr,
4165 "Wrong version of backup file, expected %u, got %lu\n",
4166 DB_VERSION, db_hdr.version);
4167 close(fd);
4168 return EINVAL;
4171 if (db_name == NULL) {
4172 db_name = db_hdr.name;
4175 strftime(timebuf, sizeof(timebuf)-1, "%Y/%m/%d %H:%M:%S",
4176 localtime(&db_hdr.timestamp));
4177 printf("Restoring database %s from backup @ %s\n", db_name, timebuf);
4179 db_flags = db_hdr.flags & 0xff;
4180 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4181 db_flags, &db);
4182 if (ret != 0) {
4183 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4184 close(fd);
4185 return ret;
4188 nodemap = get_nodemap(ctdb, false);
4189 if (nodemap == NULL) {
4190 fprintf(stderr, "Failed to get nodemap\n");
4191 close(fd);
4192 return ENOMEM;
4195 ret = get_generation(mem_ctx, ctdb, &generation);
4196 if (ret != 0) {
4197 fprintf(stderr, "Failed to get current generation\n");
4198 close(fd);
4199 return ret;
4202 count = list_of_active_nodes(nodemap, CTDB_UNKNOWN_PNN, mem_ctx,
4203 &pnn_list);
4204 if (count <= 0) {
4205 close(fd);
4206 return ENOMEM;
4209 wipedb.db_id = ctdb_db_id(db);
4210 wipedb.tid = generation;
4212 ctdb_req_control_db_freeze(&request, wipedb.db_id);
4213 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4214 ctdb->client, pnn_list, count,
4215 TIMEOUT(), &request, NULL, NULL);
4216 if (ret != 0) {
4217 goto failed;
4221 ctdb_req_control_db_transaction_start(&request, &wipedb);
4222 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4223 pnn_list, count, TIMEOUT(),
4224 &request, NULL, NULL);
4225 if (ret != 0) {
4226 goto failed;
4229 ctdb_req_control_wipe_database(&request, &wipedb);
4230 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4231 pnn_list, count, TIMEOUT(),
4232 &request, NULL, NULL);
4233 if (ret != 0) {
4234 goto failed;
4237 pulldb.db_id = ctdb_db_id(db);
4238 pulldb.lmaster = 0;
4239 pulldb.srvid = SRVID_CTDB_PUSHDB;
4241 ctdb_req_control_db_push_start(&request, &pulldb);
4242 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4243 pnn_list, count, TIMEOUT(),
4244 &request, NULL, NULL);
4245 if (ret != 0) {
4246 goto failed;
4249 for (i=0; i<db_hdr.nbuf; i++) {
4250 struct ctdb_req_message message;
4251 TDB_DATA data;
4252 size_t np;
4254 ret = ctdb_rec_buffer_read(fd, mem_ctx, &recbuf);
4255 if (ret != 0) {
4256 goto failed;
4259 data.dsize = ctdb_rec_buffer_len(recbuf);
4260 data.dptr = talloc_size(mem_ctx, data.dsize);
4261 if (data.dptr == NULL) {
4262 goto failed;
4265 ctdb_rec_buffer_push(recbuf, data.dptr, &np);
4267 message.srvid = pulldb.srvid;
4268 message.data.data = data;
4270 ret = ctdb_client_message_multi(mem_ctx, ctdb->ev,
4271 ctdb->client,
4272 pnn_list, count,
4273 &message, NULL);
4274 if (ret != 0) {
4275 goto failed;
4278 talloc_free(recbuf);
4279 talloc_free(data.dptr);
4282 ctdb_req_control_db_push_confirm(&request, pulldb.db_id);
4283 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4284 pnn_list, count, TIMEOUT(),
4285 &request, NULL, &reply);
4286 if (ret != 0) {
4287 goto failed;
4290 for (i=0; i<count; i++) {
4291 uint32_t num_records;
4293 ret = ctdb_reply_control_db_push_confirm(reply[i],
4294 &num_records);
4295 if (ret != 0) {
4296 fprintf(stderr, "Invalid response from node %u\n",
4297 pnn_list[i]);
4298 goto failed;
4301 if (num_records != db_hdr.nrec) {
4302 fprintf(stderr, "Node %u received %u of %lu records\n",
4303 pnn_list[i], num_records, db_hdr.nrec);
4304 goto failed;
4308 ctdb_req_control_db_set_healthy(&request, wipedb.db_id);
4309 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4310 pnn_list, count, TIMEOUT(),
4311 &request, NULL, NULL);
4312 if (ret != 0) {
4313 goto failed;
4316 ctdb_req_control_db_transaction_commit(&request, &wipedb);
4317 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4318 pnn_list, count, TIMEOUT(),
4319 &request, NULL, NULL);
4320 if (ret != 0) {
4321 goto failed;
4324 ctdb_req_control_db_thaw(&request, wipedb.db_id);
4325 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4326 ctdb->client, pnn_list, count,
4327 TIMEOUT(), &request, NULL, NULL);
4328 if (ret != 0) {
4329 goto failed;
4332 printf("Database %s restored\n", db_name);
4333 close(fd);
4334 return 0;
4337 failed:
4338 close(fd);
4339 ctdb_ctrl_set_recmode(mem_ctx, ctdb->ev, ctdb->client,
4340 ctdb->pnn, TIMEOUT(), CTDB_RECOVERY_ACTIVE);
4341 return ret;
4344 struct dumpdbbackup_state {
4345 ctdb_rec_parser_func_t parser;
4346 struct dump_record_state sub_state;
4349 static int dumpdbbackup_handler(uint32_t reqid,
4350 struct ctdb_ltdb_header *header,
4351 TDB_DATA key, TDB_DATA data,
4352 void *private_data)
4354 struct dumpdbbackup_state *state =
4355 (struct dumpdbbackup_state *)private_data;
4356 struct ctdb_ltdb_header hdr;
4357 int ret;
4359 ret = ctdb_ltdb_header_extract(&data, &hdr);
4360 if (ret != 0) {
4361 return ret;
4364 return state->parser(reqid, &hdr, key, data, &state->sub_state);
4367 static int control_dumpdbbackup(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4368 int argc, const char **argv)
4370 struct db_header db_hdr;
4371 char timebuf[128];
4372 struct dumpdbbackup_state state;
4373 ssize_t n;
4374 int fd, ret, i;
4376 if (argc != 1) {
4377 usage("dumpbackup");
4380 fd = open(argv[0], O_RDONLY, 0600);
4381 if (fd == -1) {
4382 ret = errno;
4383 fprintf(stderr, "Failed to open file %s for reading\n",
4384 argv[0]);
4385 return ret;
4388 n = read(fd, &db_hdr, sizeof(struct db_header));
4389 if (n == -1) {
4390 ret = errno;
4391 close(fd);
4392 fprintf(stderr, "Failed to read db header from file %s\n",
4393 argv[0]);
4394 return ret;
4396 db_hdr.name[sizeof(db_hdr.name)-1] = '\0';
4398 if (db_hdr.version != DB_VERSION) {
4399 fprintf(stderr,
4400 "Wrong version of backup file, expected %u, got %lu\n",
4401 DB_VERSION, db_hdr.version);
4402 close(fd);
4403 return EINVAL;
4406 strftime(timebuf, sizeof(timebuf)-1, "%Y/%m/%d %H:%M:%S",
4407 localtime(&db_hdr.timestamp));
4408 printf("Dumping database %s from backup @ %s\n",
4409 db_hdr.name, timebuf);
4411 state.parser = dump_record;
4412 state.sub_state.count = 0;
4414 for (i=0; i<db_hdr.nbuf; i++) {
4415 struct ctdb_rec_buffer *recbuf;
4417 ret = ctdb_rec_buffer_read(fd, mem_ctx, &recbuf);
4418 if (ret != 0) {
4419 fprintf(stderr, "Failed to read records\n");
4420 close(fd);
4421 return ret;
4424 ret = ctdb_rec_buffer_traverse(recbuf, dumpdbbackup_handler,
4425 &state);
4426 if (ret != 0) {
4427 fprintf(stderr, "Failed to dump records\n");
4428 close(fd);
4429 return ret;
4433 close(fd);
4434 printf("Dumped %u record(s)\n", state.sub_state.count);
4435 return 0;
4438 static int control_wipedb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4439 int argc, const char **argv)
4441 const char *db_name;
4442 struct ctdb_db_context *db;
4443 uint32_t db_id;
4444 uint8_t db_flags;
4445 struct ctdb_node_map *nodemap;
4446 struct ctdb_req_control request;
4447 struct ctdb_transdb wipedb;
4448 uint32_t generation;
4449 uint32_t *pnn_list;
4450 int count, ret;
4452 if (argc != 1) {
4453 usage("wipedb");
4456 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
4457 return 1;
4460 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4461 db_flags, &db);
4462 if (ret != 0) {
4463 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4464 return ret;
4467 nodemap = get_nodemap(ctdb, false);
4468 if (nodemap == NULL) {
4469 fprintf(stderr, "Failed to get nodemap\n");
4470 return ENOMEM;
4473 ret = get_generation(mem_ctx, ctdb, &generation);
4474 if (ret != 0) {
4475 fprintf(stderr, "Failed to get current generation\n");
4476 return ret;
4479 count = list_of_active_nodes(nodemap, CTDB_UNKNOWN_PNN, mem_ctx,
4480 &pnn_list);
4481 if (count <= 0) {
4482 return ENOMEM;
4485 ctdb_req_control_db_freeze(&request, db_id);
4486 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4487 ctdb->client, pnn_list, count,
4488 TIMEOUT(), &request, NULL, NULL);
4489 if (ret != 0) {
4490 goto failed;
4493 wipedb.db_id = db_id;
4494 wipedb.tid = generation;
4496 ctdb_req_control_db_transaction_start(&request, &wipedb);
4497 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4498 pnn_list, count, TIMEOUT(),
4499 &request, NULL, NULL);
4500 if (ret != 0) {
4501 goto failed;
4504 ctdb_req_control_wipe_database(&request, &wipedb);
4505 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4506 pnn_list, count, TIMEOUT(),
4507 &request, NULL, NULL);
4508 if (ret != 0) {
4509 goto failed;
4512 ctdb_req_control_db_set_healthy(&request, db_id);
4513 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4514 pnn_list, count, TIMEOUT(),
4515 &request, NULL, NULL);
4516 if (ret != 0) {
4517 goto failed;
4520 ctdb_req_control_db_transaction_commit(&request, &wipedb);
4521 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4522 pnn_list, count, TIMEOUT(),
4523 &request, NULL, NULL);
4524 if (ret != 0) {
4525 goto failed;
4528 ctdb_req_control_db_thaw(&request, db_id);
4529 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4530 ctdb->client, pnn_list, count,
4531 TIMEOUT(), &request, NULL, NULL);
4532 if (ret != 0) {
4533 goto failed;
4536 printf("Database %s wiped\n", db_name);
4537 return 0;
4540 failed:
4541 ctdb_ctrl_set_recmode(mem_ctx, ctdb->ev, ctdb->client,
4542 ctdb->pnn, TIMEOUT(), CTDB_RECOVERY_ACTIVE);
4543 return ret;
4546 static int control_recmaster(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4547 int argc, const char **argv)
4549 uint32_t recmaster;
4550 int ret;
4552 ret = ctdb_ctrl_get_recmaster(mem_ctx, ctdb->ev, ctdb->client,
4553 ctdb->cmd_pnn, TIMEOUT(), &recmaster);
4554 if (ret != 0) {
4555 return ret;
4558 printf("%u\n", recmaster);
4559 return 0;
4562 static int control_event(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4563 int argc, const char **argv)
4565 char *t, *event_helper = NULL;
4567 t = getenv("CTDB_EVENT_HELPER");
4568 if (t != NULL) {
4569 event_helper = talloc_strdup(mem_ctx, t);
4570 } else {
4571 event_helper = talloc_asprintf(mem_ctx, "%s/ctdb-event",
4572 CTDB_HELPER_BINDIR);
4575 if (event_helper == NULL) {
4576 fprintf(stderr, "Unable to set event daemon helper\n");
4577 return 1;
4580 return run_helper(mem_ctx, "event daemon helper", event_helper,
4581 argc, argv);
4584 static int control_scriptstatus(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4585 int argc, const char **argv)
4587 const char *new_argv[4];
4589 if (argc > 1) {
4590 usage("scriptstatus");
4593 new_argv[0] = "status";
4594 new_argv[1] = "legacy";
4595 new_argv[2] = (argc == 0) ? "monitor" : argv[0];
4596 new_argv[3] = NULL;
4598 (void) control_event(mem_ctx, ctdb, 3, new_argv);
4599 return 0;
4602 static int control_natgw(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4603 int argc, const char **argv)
4605 char *t, *natgw_helper = NULL;
4607 if (argc != 1) {
4608 usage("natgw");
4611 t = getenv("CTDB_NATGW_HELPER");
4612 if (t != NULL) {
4613 natgw_helper = talloc_strdup(mem_ctx, t);
4614 } else {
4615 natgw_helper = talloc_asprintf(mem_ctx, "%s/ctdb_natgw",
4616 CTDB_HELPER_BINDIR);
4619 if (natgw_helper == NULL) {
4620 fprintf(stderr, "Unable to set NAT gateway helper\n");
4621 return 1;
4624 return run_helper(mem_ctx, "NAT gateway helper", natgw_helper,
4625 argc, argv);
4629 * Find the PNN of the current node
4630 * discover the pnn by loading the nodes file and try to bind
4631 * to all addresses one at a time until the ip address is found.
4633 static bool find_node_xpnn(TALLOC_CTX *mem_ctx, uint32_t *pnn)
4635 struct ctdb_node_map *nodemap;
4636 int i;
4638 nodemap = read_nodes_file(mem_ctx, CTDB_UNKNOWN_PNN);
4639 if (nodemap == NULL) {
4640 return false;
4643 for (i=0; i<nodemap->num; i++) {
4644 if (nodemap->node[i].flags & NODE_FLAGS_DELETED) {
4645 continue;
4647 if (ctdb_sys_have_ip(&nodemap->node[i].addr)) {
4648 if (pnn != NULL) {
4649 *pnn = nodemap->node[i].pnn;
4651 talloc_free(nodemap);
4652 return true;
4656 fprintf(stderr, "Failed to detect PNN of the current node.\n");
4657 talloc_free(nodemap);
4658 return false;
4661 static int control_getreclock(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4662 int argc, const char **argv)
4664 const char *reclock;
4665 int ret;
4667 if (argc != 0) {
4668 usage("getreclock");
4671 ret = ctdb_ctrl_get_reclock_file(mem_ctx, ctdb->ev, ctdb->client,
4672 ctdb->cmd_pnn, TIMEOUT(), &reclock);
4673 if (ret != 0) {
4674 return ret;
4677 if (reclock != NULL) {
4678 printf("%s\n", reclock);
4681 return 0;
4684 static int control_setlmasterrole(TALLOC_CTX *mem_ctx,
4685 struct ctdb_context *ctdb,
4686 int argc, const char **argv)
4688 uint32_t lmasterrole = 0;
4689 int ret;
4691 if (argc != 1) {
4692 usage("setlmasterrole");
4695 if (strcmp(argv[0], "on") == 0) {
4696 lmasterrole = 1;
4697 } else if (strcmp(argv[0], "off") == 0) {
4698 lmasterrole = 0;
4699 } else {
4700 usage("setlmasterrole");
4703 ret = ctdb_ctrl_set_lmasterrole(mem_ctx, ctdb->ev, ctdb->client,
4704 ctdb->cmd_pnn, TIMEOUT(), lmasterrole);
4705 if (ret != 0) {
4706 return ret;
4709 return 0;
4712 static int control_setrecmasterrole(TALLOC_CTX *mem_ctx,
4713 struct ctdb_context *ctdb,
4714 int argc, const char **argv)
4716 uint32_t recmasterrole = 0;
4717 int ret;
4719 if (argc != 1) {
4720 usage("setrecmasterrole");
4723 if (strcmp(argv[0], "on") == 0) {
4724 recmasterrole = 1;
4725 } else if (strcmp(argv[0], "off") == 0) {
4726 recmasterrole = 0;
4727 } else {
4728 usage("setrecmasterrole");
4731 ret = ctdb_ctrl_set_recmasterrole(mem_ctx, ctdb->ev, ctdb->client,
4732 ctdb->cmd_pnn, TIMEOUT(),
4733 recmasterrole);
4734 if (ret != 0) {
4735 return ret;
4738 return 0;
4741 static int control_setdbreadonly(TALLOC_CTX *mem_ctx,
4742 struct ctdb_context *ctdb,
4743 int argc, const char **argv)
4745 uint32_t db_id;
4746 uint8_t db_flags;
4747 int ret;
4749 if (argc != 1) {
4750 usage("setdbreadonly");
4753 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, NULL, &db_flags)) {
4754 return 1;
4757 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
4758 fprintf(stderr, "READONLY can be set only on volatile DB\n");
4759 return 1;
4762 ret = ctdb_ctrl_set_db_readonly(mem_ctx, ctdb->ev, ctdb->client,
4763 ctdb->cmd_pnn, TIMEOUT(), db_id);
4764 if (ret != 0) {
4765 return ret;
4768 return 0;
4771 static int control_setdbsticky(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4772 int argc, const char **argv)
4774 uint32_t db_id;
4775 uint8_t db_flags;
4776 int ret;
4778 if (argc != 1) {
4779 usage("setdbsticky");
4782 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, NULL, &db_flags)) {
4783 return 1;
4786 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
4787 fprintf(stderr, "STICKY can be set only on volatile DB\n");
4788 return 1;
4791 ret = ctdb_ctrl_set_db_sticky(mem_ctx, ctdb->ev, ctdb->client,
4792 ctdb->cmd_pnn, TIMEOUT(), db_id);
4793 if (ret != 0) {
4794 return ret;
4797 return 0;
4800 static int control_pfetch(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4801 int argc, const char **argv)
4803 const char *db_name;
4804 struct ctdb_db_context *db;
4805 struct ctdb_transaction_handle *h;
4806 uint8_t db_flags;
4807 TDB_DATA key, data;
4808 int ret;
4810 if (argc != 2) {
4811 usage("pfetch");
4814 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
4815 return 1;
4818 if (! (db_flags &
4819 (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED))) {
4820 fprintf(stderr, "Transactions not supported on DB %s\n",
4821 db_name);
4822 return 1;
4825 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4826 db_flags, &db);
4827 if (ret != 0) {
4828 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4829 return ret;
4832 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
4833 if (ret != 0) {
4834 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
4835 return ret;
4838 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
4839 TIMEOUT(), db, true, &h);
4840 if (ret != 0) {
4841 fprintf(stderr, "Failed to start transaction on db %s\n",
4842 db_name);
4843 return ret;
4846 ret = ctdb_transaction_fetch_record(h, key, mem_ctx, &data);
4847 if (ret != 0) {
4848 fprintf(stderr, "Failed to read record for key %s\n",
4849 argv[1]);
4850 ctdb_transaction_cancel(h);
4851 return ret;
4854 printf("%.*s\n", (int)data.dsize, data.dptr);
4856 ctdb_transaction_cancel(h);
4857 return 0;
4860 static int control_pstore(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4861 int argc, const char **argv)
4863 const char *db_name;
4864 struct ctdb_db_context *db;
4865 struct ctdb_transaction_handle *h;
4866 uint8_t db_flags;
4867 TDB_DATA key, data;
4868 int ret;
4870 if (argc != 3) {
4871 usage("pstore");
4874 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
4875 return 1;
4878 if (! (db_flags &
4879 (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED))) {
4880 fprintf(stderr, "Transactions not supported on DB %s\n",
4881 db_name);
4882 return 1;
4885 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4886 db_flags, &db);
4887 if (ret != 0) {
4888 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4889 return ret;
4892 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
4893 if (ret != 0) {
4894 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
4895 return ret;
4898 ret = str_to_data(argv[2], strlen(argv[2]), mem_ctx, &data);
4899 if (ret != 0) {
4900 fprintf(stderr, "Failed to parse value %s\n", argv[2]);
4901 return ret;
4904 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
4905 TIMEOUT(), db, false, &h);
4906 if (ret != 0) {
4907 fprintf(stderr, "Failed to start transaction on db %s\n",
4908 db_name);
4909 return ret;
4912 ret = ctdb_transaction_store_record(h, key, data);
4913 if (ret != 0) {
4914 fprintf(stderr, "Failed to store record for key %s\n",
4915 argv[1]);
4916 ctdb_transaction_cancel(h);
4917 return ret;
4920 ret = ctdb_transaction_commit(h);
4921 if (ret != 0) {
4922 fprintf(stderr, "Failed to commit transaction on db %s\n",
4923 db_name);
4924 ctdb_transaction_cancel(h);
4925 return ret;
4928 return 0;
4931 static int control_pdelete(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4932 int argc, const char **argv)
4934 const char *db_name;
4935 struct ctdb_db_context *db;
4936 struct ctdb_transaction_handle *h;
4937 uint8_t db_flags;
4938 TDB_DATA key;
4939 int ret;
4941 if (argc != 2) {
4942 usage("pdelete");
4945 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
4946 return 1;
4949 if (! (db_flags &
4950 (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED))) {
4951 fprintf(stderr, "Transactions not supported on DB %s\n",
4952 db_name);
4953 return 1;
4956 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4957 db_flags, &db);
4958 if (ret != 0) {
4959 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4960 return ret;
4963 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
4964 if (ret != 0) {
4965 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
4966 return ret;
4969 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
4970 TIMEOUT(), db, false, &h);
4971 if (ret != 0) {
4972 fprintf(stderr, "Failed to start transaction on db %s\n",
4973 db_name);
4974 return ret;
4977 ret = ctdb_transaction_delete_record(h, key);
4978 if (ret != 0) {
4979 fprintf(stderr, "Failed to delete record for key %s\n",
4980 argv[1]);
4981 ctdb_transaction_cancel(h);
4982 return ret;
4985 ret = ctdb_transaction_commit(h);
4986 if (ret != 0) {
4987 fprintf(stderr, "Failed to commit transaction on db %s\n",
4988 db_name);
4989 ctdb_transaction_cancel(h);
4990 return ret;
4993 return 0;
4996 static int ptrans_parse_string(TALLOC_CTX *mem_ctx, const char **ptr, TDB_DATA *data)
4998 const char *t;
4999 size_t n;
5000 int ret;
5002 *data = tdb_null;
5004 /* Skip whitespace */
5005 n = strspn(*ptr, " \t");
5006 t = *ptr + n;
5008 if (t[0] == '"') {
5009 /* Quoted ASCII string - no wide characters! */
5010 t++;
5011 n = strcspn(t, "\"");
5012 if (t[n] == '"') {
5013 if (n > 0) {
5014 ret = str_to_data(t, n, mem_ctx, data);
5015 if (ret != 0) {
5016 return ret;
5019 *ptr = t + n + 1;
5020 } else {
5021 fprintf(stderr, "Unmatched \" in input %s\n", *ptr);
5022 return 1;
5024 } else {
5025 fprintf(stderr, "Unsupported input format in %s\n", *ptr);
5026 return 1;
5029 return 0;
5032 #define MAX_LINE_SIZE 1024
5034 static bool ptrans_get_key_value(TALLOC_CTX *mem_ctx, FILE *file,
5035 TDB_DATA *key, TDB_DATA *value)
5037 char line [MAX_LINE_SIZE]; /* FIXME: make this more flexible? */
5038 const char *ptr;
5039 int ret;
5041 ptr = fgets(line, MAX_LINE_SIZE, file);
5042 if (ptr == NULL) {
5043 return false;
5046 /* Get key */
5047 ret = ptrans_parse_string(mem_ctx, &ptr, key);
5048 if (ret != 0 || ptr == NULL || key->dptr == NULL) {
5049 /* Line Ignored but not EOF */
5050 *key = tdb_null;
5051 return true;
5054 /* Get value */
5055 ret = ptrans_parse_string(mem_ctx, &ptr, value);
5056 if (ret != 0) {
5057 /* Line Ignored but not EOF */
5058 talloc_free(key->dptr);
5059 *key = tdb_null;
5060 return true;
5063 return true;
5066 static int control_ptrans(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5067 int argc, const char **argv)
5069 const char *db_name;
5070 struct ctdb_db_context *db;
5071 struct ctdb_transaction_handle *h;
5072 uint8_t db_flags;
5073 FILE *file;
5074 TDB_DATA key, value;
5075 int ret;
5077 if (argc < 1 || argc > 2) {
5078 usage("ptrans");
5081 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5082 return 1;
5085 if (! (db_flags &
5086 (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED))) {
5087 fprintf(stderr, "Transactions not supported on DB %s\n",
5088 db_name);
5089 return 1;
5092 if (argc == 2) {
5093 file = fopen(argv[1], "r");
5094 if (file == NULL) {
5095 fprintf(stderr, "Failed to open file %s\n", argv[1]);
5096 return 1;
5098 } else {
5099 file = stdin;
5102 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5103 db_flags, &db);
5104 if (ret != 0) {
5105 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5106 goto done;
5109 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
5110 TIMEOUT(), db, false, &h);
5111 if (ret != 0) {
5112 fprintf(stderr, "Failed to start transaction on db %s\n",
5113 db_name);
5114 goto done;
5117 while (ptrans_get_key_value(mem_ctx, file, &key, &value)) {
5118 if (key.dsize != 0) {
5119 ret = ctdb_transaction_store_record(h, key, value);
5120 if (ret != 0) {
5121 fprintf(stderr, "Failed to store record\n");
5122 ctdb_transaction_cancel(h);
5123 goto done;
5125 talloc_free(key.dptr);
5126 talloc_free(value.dptr);
5130 ret = ctdb_transaction_commit(h);
5131 if (ret != 0) {
5132 fprintf(stderr, "Failed to commit transaction on db %s\n",
5133 db_name);
5134 ctdb_transaction_cancel(h);
5137 done:
5138 if (file != stdin) {
5139 fclose(file);
5141 return ret;
5144 static int control_tfetch(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5145 int argc, const char **argv)
5147 struct tdb_context *tdb;
5148 TDB_DATA key, data;
5149 struct ctdb_ltdb_header header;
5150 int ret;
5152 if (argc < 2 || argc > 3) {
5153 usage("tfetch");
5156 tdb = tdb_open(argv[0], 0, 0, O_RDWR, 0);
5157 if (tdb == NULL) {
5158 fprintf(stderr, "Failed to open TDB file %s\n", argv[0]);
5159 return 1;
5162 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5163 if (ret != 0) {
5164 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5165 tdb_close(tdb);
5166 return ret;
5169 data = tdb_fetch(tdb, key);
5170 if (data.dptr == NULL) {
5171 fprintf(stderr, "No record for key %s\n", argv[1]);
5172 tdb_close(tdb);
5173 return 1;
5176 if (data.dsize < sizeof(struct ctdb_ltdb_header)) {
5177 fprintf(stderr, "Invalid record for key %s\n", argv[1]);
5178 tdb_close(tdb);
5179 return 1;
5182 tdb_close(tdb);
5184 if (argc == 3) {
5185 int fd;
5186 ssize_t nwritten;
5188 fd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0600);
5189 if (fd == -1) {
5190 fprintf(stderr, "Failed to open output file %s\n",
5191 argv[2]);
5192 goto fail;
5195 nwritten = sys_write(fd, data.dptr, data.dsize);
5196 if (nwritten != data.dsize) {
5197 fprintf(stderr, "Failed to write record to file\n");
5198 close(fd);
5199 goto fail;
5202 close(fd);
5205 fail:
5206 ret = ctdb_ltdb_header_extract(&data, &header);
5207 if (ret != 0) {
5208 fprintf(stderr, "Failed to parse header from data\n");
5209 return 1;
5212 dump_ltdb_header(&header);
5213 dump_tdb_data("data", data);
5215 return 0;
5218 static int control_tstore(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5219 int argc, const char **argv)
5221 struct tdb_context *tdb;
5222 TDB_DATA key, data[2], value;
5223 struct ctdb_ltdb_header header;
5224 uint8_t header_buf[sizeof(struct ctdb_ltdb_header)];
5225 size_t np;
5226 int ret;
5228 if (argc < 3 || argc > 5) {
5229 usage("tstore");
5232 tdb = tdb_open(argv[0], 0, 0, O_RDWR, 0);
5233 if (tdb == NULL) {
5234 fprintf(stderr, "Failed to open TDB file %s\n", argv[0]);
5235 return 1;
5238 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5239 if (ret != 0) {
5240 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5241 tdb_close(tdb);
5242 return ret;
5245 ret = str_to_data(argv[2], strlen(argv[2]), mem_ctx, &value);
5246 if (ret != 0) {
5247 fprintf(stderr, "Failed to parse value %s\n", argv[2]);
5248 tdb_close(tdb);
5249 return ret;
5252 ZERO_STRUCT(header);
5254 if (argc > 3) {
5255 header.rsn = (uint64_t)strtoull(argv[3], NULL, 0);
5257 if (argc > 4) {
5258 header.dmaster = (uint32_t)atol(argv[4]);
5260 if (argc > 5) {
5261 header.flags = (uint32_t)atol(argv[5]);
5264 ctdb_ltdb_header_push(&header, header_buf, &np);
5266 data[0].dsize = np;
5267 data[0].dptr = header_buf;
5269 data[1].dsize = value.dsize;
5270 data[1].dptr = value.dptr;
5272 ret = tdb_storev(tdb, key, data, 2, TDB_REPLACE);
5273 if (ret != 0) {
5274 fprintf(stderr, "Failed to write record %s to file %s\n",
5275 argv[1], argv[0]);
5278 tdb_close(tdb);
5280 return ret;
5283 static int control_readkey(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5284 int argc, const char **argv)
5286 const char *db_name;
5287 struct ctdb_db_context *db;
5288 struct ctdb_record_handle *h;
5289 uint8_t db_flags;
5290 TDB_DATA key, data;
5291 bool readonly = false;
5292 int ret;
5294 if (argc < 2 || argc > 3) {
5295 usage("readkey");
5298 if (argc == 3) {
5299 if (strcmp(argv[2], "readonly") == 0) {
5300 readonly = true;
5301 } else {
5302 usage("readkey");
5306 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5307 return 1;
5310 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
5311 fprintf(stderr, "DB %s is not a volatile database\n",
5312 db_name);
5313 return 1;
5316 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5317 db_flags, &db);
5318 if (ret != 0) {
5319 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5320 return ret;
5323 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5324 if (ret != 0) {
5325 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5326 return ret;
5329 ret = ctdb_fetch_lock(mem_ctx, ctdb->ev, ctdb->client,
5330 db, key, readonly, &h, NULL, &data);
5331 if (ret != 0) {
5332 fprintf(stderr, "Failed to read record for key %s\n",
5333 argv[1]);
5334 } else {
5335 printf("Data: size:%zu ptr:[%.*s]\n", data.dsize,
5336 (int)data.dsize, data.dptr);
5339 talloc_free(h);
5340 return ret;
5343 static int control_writekey(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5344 int argc, const char **argv)
5346 const char *db_name;
5347 struct ctdb_db_context *db;
5348 struct ctdb_record_handle *h;
5349 uint8_t db_flags;
5350 TDB_DATA key, data;
5351 int ret;
5353 if (argc != 3) {
5354 usage("writekey");
5357 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5358 return 1;
5361 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
5362 fprintf(stderr, "DB %s is not a volatile database\n",
5363 db_name);
5364 return 1;
5367 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5368 db_flags, &db);
5369 if (ret != 0) {
5370 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5371 return ret;
5374 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5375 if (ret != 0) {
5376 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5377 return ret;
5380 ret = str_to_data(argv[2], strlen(argv[2]), mem_ctx, &data);
5381 if (ret != 0) {
5382 fprintf(stderr, "Failed to parse value %s\n", argv[2]);
5383 return ret;
5386 ret = ctdb_fetch_lock(mem_ctx, ctdb->ev, ctdb->client,
5387 db, key, false, &h, NULL, NULL);
5388 if (ret != 0) {
5389 fprintf(stderr, "Failed to lock record for key %s\n", argv[0]);
5390 return ret;
5393 ret = ctdb_store_record(h, data);
5394 if (ret != 0) {
5395 fprintf(stderr, "Failed to store record for key %s\n",
5396 argv[1]);
5399 talloc_free(h);
5400 return ret;
5403 static int control_deletekey(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5404 int argc, const char **argv)
5406 const char *db_name;
5407 struct ctdb_db_context *db;
5408 struct ctdb_record_handle *h;
5409 uint8_t db_flags;
5410 TDB_DATA key, data;
5411 int ret;
5413 if (argc != 2) {
5414 usage("deletekey");
5417 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5418 return 1;
5421 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
5422 fprintf(stderr, "DB %s is not a volatile database\n",
5423 db_name);
5424 return 1;
5427 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5428 db_flags, &db);
5429 if (ret != 0) {
5430 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5431 return ret;
5434 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5435 if (ret != 0) {
5436 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5437 return ret;
5440 ret = ctdb_fetch_lock(mem_ctx, ctdb->ev, ctdb->client,
5441 db, key, false, &h, NULL, &data);
5442 if (ret != 0) {
5443 fprintf(stderr, "Failed to fetch record for key %s\n",
5444 argv[1]);
5445 return ret;
5448 ret = ctdb_delete_record(h);
5449 if (ret != 0) {
5450 fprintf(stderr, "Failed to delete record for key %s\n",
5451 argv[1]);
5454 talloc_free(h);
5455 return ret;
5458 static int control_checktcpport(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5459 int argc, const char **argv)
5461 struct sockaddr_in sin;
5462 unsigned int port;
5463 int s, v;
5464 int ret;
5466 if (argc != 1) {
5467 usage("chktcpport");
5470 port = atoi(argv[0]);
5472 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
5473 if (s == -1) {
5474 fprintf(stderr, "Failed to open local socket\n");
5475 return errno;
5478 v = fcntl(s, F_GETFL, 0);
5479 if (v == -1 || fcntl(s, F_SETFL, v | O_NONBLOCK)) {
5480 fprintf(stderr, "Unable to set socket non-blocking\n");
5481 close(s);
5482 return errno;
5485 bzero(&sin, sizeof(sin));
5486 sin.sin_family = AF_INET;
5487 sin.sin_port = htons(port);
5488 ret = bind(s, (struct sockaddr *)&sin, sizeof(sin));
5489 close(s);
5490 if (ret == -1) {
5491 fprintf(stderr, "Failed to bind to TCP port %u\n", port);
5492 return errno;
5495 return 0;
5498 static int control_getdbseqnum(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5499 int argc, const char **argv)
5501 uint32_t db_id;
5502 const char *db_name;
5503 uint64_t seqnum;
5504 int ret;
5506 if (argc != 1) {
5507 usage("getdbseqnum");
5510 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, NULL)) {
5511 return 1;
5514 ret = ctdb_ctrl_get_db_seqnum(mem_ctx, ctdb->ev, ctdb->client,
5515 ctdb->cmd_pnn, TIMEOUT(), db_id,
5516 &seqnum);
5517 if (ret != 0) {
5518 fprintf(stderr, "Failed to get sequence number for DB %s\n",
5519 db_name);
5520 return ret;
5523 printf("0x%"PRIx64"\n", seqnum);
5524 return 0;
5527 static int control_nodestatus(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5528 int argc, const char **argv)
5530 const char *nodestring = NULL;
5531 struct ctdb_node_map *nodemap;
5532 int ret, i;
5533 bool print_hdr = false;
5535 if (argc > 1) {
5536 usage("nodestatus");
5539 if (argc == 1) {
5540 nodestring = argv[0];
5541 if (strcmp(nodestring, "all") == 0) {
5542 print_hdr = true;
5546 if (! parse_nodestring(mem_ctx, ctdb, nodestring, &nodemap)) {
5547 return 1;
5550 if (options.machinereadable) {
5551 print_nodemap_machine(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn);
5552 } else {
5553 print_nodemap(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn, print_hdr);
5556 ret = 0;
5557 for (i=0; i<nodemap->num; i++) {
5558 ret |= nodemap->node[i].flags;
5561 return ret;
5564 const struct {
5565 const char *name;
5566 uint32_t offset;
5567 } db_stats_fields[] = {
5568 #define DBSTATISTICS_FIELD(n) { #n, offsetof(struct ctdb_db_statistics, n) }
5569 DBSTATISTICS_FIELD(db_ro_delegations),
5570 DBSTATISTICS_FIELD(db_ro_revokes),
5571 DBSTATISTICS_FIELD(locks.num_calls),
5572 DBSTATISTICS_FIELD(locks.num_current),
5573 DBSTATISTICS_FIELD(locks.num_pending),
5574 DBSTATISTICS_FIELD(locks.num_failed),
5577 static void print_dbstatistics(const char *db_name,
5578 struct ctdb_db_statistics *s)
5580 int i;
5581 const char *prefix = NULL;
5582 int preflen = 0;
5584 printf("DB Statistics %s\n", db_name);
5586 for (i=0; i<ARRAY_SIZE(db_stats_fields); i++) {
5587 if (strchr(db_stats_fields[i].name, '.') != NULL) {
5588 preflen = strcspn(db_stats_fields[i].name, ".") + 1;
5589 if (! prefix ||
5590 strncmp(prefix, db_stats_fields[i].name, preflen) != 0) {
5591 prefix = db_stats_fields[i].name;
5592 printf(" %*.*s\n", preflen-1, preflen-1,
5593 db_stats_fields[i].name);
5595 } else {
5596 preflen = 0;
5598 printf(" %*s%-22s%*s%10u\n", preflen ? 4 : 0, "",
5599 db_stats_fields[i].name+preflen, preflen ? 0 : 4, "",
5600 *(uint32_t *)(db_stats_fields[i].offset+(uint8_t *)s));
5603 printf(" hop_count_buckets:");
5604 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
5605 printf(" %d", s->hop_count_bucket[i]);
5607 printf("\n");
5609 printf(" lock_buckets:");
5610 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
5611 printf(" %d", s->locks.buckets[i]);
5613 printf("\n");
5615 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
5616 "locks_latency MIN/AVG/MAX",
5617 s->locks.latency.min, LATENCY_AVG(s->locks.latency),
5618 s->locks.latency.max, s->locks.latency.num);
5620 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
5621 "vacuum_latency MIN/AVG/MAX",
5622 s->vacuum.latency.min, LATENCY_AVG(s->vacuum.latency),
5623 s->vacuum.latency.max, s->vacuum.latency.num);
5625 printf(" Num Hot Keys: %d\n", s->num_hot_keys);
5626 for (i=0; i<s->num_hot_keys; i++) {
5627 int j;
5628 printf(" Count:%d Key:", s->hot_keys[i].count);
5629 for (j=0; j<s->hot_keys[i].key.dsize; j++) {
5630 printf("%02x", s->hot_keys[i].key.dptr[j] & 0xff);
5632 printf("\n");
5636 static int control_dbstatistics(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5637 int argc, const char **argv)
5639 uint32_t db_id;
5640 const char *db_name;
5641 struct ctdb_db_statistics *dbstats;
5642 int ret;
5644 if (argc != 1) {
5645 usage("dbstatistics");
5648 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, NULL)) {
5649 return 1;
5652 ret = ctdb_ctrl_get_db_statistics(mem_ctx, ctdb->ev, ctdb->client,
5653 ctdb->cmd_pnn, TIMEOUT(), db_id,
5654 &dbstats);
5655 if (ret != 0) {
5656 fprintf(stderr, "Failed to get statistics for DB %s\n",
5657 db_name);
5658 return ret;
5661 print_dbstatistics(db_name, dbstats);
5662 return 0;
5665 struct disable_takeover_runs_state {
5666 uint32_t *pnn_list;
5667 int node_count;
5668 bool *reply;
5669 int status;
5670 bool done;
5673 static void disable_takeover_run_handler(uint64_t srvid, TDB_DATA data,
5674 void *private_data)
5676 struct disable_takeover_runs_state *state =
5677 (struct disable_takeover_runs_state *)private_data;
5678 int ret, i;
5680 if (data.dsize != sizeof(int)) {
5681 /* Ignore packet */
5682 return;
5685 /* ret will be a PNN (i.e. >=0) on success, or negative on error */
5686 ret = *(int *)data.dptr;
5687 if (ret < 0) {
5688 state->status = ret;
5689 state->done = true;
5690 return;
5692 for (i=0; i<state->node_count; i++) {
5693 if (state->pnn_list[i] == ret) {
5694 state->reply[i] = true;
5695 break;
5699 state->done = true;
5700 for (i=0; i<state->node_count; i++) {
5701 if (! state->reply[i]) {
5702 state->done = false;
5703 break;
5708 static int disable_takeover_runs(TALLOC_CTX *mem_ctx,
5709 struct ctdb_context *ctdb, uint32_t timeout,
5710 uint32_t *pnn_list, int count)
5712 struct ctdb_disable_message disable = { 0 };
5713 struct disable_takeover_runs_state state;
5714 int ret, i;
5716 disable.pnn = ctdb->pnn;
5717 disable.srvid = next_srvid(ctdb);
5718 disable.timeout = timeout;
5720 state.pnn_list = pnn_list;
5721 state.node_count = count;
5722 state.done = false;
5723 state.status = 0;
5724 state.reply = talloc_zero_array(mem_ctx, bool, count);
5725 if (state.reply == NULL) {
5726 return ENOMEM;
5729 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
5730 disable.srvid,
5731 disable_takeover_run_handler,
5732 &state);
5733 if (ret != 0) {
5734 return ret;
5737 for (i=0; i<count; i++) {
5738 ret = ctdb_message_disable_takeover_runs(mem_ctx, ctdb->ev,
5739 ctdb->client,
5740 pnn_list[i],
5741 &disable);
5742 if (ret != 0) {
5743 goto fail;
5747 ret = ctdb_client_wait_timeout(ctdb->ev, &state.done, TIMEOUT());
5748 if (ret == ETIME) {
5749 fprintf(stderr, "Timed out waiting to disable takeover runs\n");
5750 } else {
5751 ret = (state.status >= 0 ? 0 : 1);
5754 fail:
5755 ctdb_client_remove_message_handler(ctdb->ev, ctdb->client,
5756 disable.srvid, &state);
5757 return ret;
5760 static int control_reloadips(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5761 int argc, const char **argv)
5763 const char *nodestring = NULL;
5764 struct ctdb_node_map *nodemap, *nodemap2;
5765 struct ctdb_req_control request;
5766 uint32_t *pnn_list, *pnn_list2;
5767 int ret, count, count2;
5769 if (argc > 1) {
5770 usage("reloadips");
5773 if (argc == 1) {
5774 nodestring = argv[0];
5777 nodemap = get_nodemap(ctdb, false);
5778 if (nodemap == NULL) {
5779 return 1;
5782 if (! parse_nodestring(mem_ctx, ctdb, nodestring, &nodemap2)) {
5783 return 1;
5786 count = list_of_connected_nodes(nodemap, CTDB_UNKNOWN_PNN,
5787 mem_ctx, &pnn_list);
5788 if (count <= 0) {
5789 fprintf(stderr, "Memory allocation error\n");
5790 return 1;
5793 count2 = list_of_active_nodes(nodemap2, CTDB_UNKNOWN_PNN,
5794 mem_ctx, &pnn_list2);
5795 if (count2 <= 0) {
5796 fprintf(stderr, "Memory allocation error\n");
5797 return 1;
5800 /* Disable takeover runs on all connected nodes. A reply
5801 * indicating success is needed from each node so all nodes
5802 * will need to be active.
5804 * A check could be added to not allow reloading of IPs when
5805 * there are disconnected nodes. However, this should
5806 * probably be left up to the administrator.
5808 ret = disable_takeover_runs(mem_ctx, ctdb, 2*options.timelimit,
5809 pnn_list, count);
5810 if (ret != 0) {
5811 fprintf(stderr, "Failed to disable takeover runs\n");
5812 return ret;
5815 /* Now tell all the desired nodes to reload their public IPs.
5816 * Keep trying this until it succeeds. This assumes all
5817 * failures are transient, which might not be true...
5819 ctdb_req_control_reload_public_ips(&request);
5820 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
5821 pnn_list2, count2, TIMEOUT(),
5822 &request, NULL, NULL);
5823 if (ret != 0) {
5824 fprintf(stderr, "Failed to reload IPs on some nodes.\n");
5827 /* It isn't strictly necessary to wait until takeover runs are
5828 * re-enabled but doing so can't hurt.
5830 ret = disable_takeover_runs(mem_ctx, ctdb, 0, pnn_list, count);
5831 if (ret != 0) {
5832 fprintf(stderr, "Failed to enable takeover runs\n");
5833 return ret;
5836 return ipreallocate(mem_ctx, ctdb);
5840 static const struct ctdb_cmd {
5841 const char *name;
5842 int (*fn)(TALLOC_CTX *, struct ctdb_context *, int, const char **);
5843 bool without_daemon; /* can be run without daemon running ? */
5844 bool remote; /* can be run on remote nodes */
5845 const char *msg;
5846 const char *args;
5847 } ctdb_commands[] = {
5848 { "version", control_version, true, false,
5849 "show version of ctdb", NULL },
5850 { "status", control_status, false, true,
5851 "show node status", NULL },
5852 { "uptime", control_uptime, false, true,
5853 "show node uptime", NULL },
5854 { "ping", control_ping, false, true,
5855 "ping a node", NULL },
5856 { "runstate", control_runstate, false, true,
5857 "get/check runstate of a node",
5858 "[setup|first_recovery|startup|running]" },
5859 { "getvar", control_getvar, false, true,
5860 "get a tunable variable", "<name>" },
5861 { "setvar", control_setvar, false, true,
5862 "set a tunable variable", "<name> <value>" },
5863 { "listvars", control_listvars, false, true,
5864 "list tunable variables", NULL },
5865 { "statistics", control_statistics, false, true,
5866 "show ctdb statistics", NULL },
5867 { "statisticsreset", control_statistics_reset, false, true,
5868 "reset ctdb statistics", NULL },
5869 { "stats", control_stats, false, true,
5870 "show rolling statistics", "[count]" },
5871 { "ip", control_ip, false, true,
5872 "show public ips", "[all]" },
5873 { "ipinfo", control_ipinfo, false, true,
5874 "show public ip details", "<ip>" },
5875 { "ifaces", control_ifaces, false, true,
5876 "show interfaces", NULL },
5877 { "setifacelink", control_setifacelink, false, true,
5878 "set interface link status", "<iface> up|down" },
5879 { "process-exists", control_process_exists, false, true,
5880 "check if a process exists on a node", "<pid> [<srvid>]" },
5881 { "getdbmap", control_getdbmap, false, true,
5882 "show attached databases", NULL },
5883 { "getdbstatus", control_getdbstatus, false, true,
5884 "show database status", "<dbname|dbid>" },
5885 { "catdb", control_catdb, false, false,
5886 "dump cluster-wide ctdb database", "<dbname|dbid>" },
5887 { "cattdb", control_cattdb, false, false,
5888 "dump local ctdb database", "<dbname|dbid>" },
5889 { "getcapabilities", control_getcapabilities, false, true,
5890 "show node capabilities", NULL },
5891 { "pnn", control_pnn, false, false,
5892 "show the pnn of the currnet node", NULL },
5893 { "lvs", control_lvs, false, false,
5894 "show lvs configuration", "master|list|status" },
5895 { "setdebug", control_setdebug, false, true,
5896 "set debug level", "ERROR|WARNING|NOTICE|INFO|DEBUG" },
5897 { "getdebug", control_getdebug, false, true,
5898 "get debug level", NULL },
5899 { "attach", control_attach, false, false,
5900 "attach a database", "<dbname> [persistent|replicated]" },
5901 { "detach", control_detach, false, false,
5902 "detach database(s)", "<dbname|dbid> ..." },
5903 { "dumpmemory", control_dumpmemory, false, true,
5904 "dump ctdbd memory map", NULL },
5905 { "rddumpmemory", control_rddumpmemory, false, true,
5906 "dump recoverd memory map", NULL },
5907 { "getpid", control_getpid, false, true,
5908 "get ctdbd process ID", NULL },
5909 { "disable", control_disable, false, true,
5910 "disable a node", NULL },
5911 { "enable", control_enable, false, true,
5912 "enable a node", NULL },
5913 { "stop", control_stop, false, true,
5914 "stop a node", NULL },
5915 { "continue", control_continue, false, true,
5916 "continue a stopped node", NULL },
5917 { "ban", control_ban, false, true,
5918 "ban a node", "<bantime>"},
5919 { "unban", control_unban, false, true,
5920 "unban a node", NULL },
5921 { "shutdown", control_shutdown, false, true,
5922 "shutdown ctdb daemon", NULL },
5923 { "recover", control_recover, false, true,
5924 "force recovery", NULL },
5925 { "sync", control_ipreallocate, false, true,
5926 "run ip reallocation (deprecated)", NULL },
5927 { "ipreallocate", control_ipreallocate, false, true,
5928 "run ip reallocation", NULL },
5929 { "isnotrecmaster", control_isnotrecmaster, false, false,
5930 "check if local node is the recmaster", NULL },
5931 { "gratarp", control_gratarp, false, true,
5932 "send a gratuitous arp", "<ip> <interface>" },
5933 { "tickle", control_tickle, true, false,
5934 "send a tcp tickle ack", "<srcip:port> <dstip:port>" },
5935 { "gettickles", control_gettickles, false, true,
5936 "get the list of tickles", "<ip> [<port>]" },
5937 { "addtickle", control_addtickle, false, true,
5938 "add a tickle", "<ip>:<port> <ip>:<port>" },
5939 { "deltickle", control_deltickle, false, true,
5940 "delete a tickle", "<ip>:<port> <ip>:<port>" },
5941 { "listnodes", control_listnodes, true, true,
5942 "list nodes in the cluster", NULL },
5943 { "reloadnodes", control_reloadnodes, false, false,
5944 "reload the nodes file all nodes", NULL },
5945 { "moveip", control_moveip, false, false,
5946 "move an ip address to another node", "<ip> <node>" },
5947 { "addip", control_addip, false, true,
5948 "add an ip address to a node", "<ip/mask> <iface>" },
5949 { "delip", control_delip, false, true,
5950 "delete an ip address from a node", "<ip>" },
5951 { "backupdb", control_backupdb, false, false,
5952 "backup a database into a file", "<dbname|dbid> <file>" },
5953 { "restoredb", control_restoredb, false, false,
5954 "restore a database from a file", "<file> [dbname]" },
5955 { "dumpdbbackup", control_dumpdbbackup, true, false,
5956 "dump database from a backup file", "<file>" },
5957 { "wipedb", control_wipedb, false, false,
5958 "wipe the contents of a database.", "<dbname|dbid>"},
5959 { "recmaster", control_recmaster, false, true,
5960 "show the pnn for the recovery master", NULL },
5961 { "event", control_event, true, false,
5962 "event and event script commands", NULL },
5963 { "scriptstatus", control_scriptstatus, true, false,
5964 "show event script status",
5965 "[init|setup|startup|monitor|takeip|releaseip|ipreallocated]" },
5966 { "natgw", control_natgw, false, false,
5967 "show natgw configuration", "master|list|status" },
5968 { "getreclock", control_getreclock, false, true,
5969 "get recovery lock file", NULL },
5970 { "setlmasterrole", control_setlmasterrole, false, true,
5971 "set LMASTER role", "on|off" },
5972 { "setrecmasterrole", control_setrecmasterrole, false, true,
5973 "set RECMASTER role", "on|off"},
5974 { "setdbreadonly", control_setdbreadonly, false, true,
5975 "enable readonly records", "<dbname|dbid>" },
5976 { "setdbsticky", control_setdbsticky, false, true,
5977 "enable sticky records", "<dbname|dbid>"},
5978 { "pfetch", control_pfetch, false, false,
5979 "fetch record from persistent database", "<dbname|dbid> <key>" },
5980 { "pstore", control_pstore, false, false,
5981 "write record to persistent database", "<dbname|dbid> <key> <value>" },
5982 { "pdelete", control_pdelete, false, false,
5983 "delete record from persistent database", "<dbname|dbid> <key>" },
5984 { "ptrans", control_ptrans, false, false,
5985 "update a persistent database (from file or stdin)", "<dbname|dbid> [<file>]" },
5986 { "tfetch", control_tfetch, false, true,
5987 "fetch a record", "<tdb-file> <key> [<file>]" },
5988 { "tstore", control_tstore, false, true,
5989 "store a record", "<tdb-file> <key> <data> [<rsn> <dmaster> <flags>]" },
5990 { "readkey", control_readkey, false, false,
5991 "read value of a database key", "<dbname|dbid> <key> [readonly]" },
5992 { "writekey", control_writekey, false, false,
5993 "write value for a database key", "<dbname|dbid> <key> <value>" },
5994 { "deletekey", control_deletekey, false, false,
5995 "delete a database key", "<dbname|dbid> <key>" },
5996 { "checktcpport", control_checktcpport, true, false,
5997 "check if a service is bound to a specific tcp port or not", "<port>" },
5998 { "getdbseqnum", control_getdbseqnum, false, false,
5999 "get database sequence number", "<dbname|dbid>" },
6000 { "nodestatus", control_nodestatus, false, true,
6001 "show and return node status", "[all|<pnn-list>]" },
6002 { "dbstatistics", control_dbstatistics, false, true,
6003 "show database statistics", "<dbname|dbid>" },
6004 { "reloadips", control_reloadips, false, false,
6005 "reload the public addresses file", "[all|<pnn-list>]" },
6008 static const struct ctdb_cmd *match_command(const char *command)
6010 const struct ctdb_cmd *cmd;
6011 int i;
6013 for (i=0; i<ARRAY_SIZE(ctdb_commands); i++) {
6014 cmd = &ctdb_commands[i];
6015 if (strlen(command) == strlen(cmd->name) &&
6016 strncmp(command, cmd->name, strlen(command)) == 0) {
6017 return cmd;
6021 return NULL;
6026 * Show usage message
6028 static void usage_full(void)
6030 int i;
6032 poptPrintHelp(pc, stdout, 0);
6033 printf("\nCommands:\n");
6034 for (i=0; i<ARRAY_SIZE(ctdb_commands); i++) {
6035 printf(" %-15s %-27s %s\n",
6036 ctdb_commands[i].name,
6037 ctdb_commands[i].args ? ctdb_commands[i].args : "",
6038 ctdb_commands[i].msg);
6042 static void usage(const char *command)
6044 const struct ctdb_cmd *cmd;
6046 if (command == NULL) {
6047 usage_full();
6048 exit(1);
6051 cmd = match_command(command);
6052 if (cmd == NULL) {
6053 usage_full();
6054 } else {
6055 poptPrintUsage(pc, stdout, 0);
6056 printf("\nCommands:\n");
6057 printf(" %-15s %-27s %s\n",
6058 cmd->name, cmd->args ? cmd->args : "", cmd->msg);
6061 exit(1);
6064 struct poptOption cmdline_options[] = {
6065 POPT_AUTOHELP
6066 { "debug", 'd', POPT_ARG_STRING, &options.debuglevelstr, 0,
6067 "debug level"},
6068 { "timelimit", 't', POPT_ARG_INT, &options.timelimit, 0,
6069 "timelimit (in seconds)" },
6070 { "node", 'n', POPT_ARG_INT, &options.pnn, 0,
6071 "node specification - integer" },
6072 { NULL, 'Y', POPT_ARG_NONE, &options.machinereadable, 0,
6073 "enable machine readable output", NULL },
6074 { "separator", 'x', POPT_ARG_STRING, &options.sep, 0,
6075 "specify separator for machine readable output", "CHAR" },
6076 { NULL, 'X', POPT_ARG_NONE, &options.machineparsable, 0,
6077 "enable machine parsable output with separator |", NULL },
6078 { "verbose", 'v', POPT_ARG_NONE, &options.verbose, 0,
6079 "enable verbose output", NULL },
6080 { "maxruntime", 'T', POPT_ARG_INT, &options.maxruntime, 0,
6081 "die if runtime exceeds this limit (in seconds)" },
6082 POPT_TABLEEND
6085 static int process_command(const struct ctdb_cmd *cmd, int argc,
6086 const char **argv)
6088 TALLOC_CTX *tmp_ctx;
6089 struct ctdb_context *ctdb;
6090 const char *ctdb_socket;
6091 int ret;
6092 bool status;
6093 uint64_t srvid_offset;
6095 tmp_ctx = talloc_new(NULL);
6096 if (tmp_ctx == NULL) {
6097 fprintf(stderr, "Memory allocation error\n");
6098 goto fail;
6101 if (cmd->without_daemon) {
6102 if (options.pnn != -1) {
6103 fprintf(stderr,
6104 "Cannot specify node for command %s\n",
6105 cmd->name);
6106 goto fail;
6109 ret = cmd->fn(tmp_ctx, NULL, argc-1, argv+1);
6110 talloc_free(tmp_ctx);
6111 return ret;
6114 ctdb = talloc_zero(tmp_ctx, struct ctdb_context);
6115 if (ctdb == NULL) {
6116 fprintf(stderr, "Memory allocation error\n");
6117 goto fail;
6120 ctdb->ev = tevent_context_init(ctdb);
6121 if (ctdb->ev == NULL) {
6122 fprintf(stderr, "Failed to initialize tevent\n");
6123 goto fail;
6126 ctdb_socket = path_socket(ctdb, "ctdbd");
6127 if (ctdb_socket == NULL) {
6128 fprintf(stderr, "Memory allocation error\n");
6129 goto fail;
6132 ret = ctdb_client_init(ctdb, ctdb->ev, ctdb_socket, &ctdb->client);
6133 if (ret != 0) {
6134 fprintf(stderr, "Failed to connect to CTDB daemon (%s)\n",
6135 ctdb_socket);
6137 if (!find_node_xpnn(ctdb, NULL)) {
6138 fprintf(stderr, "Is this node part of CTDB cluster?\n");
6140 goto fail;
6143 ctdb->pnn = ctdb_client_pnn(ctdb->client);
6144 srvid_offset = getpid() & 0xFFFF;
6145 ctdb->srvid = SRVID_CTDB_TOOL | (srvid_offset << 16);
6147 if (options.pnn != -1) {
6148 status = verify_pnn(ctdb, options.pnn);
6149 if (! status) {
6150 goto fail;
6153 ctdb->cmd_pnn = options.pnn;
6154 } else {
6155 ctdb->cmd_pnn = ctdb->pnn;
6158 if (! cmd->remote && ctdb->pnn != ctdb->cmd_pnn) {
6159 fprintf(stderr, "Node cannot be specified for command %s\n",
6160 cmd->name);
6161 goto fail;
6164 ret = cmd->fn(tmp_ctx, ctdb, argc-1, argv+1);
6165 talloc_free(tmp_ctx);
6166 return ret;
6168 fail:
6169 talloc_free(tmp_ctx);
6170 return 1;
6173 static void signal_handler(int sig)
6175 fprintf(stderr, "Maximum runtime exceeded - exiting\n");
6178 static void alarm_handler(int sig)
6180 /* Kill any child processes */
6181 signal(SIGTERM, signal_handler);
6182 kill(0, SIGTERM);
6184 _exit(1);
6187 int main(int argc, const char *argv[])
6189 int opt;
6190 const char **extra_argv;
6191 int extra_argc;
6192 const struct ctdb_cmd *cmd;
6193 int loglevel;
6194 bool ok;
6195 int ret;
6197 setlinebuf(stdout);
6199 /* Set default options */
6200 options.debuglevelstr = NULL;
6201 options.timelimit = 10;
6202 options.sep = "|";
6203 options.maxruntime = 0;
6204 options.pnn = -1;
6206 pc = poptGetContext(argv[0], argc, argv, cmdline_options,
6207 POPT_CONTEXT_KEEP_FIRST);
6208 while ((opt = poptGetNextOpt(pc)) != -1) {
6209 fprintf(stderr, "Invalid option %s: %s\n",
6210 poptBadOption(pc, 0), poptStrerror(opt));
6211 exit(1);
6214 if (options.maxruntime == 0) {
6215 const char *ctdb_timeout;
6217 ctdb_timeout = getenv("CTDB_TIMEOUT");
6218 if (ctdb_timeout != NULL) {
6219 options.maxruntime = strtoul(ctdb_timeout, NULL, 0);
6220 } else {
6221 options.maxruntime = 120;
6224 if (options.maxruntime <= 120) {
6225 /* default timeout is 120 seconds */
6226 options.maxruntime = 120;
6229 if (options.machineparsable) {
6230 options.machinereadable = 1;
6233 /* setup the remaining options for the commands */
6234 extra_argc = 0;
6235 extra_argv = poptGetArgs(pc);
6236 if (extra_argv) {
6237 extra_argv++;
6238 while (extra_argv[extra_argc]) extra_argc++;
6241 if (extra_argc < 1) {
6242 usage(NULL);
6245 cmd = match_command(extra_argv[0]);
6246 if (cmd == NULL) {
6247 fprintf(stderr, "Unknown command '%s'\n", extra_argv[0]);
6248 exit(1);
6251 /* Enable logging */
6252 setup_logging("ctdb", DEBUG_STDERR);
6253 ok = debug_level_parse(options.debuglevelstr, &loglevel);
6254 if (!ok) {
6255 loglevel = DEBUG_ERR;
6257 debuglevel_set(loglevel);
6259 signal(SIGALRM, alarm_handler);
6260 alarm(options.maxruntime);
6262 ret = process_command(cmd, extra_argc, extra_argv);
6263 if (ret == -1) {
6264 ret = 1;
6267 (void)poptFreeContext(pc);
6269 return ret;