smbd: Fix cached dos attributes
[Samba.git] / ctdb / tools / ctdb.c
blob1e438d9c1c0642c7d84cd0d0eb2992a84416d4ad
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 "version.h"
34 #include "lib/util/debug.h"
35 #include "lib/util/samba_util.h"
36 #include "lib/util/util_file.h"
37 #include "lib/util/sys_rw.h"
38 #include "lib/util/smb_strtox.h"
40 #include "common/db_hash.h"
41 #include "common/logging.h"
42 #include "common/path.h"
43 #include "protocol/protocol.h"
44 #include "protocol/protocol_basic.h"
45 #include "protocol/protocol_api.h"
46 #include "protocol/protocol_util.h"
47 #include "common/system_socket.h"
48 #include "client/client.h"
49 #include "client/client_sync.h"
51 #define TIMEOUT() timeval_current_ofs(options.timelimit, 0)
53 #define SRVID_CTDB_TOOL (CTDB_SRVID_TOOL_RANGE | 0x0001000000000000LL)
54 #define SRVID_CTDB_PUSHDB (CTDB_SRVID_TOOL_RANGE | 0x0002000000000000LL)
56 #define NODE_FLAGS_UNKNOWN 0x00000040
58 static struct {
59 const char *debuglevelstr;
60 int timelimit;
61 int pnn;
62 int machinereadable;
63 const char *sep;
64 int machineparsable;
65 int verbose;
66 int maxruntime;
67 int printemptyrecords;
68 int printdatasize;
69 int printlmaster;
70 int printhash;
71 int printrecordflags;
72 } options;
74 static poptContext pc;
76 struct ctdb_context {
77 struct tevent_context *ev;
78 struct ctdb_client_context *client;
79 struct ctdb_node_map *nodemap;
80 uint32_t pnn, cmd_pnn, leader_pnn;
81 uint64_t srvid;
84 static void usage(const char *command);
86 static int disable_takeover_runs(TALLOC_CTX *mem_ctx,
87 struct ctdb_context *ctdb,
88 uint32_t timeout,
89 uint32_t *pnn_list,
90 int count);
91 static int send_ipreallocated_control_to_nodes(TALLOC_CTX *mem_ctx,
92 struct ctdb_context *ctdb,
93 uint32_t *pnn_list,
94 int count);
97 * Utility Functions
100 static double timeval_delta(struct timeval *tv2, struct timeval *tv)
102 return (tv2->tv_sec - tv->tv_sec) +
103 (tv2->tv_usec - tv->tv_usec) * 1.0e-6;
106 static struct ctdb_node_and_flags *get_node_by_pnn(
107 struct ctdb_node_map *nodemap,
108 uint32_t pnn)
110 unsigned int i;
112 for (i=0; i<nodemap->num; i++) {
113 if (nodemap->node[i].pnn == pnn) {
114 return &nodemap->node[i];
117 return NULL;
120 static const char *pretty_print_flags(TALLOC_CTX *mem_ctx, uint32_t flags)
122 static const struct {
123 uint32_t flag;
124 const char *name;
125 } flag_names[] = {
126 { NODE_FLAGS_DISCONNECTED, "DISCONNECTED" },
127 { NODE_FLAGS_UNKNOWN, "UNKNOWN" },
128 { NODE_FLAGS_PERMANENTLY_DISABLED, "DISABLED" },
129 { NODE_FLAGS_BANNED, "BANNED" },
130 { NODE_FLAGS_UNHEALTHY, "UNHEALTHY" },
131 { NODE_FLAGS_DELETED, "DELETED" },
132 { NODE_FLAGS_STOPPED, "STOPPED" },
133 { NODE_FLAGS_INACTIVE, "INACTIVE" },
135 char *flags_str = NULL;
136 size_t i;
138 for (i=0; i<ARRAY_SIZE(flag_names); i++) {
139 if (flags & flag_names[i].flag) {
140 if (flags_str == NULL) {
141 flags_str = talloc_asprintf(mem_ctx,
142 "%s", flag_names[i].name);
143 } else {
144 flags_str = talloc_asprintf_append(flags_str,
145 "|%s", flag_names[i].name);
147 if (flags_str == NULL) {
148 return "OUT-OF-MEMORY";
152 if (flags_str == NULL) {
153 return "OK";
156 return flags_str;
159 static uint64_t next_srvid(struct ctdb_context *ctdb)
161 ctdb->srvid += 1;
162 return ctdb->srvid;
166 * Get consistent nodemap information.
168 * If nodemap is already cached, use that. If not get it.
169 * If the current node is BANNED, then get nodemap from "better" node.
171 static struct ctdb_node_map *get_nodemap(struct ctdb_context *ctdb, bool force)
173 TALLOC_CTX *tmp_ctx;
174 struct ctdb_node_map *nodemap;
175 struct ctdb_node_and_flags *node;
176 uint32_t current_node;
177 int ret;
179 if (force) {
180 TALLOC_FREE(ctdb->nodemap);
183 if (ctdb->nodemap != NULL) {
184 return ctdb->nodemap;
187 tmp_ctx = talloc_new(ctdb);
188 if (tmp_ctx == NULL) {
189 return false;
192 current_node = ctdb->pnn;
193 again:
194 ret = ctdb_ctrl_get_nodemap(tmp_ctx, ctdb->ev, ctdb->client,
195 current_node, TIMEOUT(), &nodemap);
196 if (ret != 0) {
197 fprintf(stderr, "Failed to get nodemap from node %u\n",
198 current_node);
199 goto failed;
202 node = get_node_by_pnn(nodemap, current_node);
203 if (node->flags & NODE_FLAGS_BANNED) {
204 /* Pick next node */
205 do {
206 current_node = (current_node + 1) % nodemap->num;
207 node = get_node_by_pnn(nodemap, current_node);
208 if (! (node->flags &
209 (NODE_FLAGS_DELETED|NODE_FLAGS_DISCONNECTED))) {
210 break;
212 } while (current_node != ctdb->pnn);
214 if (current_node == ctdb->pnn) {
215 /* Tried all nodes in the cluster */
216 fprintf(stderr, "Warning: All nodes are banned.\n");
217 goto failed;
220 goto again;
223 ctdb->nodemap = talloc_steal(ctdb, nodemap);
224 return nodemap;
226 failed:
227 talloc_free(tmp_ctx);
228 return NULL;
231 static void print_pnn(uint32_t pnn)
233 if (pnn == CTDB_UNKNOWN_PNN) {
234 printf("UNKNOWN\n");
235 return;
238 printf("%u\n", pnn);
241 static bool verify_pnn(struct ctdb_context *ctdb, int pnn)
243 struct ctdb_node_map *nodemap;
244 bool found;
245 unsigned int i;
247 if (pnn == -1) {
248 return false;
251 nodemap = get_nodemap(ctdb, false);
252 if (nodemap == NULL) {
253 return false;
256 found = false;
257 for (i=0; i<nodemap->num; i++) {
258 if (nodemap->node[i].pnn == (uint32_t)pnn) {
259 found = true;
260 break;
263 if (! found) {
264 fprintf(stderr, "Node %u does not exist\n", pnn);
265 return false;
268 if (nodemap->node[i].flags &
269 (NODE_FLAGS_DISCONNECTED|NODE_FLAGS_DELETED)) {
270 fprintf(stderr, "Node %u has status %s\n", pnn,
271 pretty_print_flags(ctdb, nodemap->node[i].flags));
272 return false;
275 return true;
278 static struct ctdb_node_map *talloc_nodemap(TALLOC_CTX *mem_ctx,
279 struct ctdb_node_map *nodemap)
281 struct ctdb_node_map *nodemap2;
283 nodemap2 = talloc_zero(mem_ctx, struct ctdb_node_map);
284 if (nodemap2 == NULL) {
285 return NULL;
288 nodemap2->node = talloc_array(nodemap2, struct ctdb_node_and_flags,
289 nodemap->num);
290 if (nodemap2->node == NULL) {
291 talloc_free(nodemap2);
292 return NULL;
295 return nodemap2;
299 * Get the number and the list of matching nodes
301 * nodestring := NULL | all | pnn,[pnn,...]
303 * If nodestring is NULL, use the current node.
305 static bool parse_nodestring(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
306 const char *nodestring,
307 struct ctdb_node_map **out)
309 struct ctdb_node_map *nodemap, *nodemap2;
310 struct ctdb_node_and_flags *node;
311 unsigned int i;
313 nodemap = get_nodemap(ctdb, false);
314 if (nodemap == NULL) {
315 return false;
318 nodemap2 = talloc_nodemap(mem_ctx, nodemap);
319 if (nodemap2 == NULL) {
320 return false;
323 if (nodestring == NULL) {
324 for (i=0; i<nodemap->num; i++) {
325 if (nodemap->node[i].pnn == ctdb->cmd_pnn) {
326 nodemap2->node[0] = nodemap->node[i];
327 break;
330 nodemap2->num = 1;
332 goto done;
335 if (strcmp(nodestring, "all") == 0) {
336 for (i=0; i<nodemap->num; i++) {
337 nodemap2->node[i] = nodemap->node[i];
339 nodemap2->num = nodemap->num;
341 goto done;
342 } else {
343 char *ns, *tok;
344 int error = 0;
346 ns = talloc_strdup(mem_ctx, nodestring);
347 if (ns == NULL) {
348 return false;
351 tok = strtok(ns, ",");
352 while (tok != NULL) {
353 uint32_t pnn;
355 pnn = (uint32_t)smb_strtoul(tok,
356 NULL,
358 &error,
359 SMB_STR_STANDARD);
360 if (error != 0) {
361 fprintf(stderr, "Invalid node %s\n", tok);
362 return false;
365 node = get_node_by_pnn(nodemap, pnn);
366 if (node == NULL) {
367 fprintf(stderr, "Node %u does not exist\n",
368 pnn);
369 return false;
372 nodemap2->node[nodemap2->num] = *node;
373 nodemap2->num += 1;
375 tok = strtok(NULL, ",");
379 done:
380 *out = nodemap2;
381 return true;
385 * Remote nodes are initialised as UNHEALTHY in the daemon and their
386 * true status is updated after they are connected. However, there
387 * is a small window when a healthy node may be shown as unhealthy
388 * between connecting and the status update. Hide this for nodes
389 * that are not DISCONNECTED nodes by reporting them as UNKNOWN until
390 * the runstate passes FIRST_RECOVERY. Code paths where this is used
391 * do not make any control decisions depending upon unknown/unhealthy
392 * state.
394 static struct ctdb_node_map *get_nodemap_unknown(
395 TALLOC_CTX *mem_ctx,
396 struct ctdb_context *ctdb,
397 struct ctdb_node_map *nodemap_in)
399 unsigned int i;
400 int ret;
401 enum ctdb_runstate runstate;
402 struct ctdb_node_map *nodemap;
404 ret = ctdb_ctrl_get_runstate(mem_ctx,
405 ctdb->ev,
406 ctdb->client,
407 ctdb->cmd_pnn,
408 TIMEOUT(),
409 &runstate);
410 if (ret != 0 ) {
411 printf("Unable to get runstate");
412 return NULL;
415 nodemap = talloc_nodemap(mem_ctx, nodemap_in);
416 if (nodemap == NULL) {
417 printf("Unable to get nodemap");
418 return NULL;
421 nodemap->num = nodemap_in->num;
422 for (i=0; i<nodemap->num; i++) {
423 struct ctdb_node_and_flags *node_in = &nodemap_in->node[i];
424 struct ctdb_node_and_flags *node = &nodemap->node[i];
426 *node = *node_in;
428 if (node->flags & NODE_FLAGS_DELETED) {
429 continue;
432 if ((runstate <= CTDB_RUNSTATE_FIRST_RECOVERY) &&
433 !(node->flags & NODE_FLAGS_DISCONNECTED) &&
434 (node->pnn != ctdb->cmd_pnn)) {
435 node->flags = NODE_FLAGS_UNKNOWN;
439 return nodemap;
442 /* Compare IP address */
443 static bool ctdb_same_ip(ctdb_sock_addr *ip1, ctdb_sock_addr *ip2)
445 bool ret = false;
447 if (ip1->sa.sa_family != ip2->sa.sa_family) {
448 return false;
451 switch (ip1->sa.sa_family) {
452 case AF_INET:
453 ret = (memcmp(&ip1->ip.sin_addr, &ip2->ip.sin_addr,
454 sizeof(struct in_addr)) == 0);
455 break;
457 case AF_INET6:
458 ret = (memcmp(&ip1->ip6.sin6_addr, &ip2->ip6.sin6_addr,
459 sizeof(struct in6_addr)) == 0);
460 break;
463 return ret;
466 /* Append a node to a node map with given address and flags */
467 static bool node_map_add(struct ctdb_node_map *nodemap,
468 const char *nstr, uint32_t flags)
470 ctdb_sock_addr addr;
471 uint32_t num;
472 struct ctdb_node_and_flags *n;
473 int ret;
475 ret = ctdb_sock_addr_from_string(nstr, &addr, false);
476 if (ret != 0) {
477 fprintf(stderr, "Invalid IP address %s\n", nstr);
478 return false;
481 num = nodemap->num;
482 nodemap->node = talloc_realloc(nodemap, nodemap->node,
483 struct ctdb_node_and_flags, num+1);
484 if (nodemap->node == NULL) {
485 return false;
488 n = &nodemap->node[num];
489 n->addr = addr;
490 n->pnn = num;
491 n->flags = flags;
493 nodemap->num = num+1;
494 return true;
497 /* Read a nodes file into a node map */
498 static struct ctdb_node_map *ctdb_read_nodes_file(TALLOC_CTX *mem_ctx,
499 const char *nlist)
501 char **lines;
502 int nlines;
503 int i;
504 struct ctdb_node_map *nodemap;
506 nodemap = talloc_zero(mem_ctx, struct ctdb_node_map);
507 if (nodemap == NULL) {
508 return NULL;
511 lines = file_lines_load(nlist, &nlines, 0, mem_ctx);
512 if (lines == NULL) {
513 return NULL;
516 while (nlines > 0 && strcmp(lines[nlines-1], "") == 0) {
517 nlines--;
520 for (i=0; i<nlines; i++) {
521 char *node;
522 uint32_t flags;
523 size_t len;
525 node = lines[i];
526 /* strip leading spaces */
527 while((*node == ' ') || (*node == '\t')) {
528 node++;
531 len = strlen(node);
533 /* strip trailing spaces */
534 while ((len > 1) &&
535 ((node[len-1] == ' ') || (node[len-1] == '\t')))
537 node[len-1] = '\0';
538 len--;
541 if (len == 0) {
542 continue;
544 if (*node == '#') {
545 /* A "deleted" node is a node that is
546 commented out in the nodes file. This is
547 used instead of removing a line, which
548 would cause subsequent nodes to change
549 their PNN. */
550 flags = NODE_FLAGS_DELETED;
551 node = discard_const("0.0.0.0");
552 } else {
553 flags = 0;
555 if (! node_map_add(nodemap, node, flags)) {
556 talloc_free(lines);
557 TALLOC_FREE(nodemap);
558 return NULL;
562 talloc_free(lines);
563 return nodemap;
566 static struct ctdb_node_map *read_nodes_file(TALLOC_CTX *mem_ctx, uint32_t pnn)
568 struct ctdb_node_map *nodemap;
569 const char *nodes_list = NULL;
571 const char *basedir = getenv("CTDB_BASE");
572 if (basedir == NULL) {
573 basedir = CTDB_ETCDIR;
575 nodes_list = talloc_asprintf(mem_ctx, "%s/nodes", basedir);
576 if (nodes_list == NULL) {
577 fprintf(stderr, "Memory allocation error\n");
578 return NULL;
581 nodemap = ctdb_read_nodes_file(mem_ctx, nodes_list);
582 if (nodemap == NULL) {
583 fprintf(stderr, "Failed to read nodes file \"%s\"\n",
584 nodes_list);
585 return NULL;
588 return nodemap;
591 static struct ctdb_dbid *db_find(TALLOC_CTX *mem_ctx,
592 struct ctdb_context *ctdb,
593 struct ctdb_dbid_map *dbmap,
594 const char *db_name)
596 struct ctdb_dbid *db = NULL;
597 const char *name;
598 unsigned int i;
599 int ret;
601 for (i=0; i<dbmap->num; i++) {
602 ret = ctdb_ctrl_get_dbname(mem_ctx, ctdb->ev, ctdb->client,
603 ctdb->pnn, TIMEOUT(),
604 dbmap->dbs[i].db_id, &name);
605 if (ret != 0) {
606 return false;
609 if (strcmp(db_name, name) == 0) {
610 talloc_free(discard_const(name));
611 db = &dbmap->dbs[i];
612 break;
616 return db;
619 static bool db_exists(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
620 const char *db_arg, uint32_t *db_id,
621 const char **db_name, uint8_t *db_flags)
623 struct ctdb_dbid_map *dbmap;
624 struct ctdb_dbid *db = NULL;
625 uint32_t id = 0;
626 const char *name = NULL;
627 unsigned int i;
628 int ret = 0;
630 ret = ctdb_ctrl_get_dbmap(mem_ctx, ctdb->ev, ctdb->client,
631 ctdb->pnn, TIMEOUT(), &dbmap);
632 if (ret != 0) {
633 return false;
636 if (strncmp(db_arg, "0x", 2) == 0) {
637 id = smb_strtoul(db_arg, NULL, 0, &ret, SMB_STR_STANDARD);
638 if (ret != 0) {
639 return false;
641 for (i=0; i<dbmap->num; i++) {
642 if (id == dbmap->dbs[i].db_id) {
643 db = &dbmap->dbs[i];
644 break;
647 } else {
648 name = db_arg;
649 db = db_find(mem_ctx, ctdb, dbmap, name);
652 if (db == NULL) {
653 fprintf(stderr, "No database matching '%s' found\n", db_arg);
654 return false;
657 if (name == NULL) {
658 ret = ctdb_ctrl_get_dbname(mem_ctx, ctdb->ev, ctdb->client,
659 ctdb->pnn, TIMEOUT(), id, &name);
660 if (ret != 0) {
661 return false;
665 if (db_id != NULL) {
666 *db_id = db->db_id;
668 if (db_name != NULL) {
669 *db_name = talloc_strdup(mem_ctx, name);
671 if (db_flags != NULL) {
672 *db_flags = db->flags;
674 return true;
677 static int hex_to_data(const char *str, size_t len, TALLOC_CTX *mem_ctx,
678 TDB_DATA *out)
680 unsigned int i;
681 TDB_DATA data;
683 if (len & 0x01) {
684 fprintf(stderr, "Key (%s) contains odd number of hex digits\n",
685 str);
686 return EINVAL;
689 data.dsize = len / 2;
690 data.dptr = talloc_size(mem_ctx, data.dsize);
691 if (data.dptr == NULL) {
692 return ENOMEM;
695 for (i=0; i<data.dsize; i++) {
696 bool ok = hex_byte(&str[i*2], &data.dptr[i]);
697 if (!ok) {
698 fprintf(stderr, "Invalid hex: %s\n", &str[i*2]);
699 return EINVAL;
703 *out = data;
704 return 0;
707 static int str_to_data(const char *str, size_t len, TALLOC_CTX *mem_ctx,
708 TDB_DATA *out)
710 TDB_DATA data;
711 int ret = 0;
713 if (strncmp(str, "0x", 2) == 0) {
714 ret = hex_to_data(str+2, len-2, mem_ctx, &data);
715 if (ret != 0) {
716 return ret;
718 } else {
719 data.dptr = talloc_memdup(mem_ctx, str, len);
720 if (data.dptr == NULL) {
721 return ENOMEM;
723 data.dsize = len;
726 *out = data;
727 return 0;
730 static int run_helper(TALLOC_CTX *mem_ctx, const char *command,
731 const char *path, int argc, const char **argv)
733 pid_t pid;
734 int save_errno, status, ret;
735 const char **new_argv;
736 int i;
738 new_argv = talloc_array(mem_ctx, const char *, argc + 2);
739 if (new_argv == NULL) {
740 return ENOMEM;
743 new_argv[0] = path;
744 for (i=0; i<argc; i++) {
745 new_argv[i+1] = argv[i];
747 new_argv[argc+1] = NULL;
749 pid = fork();
750 if (pid < 0) {
751 save_errno = errno;
752 talloc_free(new_argv);
753 fprintf(stderr, "Failed to fork %s (%s) - %s\n",
754 command, path, strerror(save_errno));
755 return save_errno;
758 if (pid == 0) {
759 ret = execv(path, discard_const(new_argv));
760 if (ret == -1) {
761 _exit(64+errno);
763 /* Should not happen */
764 _exit(64+ENOEXEC);
767 talloc_free(new_argv);
769 ret = waitpid(pid, &status, 0);
770 if (ret == -1) {
771 save_errno = errno;
772 fprintf(stderr, "waitpid() failed for %s - %s\n",
773 command, strerror(save_errno));
774 return save_errno;
777 if (WIFEXITED(status)) {
778 int pstatus = WEXITSTATUS(status);
779 if (WIFSIGNALED(status)) {
780 fprintf(stderr, "%s terminated with signal %d\n",
781 command, WTERMSIG(status));
782 ret = EINTR;
783 } else if (pstatus >= 64 && pstatus < 255) {
784 fprintf(stderr, "%s failed with error %d\n",
785 command, pstatus-64);
786 ret = pstatus - 64;
787 } else {
788 ret = pstatus;
790 return ret;
791 } else if (WIFSIGNALED(status)) {
792 fprintf(stderr, "%s terminated with signal %d\n",
793 command, WTERMSIG(status));
794 return EINTR;
797 return 0;
800 static void leader_handler(uint64_t srvid,
801 TDB_DATA data,
802 void *private_data)
804 struct ctdb_context *ctdb = talloc_get_type_abort(
805 private_data, struct ctdb_context);
806 uint32_t leader_pnn;
807 size_t np;
808 int ret;
810 ret = ctdb_uint32_pull(data.dptr, data.dsize, &leader_pnn, &np);
811 if (ret != 0) {
812 /* Ignore packet */
813 return;
816 ctdb->leader_pnn = leader_pnn;
819 static bool get_leader_done(void *private_data)
821 struct ctdb_context *ctdb = talloc_get_type_abort(
822 private_data, struct ctdb_context);
824 return ctdb->leader_pnn != CTDB_UNKNOWN_PNN;
827 static int get_leader(TALLOC_CTX *mem_ctx,
828 struct ctdb_context *ctdb,
829 uint32_t *leader)
831 int ret;
833 ret = ctdb_client_wait_func_timeout(ctdb->ev,
834 get_leader_done,
835 ctdb,
836 TIMEOUT());
838 * If ETIMEDOUT then assume there is no leader and succeed so
839 * initial value of CTDB_UNKNOWN_PNN is returned
841 if (ret == ETIMEDOUT) {
842 ret = 0;
843 } else if (ret != 0) {
844 fprintf(stderr, "Error getting leader\n");
845 return ret;
848 *leader = ctdb->leader_pnn;
849 return 0;
853 * Command Functions
856 static int control_version(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
857 int argc, const char **argv)
859 printf("%s\n", SAMBA_VERSION_STRING);
860 return 0;
863 static bool partially_online(TALLOC_CTX *mem_ctx,
864 struct ctdb_context *ctdb,
865 struct ctdb_node_and_flags *node)
867 struct ctdb_iface_list *iface_list;
868 unsigned int i;
869 int ret;
870 bool status = false;
872 if (node->flags != 0) {
873 return false;
876 ret = ctdb_ctrl_get_ifaces(mem_ctx, ctdb->ev, ctdb->client,
877 node->pnn, TIMEOUT(), &iface_list);
878 if (ret != 0) {
879 return false;
882 status = false;
883 for (i=0; i < iface_list->num; i++) {
884 if (iface_list->iface[i].link_state == 0) {
885 status = true;
886 break;
890 return status;
893 static void print_nodemap_machine(TALLOC_CTX *mem_ctx,
894 struct ctdb_context *ctdb,
895 struct ctdb_node_map *nodemap,
896 uint32_t mypnn)
898 struct ctdb_node_and_flags *node;
899 unsigned int i;
901 printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
902 options.sep,
903 "Node", options.sep,
904 "IP", options.sep,
905 "Disconnected", options.sep,
906 "Unknown", options.sep,
907 "Banned", options.sep,
908 "Disabled", options.sep,
909 "Unhealthy", options.sep,
910 "Stopped", options.sep,
911 "Inactive", options.sep,
912 "PartiallyOnline", options.sep,
913 "ThisNode", options.sep);
915 for (i=0; i<nodemap->num; i++) {
916 node = &nodemap->node[i];
917 if (node->flags & NODE_FLAGS_DELETED) {
918 continue;
921 printf("%s%u%s%s%s%d%s%d%s%d%s%d%s%d%s%d%s%d%s%d%s%c%s\n",
922 options.sep,
923 node->pnn, options.sep,
924 ctdb_sock_addr_to_string(mem_ctx, &node->addr, false),
925 options.sep,
926 !! (node->flags & NODE_FLAGS_DISCONNECTED), options.sep,
927 !! (node->flags & NODE_FLAGS_UNKNOWN), options.sep,
928 !! (node->flags & NODE_FLAGS_BANNED), options.sep,
929 !! (node->flags & NODE_FLAGS_PERMANENTLY_DISABLED),
930 options.sep,
931 !! (node->flags & NODE_FLAGS_UNHEALTHY), options.sep,
932 !! (node->flags & NODE_FLAGS_STOPPED), options.sep,
933 !! (node->flags & NODE_FLAGS_INACTIVE), options.sep,
934 partially_online(mem_ctx, ctdb, node), options.sep,
935 (node->pnn == mypnn)?'Y':'N', options.sep);
940 static void print_nodemap(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
941 struct ctdb_node_map *nodemap, uint32_t mypnn,
942 bool print_header)
944 struct ctdb_node_and_flags *node;
945 int num_deleted_nodes = 0;
946 unsigned int i;
948 for (i=0; i<nodemap->num; i++) {
949 if (nodemap->node[i].flags & NODE_FLAGS_DELETED) {
950 num_deleted_nodes++;
954 if (print_header) {
955 if (num_deleted_nodes == 0) {
956 printf("Number of nodes:%d\n", nodemap->num);
957 } else {
958 printf("Number of nodes:%d "
959 "(including %d deleted nodes)\n",
960 nodemap->num, num_deleted_nodes);
964 for (i=0; i<nodemap->num; i++) {
965 node = &nodemap->node[i];
966 if (node->flags & NODE_FLAGS_DELETED) {
967 continue;
970 printf("pnn:%u %-16s %s%s\n",
971 node->pnn,
972 ctdb_sock_addr_to_string(mem_ctx, &node->addr, false),
973 partially_online(mem_ctx, ctdb, node) ?
974 "PARTIALLYONLINE" :
975 pretty_print_flags(mem_ctx, node->flags),
976 node->pnn == mypnn ? " (THIS NODE)" : "");
980 static void print_status(TALLOC_CTX *mem_ctx,
981 struct ctdb_context *ctdb,
982 struct ctdb_node_map *nodemap,
983 uint32_t mypnn,
984 struct ctdb_vnn_map *vnnmap,
985 int recmode,
986 uint32_t leader)
988 unsigned int i;
990 print_nodemap(mem_ctx, ctdb, nodemap, mypnn, true);
992 if (vnnmap->generation == INVALID_GENERATION) {
993 printf("Generation:INVALID\n");
994 } else {
995 printf("Generation:%u\n", vnnmap->generation);
997 printf("Size:%d\n", vnnmap->size);
998 for (i=0; i<vnnmap->size; i++) {
999 printf("hash:%d lmaster:%d\n", i, vnnmap->map[i]);
1002 printf("Recovery mode:%s (%d)\n",
1003 recmode == CTDB_RECOVERY_NORMAL ? "NORMAL" : "RECOVERY",
1004 recmode);
1005 printf("Leader:");
1006 print_pnn(leader);
1009 static int control_status(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1010 int argc, const char **argv)
1012 struct ctdb_node_map *nodemap_in;
1013 struct ctdb_node_map *nodemap;
1014 struct ctdb_vnn_map *vnnmap;
1015 int recmode;
1016 uint32_t leader;
1017 int ret;
1019 if (argc != 0) {
1020 usage("status");
1023 nodemap_in = get_nodemap(ctdb, false);
1024 if (nodemap_in == NULL) {
1025 return 1;
1028 nodemap = get_nodemap_unknown(mem_ctx, ctdb, nodemap_in);
1029 if (nodemap == NULL) {
1030 return 1;
1033 if (options.machinereadable == 1) {
1034 print_nodemap_machine(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn);
1035 return 0;
1038 ret = ctdb_ctrl_getvnnmap(mem_ctx, ctdb->ev, ctdb->client,
1039 ctdb->cmd_pnn, TIMEOUT(), &vnnmap);
1040 if (ret != 0) {
1041 return ret;
1044 ret = ctdb_ctrl_get_recmode(mem_ctx, ctdb->ev, ctdb->client,
1045 ctdb->cmd_pnn, TIMEOUT(), &recmode);
1046 if (ret != 0) {
1047 return ret;
1050 ret = get_leader(mem_ctx, ctdb, &leader);
1051 if (ret != 0) {
1052 return ret;
1055 print_status(mem_ctx,
1056 ctdb,
1057 nodemap,
1058 ctdb->cmd_pnn,
1059 vnnmap,
1060 recmode,
1061 leader);
1062 return 0;
1065 static int control_uptime(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1066 int argc, const char **argv)
1068 struct ctdb_uptime *uptime;
1069 int ret, tmp, days, hours, minutes, seconds;
1071 ret = ctdb_ctrl_uptime(mem_ctx, ctdb->ev, ctdb->client,
1072 ctdb->cmd_pnn, TIMEOUT(), &uptime);
1073 if (ret != 0) {
1074 return ret;
1077 printf("Current time of node %-4u : %s",
1078 ctdb->cmd_pnn, ctime(&uptime->current_time.tv_sec));
1080 tmp = uptime->current_time.tv_sec - uptime->ctdbd_start_time.tv_sec;
1081 seconds = tmp % 60; tmp /= 60;
1082 minutes = tmp % 60; tmp /= 60;
1083 hours = tmp % 24; tmp /= 24;
1084 days = tmp;
1086 printf("Ctdbd start time : (%03d %02d:%02d:%02d) %s",
1087 days, hours, minutes, seconds,
1088 ctime(&uptime->ctdbd_start_time.tv_sec));
1090 tmp = uptime->current_time.tv_sec - uptime->last_recovery_finished.tv_sec;
1091 seconds = tmp % 60; tmp /= 60;
1092 minutes = tmp % 60; tmp /= 60;
1093 hours = tmp % 24; tmp /= 24;
1094 days = tmp;
1096 printf("Time of last recovery/failover: (%03d %02d:%02d:%02d) %s",
1097 days, hours, minutes, seconds,
1098 ctime(&uptime->last_recovery_finished.tv_sec));
1100 printf("Duration of last recovery/failover: %lf seconds\n",
1101 timeval_delta(&uptime->last_recovery_finished,
1102 &uptime->last_recovery_started));
1104 return 0;
1107 static int control_ping(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1108 int argc, const char **argv)
1110 struct timeval tv;
1111 int ret, num_clients;
1113 tv = timeval_current();
1114 ret = ctdb_ctrl_ping(mem_ctx, ctdb->ev, ctdb->client,
1115 ctdb->cmd_pnn, TIMEOUT(), &num_clients);
1116 if (ret != 0) {
1117 return ret;
1120 printf("response from %u time=%.6f sec (%d clients)\n",
1121 ctdb->cmd_pnn, timeval_elapsed(&tv), num_clients);
1122 return 0;
1125 static int control_runstate(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1126 int argc, const char **argv)
1128 enum ctdb_runstate runstate;
1129 bool found;
1130 int ret, i;
1132 ret = ctdb_ctrl_get_runstate(mem_ctx, ctdb->ev, ctdb->client,
1133 ctdb->cmd_pnn, TIMEOUT(), &runstate);
1134 if (ret != 0) {
1135 return ret;
1138 found = true;
1139 for (i=0; i<argc; i++) {
1140 enum ctdb_runstate t;
1142 found = false;
1143 t = ctdb_runstate_from_string(argv[i]);
1144 if (t == CTDB_RUNSTATE_UNKNOWN) {
1145 printf("Invalid run state (%s)\n", argv[i]);
1146 return 1;
1149 if (t == runstate) {
1150 found = true;
1151 break;
1155 if (! found) {
1156 printf("CTDB not in required run state (got %s)\n",
1157 ctdb_runstate_to_string(runstate));
1158 return 1;
1161 printf("%s\n", ctdb_runstate_to_string(runstate));
1162 return 0;
1165 static int control_getvar(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1166 int argc, const char **argv)
1168 struct ctdb_var_list *tun_var_list;
1169 uint32_t value;
1170 int ret, i;
1171 bool found;
1173 if (argc != 1) {
1174 usage("getvar");
1177 ret = ctdb_ctrl_list_tunables(mem_ctx, ctdb->ev, ctdb->client,
1178 ctdb->cmd_pnn, TIMEOUT(), &tun_var_list);
1179 if (ret != 0) {
1180 fprintf(stderr,
1181 "Failed to get list of variables from node %u\n",
1182 ctdb->cmd_pnn);
1183 return ret;
1186 found = false;
1187 for (i=0; i<tun_var_list->count; i++) {
1188 if (strcasecmp(tun_var_list->var[i], argv[0]) == 0) {
1189 found = true;
1190 break;
1194 if (! found) {
1195 printf("No such tunable %s\n", argv[0]);
1196 return 1;
1199 ret = ctdb_ctrl_get_tunable(mem_ctx, ctdb->ev, ctdb->client,
1200 ctdb->cmd_pnn, TIMEOUT(), argv[0], &value);
1201 if (ret != 0) {
1202 return ret;
1205 printf("%-26s = %u\n", argv[0], value);
1206 return 0;
1209 static int control_setvar(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1210 int argc, const char **argv)
1212 struct ctdb_var_list *tun_var_list;
1213 struct ctdb_tunable tunable;
1214 bool found;
1215 int i;
1216 int ret = 0;
1218 if (argc != 2) {
1219 usage("setvar");
1222 ret = ctdb_ctrl_list_tunables(mem_ctx, ctdb->ev, ctdb->client,
1223 ctdb->cmd_pnn, TIMEOUT(), &tun_var_list);
1224 if (ret != 0) {
1225 fprintf(stderr,
1226 "Failed to get list of variables from node %u\n",
1227 ctdb->cmd_pnn);
1228 return ret;
1231 found = false;
1232 for (i=0; i<tun_var_list->count; i++) {
1233 if (strcasecmp(tun_var_list->var[i], argv[0]) == 0) {
1234 found = true;
1235 break;
1239 if (! found) {
1240 printf("No such tunable %s\n", argv[0]);
1241 return 1;
1244 tunable.name = argv[0];
1245 tunable.value = smb_strtoul(argv[1], NULL, 0, &ret, SMB_STR_STANDARD);
1246 if (ret != 0) {
1247 return ret;
1250 ret = ctdb_ctrl_set_tunable(mem_ctx, ctdb->ev, ctdb->client,
1251 ctdb->cmd_pnn, TIMEOUT(), &tunable);
1252 if (ret != 0) {
1253 if (ret == 1) {
1254 fprintf(stderr,
1255 "Setting obsolete tunable variable '%s'\n",
1256 tunable.name);
1257 return 0;
1261 return ret;
1264 static int control_listvars(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1265 int argc, const char **argv)
1267 struct ctdb_var_list *tun_var_list;
1268 int ret, i;
1270 if (argc != 0) {
1271 usage("listvars");
1274 ret = ctdb_ctrl_list_tunables(mem_ctx, ctdb->ev, ctdb->client,
1275 ctdb->cmd_pnn, TIMEOUT(), &tun_var_list);
1276 if (ret != 0) {
1277 return ret;
1280 for (i=0; i<tun_var_list->count; i++) {
1281 control_getvar(mem_ctx, ctdb, 1, &tun_var_list->var[i]);
1284 return 0;
1287 const struct {
1288 const char *name;
1289 uint32_t offset;
1290 } stats_fields[] = {
1291 #define STATISTICS_FIELD(n) { #n, offsetof(struct ctdb_statistics, n) }
1292 STATISTICS_FIELD(num_clients),
1293 STATISTICS_FIELD(frozen),
1294 STATISTICS_FIELD(recovering),
1295 STATISTICS_FIELD(num_recoveries),
1296 STATISTICS_FIELD(client_packets_sent),
1297 STATISTICS_FIELD(client_packets_recv),
1298 STATISTICS_FIELD(node_packets_sent),
1299 STATISTICS_FIELD(node_packets_recv),
1300 STATISTICS_FIELD(keepalive_packets_sent),
1301 STATISTICS_FIELD(keepalive_packets_recv),
1302 STATISTICS_FIELD(node.req_call),
1303 STATISTICS_FIELD(node.reply_call),
1304 STATISTICS_FIELD(node.req_dmaster),
1305 STATISTICS_FIELD(node.reply_dmaster),
1306 STATISTICS_FIELD(node.reply_error),
1307 STATISTICS_FIELD(node.req_message),
1308 STATISTICS_FIELD(node.req_control),
1309 STATISTICS_FIELD(node.reply_control),
1310 STATISTICS_FIELD(node.req_tunnel),
1311 STATISTICS_FIELD(client.req_call),
1312 STATISTICS_FIELD(client.req_message),
1313 STATISTICS_FIELD(client.req_control),
1314 STATISTICS_FIELD(client.req_tunnel),
1315 STATISTICS_FIELD(timeouts.call),
1316 STATISTICS_FIELD(timeouts.control),
1317 STATISTICS_FIELD(timeouts.traverse),
1318 STATISTICS_FIELD(locks.num_calls),
1319 STATISTICS_FIELD(locks.num_current),
1320 STATISTICS_FIELD(locks.num_pending),
1321 STATISTICS_FIELD(locks.num_failed),
1322 STATISTICS_FIELD(total_calls),
1323 STATISTICS_FIELD(pending_calls),
1324 STATISTICS_FIELD(childwrite_calls),
1325 STATISTICS_FIELD(pending_childwrite_calls),
1326 STATISTICS_FIELD(memory_used),
1327 STATISTICS_FIELD(max_hop_count),
1328 STATISTICS_FIELD(total_ro_delegations),
1329 STATISTICS_FIELD(total_ro_revokes),
1332 #define LATENCY_AVG(v) ((v).num ? (v).total / (v).num : 0.0 )
1334 static void print_statistics_machine(struct ctdb_statistics *s,
1335 bool show_header)
1337 size_t i;
1339 if (show_header) {
1340 printf("CTDB version%s", options.sep);
1341 printf("Current time of statistics%s", options.sep);
1342 printf("Statistics collected since%s", options.sep);
1343 for (i=0; i<ARRAY_SIZE(stats_fields); i++) {
1344 printf("%s%s", stats_fields[i].name, options.sep);
1346 printf("num_reclock_ctdbd_latency%s", options.sep);
1347 printf("min_reclock_ctdbd_latency%s", options.sep);
1348 printf("avg_reclock_ctdbd_latency%s", options.sep);
1349 printf("max_reclock_ctdbd_latency%s", options.sep);
1351 printf("num_reclock_recd_latency%s", options.sep);
1352 printf("min_reclock_recd_latency%s", options.sep);
1353 printf("avg_reclock_recd_latency%s", options.sep);
1354 printf("max_reclock_recd_latency%s", options.sep);
1356 printf("num_call_latency%s", options.sep);
1357 printf("min_call_latency%s", options.sep);
1358 printf("avg_call_latency%s", options.sep);
1359 printf("max_call_latency%s", options.sep);
1361 printf("num_lockwait_latency%s", options.sep);
1362 printf("min_lockwait_latency%s", options.sep);
1363 printf("avg_lockwait_latency%s", options.sep);
1364 printf("max_lockwait_latency%s", options.sep);
1366 printf("num_childwrite_latency%s", options.sep);
1367 printf("min_childwrite_latency%s", options.sep);
1368 printf("avg_childwrite_latency%s", options.sep);
1369 printf("max_childwrite_latency%s", options.sep);
1370 printf("\n");
1373 printf("%u%s", CTDB_PROTOCOL, options.sep);
1374 printf("%u%s", (uint32_t)s->statistics_current_time.tv_sec, options.sep);
1375 printf("%u%s", (uint32_t)s->statistics_start_time.tv_sec, options.sep);
1376 for (i=0;i<ARRAY_SIZE(stats_fields);i++) {
1377 printf("%u%s",
1378 *(uint32_t *)(stats_fields[i].offset+(uint8_t *)s),
1379 options.sep);
1381 printf("%u%s", s->reclock.ctdbd.num, options.sep);
1382 printf("%.6f%s", s->reclock.ctdbd.min, options.sep);
1383 printf("%.6f%s", LATENCY_AVG(s->reclock.ctdbd), options.sep);
1384 printf("%.6f%s", s->reclock.ctdbd.max, options.sep);
1386 printf("%u%s", s->reclock.recd.num, options.sep);
1387 printf("%.6f%s", s->reclock.recd.min, options.sep);
1388 printf("%.6f%s", LATENCY_AVG(s->reclock.recd), options.sep);
1389 printf("%.6f%s", s->reclock.recd.max, options.sep);
1391 printf("%d%s", s->call_latency.num, options.sep);
1392 printf("%.6f%s", s->call_latency.min, options.sep);
1393 printf("%.6f%s", LATENCY_AVG(s->call_latency), options.sep);
1394 printf("%.6f%s", s->call_latency.max, options.sep);
1396 printf("%u%s", s->locks.latency.num, options.sep);
1397 printf("%.6f%s", s->locks.latency.min, options.sep);
1398 printf("%.6f%s", LATENCY_AVG(s->locks.latency), options.sep);
1399 printf("%.6f%s", s->locks.latency.max, options.sep);
1401 printf("%d%s", s->childwrite_latency.num, options.sep);
1402 printf("%.6f%s", s->childwrite_latency.min, options.sep);
1403 printf("%.6f%s", LATENCY_AVG(s->childwrite_latency), options.sep);
1404 printf("%.6f%s", s->childwrite_latency.max, options.sep);
1405 printf("\n");
1408 static void print_statistics(struct ctdb_statistics *s)
1410 int tmp, days, hours, minutes, seconds;
1411 size_t i;
1412 const char *prefix = NULL;
1413 int preflen = 0;
1415 tmp = s->statistics_current_time.tv_sec -
1416 s->statistics_start_time.tv_sec;
1417 seconds = tmp % 60; tmp /= 60;
1418 minutes = tmp % 60; tmp /= 60;
1419 hours = tmp % 24; tmp /= 24;
1420 days = tmp;
1422 printf("CTDB version %u\n", CTDB_PROTOCOL);
1423 printf("Current time of statistics : %s",
1424 ctime(&s->statistics_current_time.tv_sec));
1425 printf("Statistics collected since : (%03d %02d:%02d:%02d) %s",
1426 days, hours, minutes, seconds,
1427 ctime(&s->statistics_start_time.tv_sec));
1429 for (i=0; i<ARRAY_SIZE(stats_fields); i++) {
1430 if (strchr(stats_fields[i].name, '.') != NULL) {
1431 preflen = strcspn(stats_fields[i].name, ".") + 1;
1432 if (! prefix ||
1433 strncmp(prefix, stats_fields[i].name, preflen) != 0) {
1434 prefix = stats_fields[i].name;
1435 printf(" %*.*s\n", preflen-1, preflen-1,
1436 stats_fields[i].name);
1438 } else {
1439 preflen = 0;
1441 printf(" %*s%-22s%*s%10u\n", preflen ? 4 : 0, "",
1442 stats_fields[i].name+preflen, preflen ? 0 : 4, "",
1443 *(uint32_t *)(stats_fields[i].offset+(uint8_t *)s));
1446 printf(" hop_count_buckets:");
1447 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
1448 printf(" %d", s->hop_count_bucket[i]);
1450 printf("\n");
1451 printf(" lock_buckets:");
1452 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
1453 printf(" %d", s->locks.buckets[i]);
1455 printf("\n");
1456 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1457 "locks_latency MIN/AVG/MAX",
1458 s->locks.latency.min, LATENCY_AVG(s->locks.latency),
1459 s->locks.latency.max, s->locks.latency.num);
1461 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1462 "reclock_ctdbd MIN/AVG/MAX",
1463 s->reclock.ctdbd.min, LATENCY_AVG(s->reclock.ctdbd),
1464 s->reclock.ctdbd.max, s->reclock.ctdbd.num);
1466 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1467 "reclock_recd MIN/AVG/MAX",
1468 s->reclock.recd.min, LATENCY_AVG(s->reclock.recd),
1469 s->reclock.recd.max, s->reclock.recd.num);
1471 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1472 "call_latency MIN/AVG/MAX",
1473 s->call_latency.min, LATENCY_AVG(s->call_latency),
1474 s->call_latency.max, s->call_latency.num);
1476 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
1477 "childwrite_latency MIN/AVG/MAX",
1478 s->childwrite_latency.min,
1479 LATENCY_AVG(s->childwrite_latency),
1480 s->childwrite_latency.max, s->childwrite_latency.num);
1483 static int control_statistics(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1484 int argc, const char **argv)
1486 struct ctdb_statistics *stats;
1487 int ret;
1489 if (argc != 0) {
1490 usage("statistics");
1493 ret = ctdb_ctrl_statistics(mem_ctx, ctdb->ev, ctdb->client,
1494 ctdb->cmd_pnn, TIMEOUT(), &stats);
1495 if (ret != 0) {
1496 return ret;
1499 if (options.machinereadable) {
1500 print_statistics_machine(stats, true);
1501 } else {
1502 print_statistics(stats);
1505 return 0;
1508 static int control_statistics_reset(TALLOC_CTX *mem_ctx,
1509 struct ctdb_context *ctdb,
1510 int argc, const char **argv)
1512 int ret;
1514 if (argc != 0) {
1515 usage("statisticsreset");
1518 ret = ctdb_ctrl_statistics_reset(mem_ctx, ctdb->ev, ctdb->client,
1519 ctdb->cmd_pnn, TIMEOUT());
1520 if (ret != 0) {
1521 return ret;
1524 return 0;
1527 static int control_stats(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1528 int argc, const char **argv)
1530 struct ctdb_statistics_list *slist;
1531 int ret, count = 0, i;
1532 bool show_header = true;
1534 if (argc > 1) {
1535 usage("stats");
1538 if (argc == 1) {
1539 count = atoi(argv[0]);
1542 ret = ctdb_ctrl_get_stat_history(mem_ctx, ctdb->ev, ctdb->client,
1543 ctdb->cmd_pnn, TIMEOUT(), &slist);
1544 if (ret != 0) {
1545 return ret;
1548 for (i=0; i<slist->num; i++) {
1549 if (slist->stats[i].statistics_start_time.tv_sec == 0) {
1550 continue;
1552 if (options.machinereadable == 1) {
1553 print_statistics_machine(&slist->stats[i],
1554 show_header);
1555 show_header = false;
1556 } else {
1557 print_statistics(&slist->stats[i]);
1559 if (count > 0 && i == count) {
1560 break;
1564 return 0;
1567 static int ctdb_public_ip_cmp(const void *a, const void *b)
1569 const struct ctdb_public_ip *ip_a = a;
1570 const struct ctdb_public_ip *ip_b = b;
1572 return ctdb_sock_addr_cmp(&ip_a->addr, &ip_b->addr);
1575 static void print_ip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1576 struct ctdb_public_ip_list *ips,
1577 struct ctdb_public_ip_info **ipinfo,
1578 bool all_nodes)
1580 unsigned int i, j;
1581 char *conf, *avail, *active;
1583 if (options.machinereadable == 1) {
1584 printf("%s%s%s%s%s", options.sep,
1585 "Public IP", options.sep,
1586 "Node", options.sep);
1587 if (options.verbose == 1) {
1588 printf("%s%s%s%s%s%s\n",
1589 "ActiveInterfaces", options.sep,
1590 "AvailableInterfaces", options.sep,
1591 "ConfiguredInterfaces", options.sep);
1592 } else {
1593 printf("\n");
1595 } else {
1596 if (all_nodes) {
1597 printf("Public IPs on ALL nodes\n");
1598 } else {
1599 printf("Public IPs on node %u\n", ctdb->cmd_pnn);
1603 for (i = 0; i < ips->num; i++) {
1605 if (options.machinereadable == 1) {
1606 printf("%s%s%s%d%s", options.sep,
1607 ctdb_sock_addr_to_string(
1608 mem_ctx, &ips->ip[i].addr, false),
1609 options.sep,
1610 (int)ips->ip[i].pnn, options.sep);
1611 } else {
1612 printf("%s", ctdb_sock_addr_to_string(
1613 mem_ctx, &ips->ip[i].addr, false));
1616 if (options.verbose == 0) {
1617 if (options.machinereadable == 1) {
1618 printf("\n");
1619 } else {
1620 printf(" %d\n", (int)ips->ip[i].pnn);
1622 continue;
1625 conf = NULL;
1626 avail = NULL;
1627 active = NULL;
1629 if (ipinfo[i] == NULL) {
1630 goto skip_ipinfo;
1633 for (j=0; j<ipinfo[i]->ifaces->num; j++) {
1634 struct ctdb_iface *iface;
1636 iface = &ipinfo[i]->ifaces->iface[j];
1637 if (conf == NULL) {
1638 conf = talloc_strdup(mem_ctx, iface->name);
1639 } else {
1640 conf = talloc_asprintf_append(
1641 conf, ",%s", iface->name);
1644 if (ipinfo[i]->active_idx == j) {
1645 active = iface->name;
1648 if (iface->link_state == 0) {
1649 continue;
1652 if (avail == NULL) {
1653 avail = talloc_strdup(mem_ctx, iface->name);
1654 } else {
1655 avail = talloc_asprintf_append(
1656 avail, ",%s", iface->name);
1660 skip_ipinfo:
1662 if (options.machinereadable == 1) {
1663 printf("%s%s%s%s%s%s\n",
1664 active ? active : "", options.sep,
1665 avail ? avail : "", options.sep,
1666 conf ? conf : "", options.sep);
1667 } else {
1668 printf(" node[%d] active[%s] available[%s]"
1669 " configured[%s]\n",
1670 (int)ips->ip[i].pnn, active ? active : "",
1671 avail ? avail : "", conf ? conf : "");
1676 static int collect_ips(uint8_t *keybuf, size_t keylen, uint8_t *databuf,
1677 size_t datalen, void *private_data)
1679 struct ctdb_public_ip_list *ips = talloc_get_type_abort(
1680 private_data, struct ctdb_public_ip_list);
1681 struct ctdb_public_ip *ip;
1683 ip = (struct ctdb_public_ip *)databuf;
1684 ips->ip[ips->num] = *ip;
1685 ips->num += 1;
1687 return 0;
1690 static int get_all_public_ips(struct ctdb_context *ctdb, TALLOC_CTX *mem_ctx,
1691 struct ctdb_public_ip_list **out)
1693 struct ctdb_node_map *nodemap;
1694 struct ctdb_public_ip_list *ips;
1695 struct db_hash_context *ipdb;
1696 uint32_t *pnn_list;
1697 unsigned int j;
1698 int ret, count, i;
1700 nodemap = get_nodemap(ctdb, false);
1701 if (nodemap == NULL) {
1702 return 1;
1705 ret = db_hash_init(mem_ctx, "ips", 101, DB_HASH_COMPLEX, &ipdb);
1706 if (ret != 0) {
1707 goto failed;
1710 count = list_of_active_nodes(nodemap, CTDB_UNKNOWN_PNN, mem_ctx,
1711 &pnn_list);
1712 if (count <= 0) {
1713 goto failed;
1716 for (i=0; i<count; i++) {
1717 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
1718 pnn_list[i], TIMEOUT(),
1719 false, &ips);
1720 if (ret != 0) {
1721 goto failed;
1724 for (j=0; j<ips->num; j++) {
1725 struct ctdb_public_ip ip;
1727 ip.pnn = ips->ip[j].pnn;
1728 ip.addr = ips->ip[j].addr;
1730 if (pnn_list[i] == ip.pnn) {
1731 /* Node claims IP is hosted on it, so
1732 * save that information
1734 ret = db_hash_add(ipdb, (uint8_t *)&ip.addr,
1735 sizeof(ip.addr),
1736 (uint8_t *)&ip, sizeof(ip));
1737 if (ret != 0) {
1738 goto failed;
1740 } else {
1741 /* Node thinks IP is hosted elsewhere,
1742 * so overwrite with CTDB_UNKNOWN_PNN
1743 * if there's no existing entry
1745 ret = db_hash_exists(ipdb, (uint8_t *)&ip.addr,
1746 sizeof(ip.addr));
1747 if (ret == ENOENT) {
1748 ip.pnn = CTDB_UNKNOWN_PNN;
1749 ret = db_hash_add(ipdb,
1750 (uint8_t *)&ip.addr,
1751 sizeof(ip.addr),
1752 (uint8_t *)&ip,
1753 sizeof(ip));
1754 if (ret != 0) {
1755 goto failed;
1761 TALLOC_FREE(ips);
1764 talloc_free(pnn_list);
1766 ret = db_hash_traverse(ipdb, NULL, NULL, &count);
1767 if (ret != 0) {
1768 goto failed;
1771 ips = talloc_zero(mem_ctx, struct ctdb_public_ip_list);
1772 if (ips == NULL) {
1773 goto failed;
1776 ips->ip = talloc_array(ips, struct ctdb_public_ip, count);
1777 if (ips->ip == NULL) {
1778 goto failed;
1781 ret = db_hash_traverse(ipdb, collect_ips, ips, &count);
1782 if (ret != 0) {
1783 goto failed;
1786 if ((unsigned int)count != ips->num) {
1787 goto failed;
1790 talloc_free(ipdb);
1792 *out = ips;
1793 return 0;
1795 failed:
1796 talloc_free(ipdb);
1797 return 1;
1800 static int control_ip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1801 int argc, const char **argv)
1803 struct ctdb_public_ip_list *ips;
1804 struct ctdb_public_ip_info **ipinfo;
1805 unsigned int i;
1806 int ret;
1807 bool do_all = false;
1809 if (argc > 1) {
1810 usage("ip");
1813 if (argc == 1) {
1814 if (strcmp(argv[0], "all") == 0) {
1815 do_all = true;
1816 } else {
1817 usage("ip");
1821 if (do_all) {
1822 ret = get_all_public_ips(ctdb, mem_ctx, &ips);
1823 } else {
1824 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
1825 ctdb->cmd_pnn, TIMEOUT(),
1826 false, &ips);
1828 if (ret != 0) {
1829 return ret;
1832 qsort(ips->ip, ips->num, sizeof(struct ctdb_public_ip),
1833 ctdb_public_ip_cmp);
1835 ipinfo = talloc_array(mem_ctx, struct ctdb_public_ip_info *, ips->num);
1836 if (ipinfo == NULL) {
1837 return 1;
1840 for (i=0; i<ips->num; i++) {
1841 uint32_t pnn;
1842 if (do_all) {
1843 pnn = ips->ip[i].pnn;
1844 } else {
1845 pnn = ctdb->cmd_pnn;
1847 if (pnn == CTDB_UNKNOWN_PNN) {
1848 ipinfo[i] = NULL;
1849 continue;
1851 ret = ctdb_ctrl_get_public_ip_info(mem_ctx, ctdb->ev,
1852 ctdb->client, pnn,
1853 TIMEOUT(), &ips->ip[i].addr,
1854 &ipinfo[i]);
1855 if (ret != 0) {
1856 return ret;
1860 print_ip(mem_ctx, ctdb, ips, ipinfo, do_all);
1861 return 0;
1864 static int control_ipinfo(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1865 int argc, const char **argv)
1867 struct ctdb_public_ip_info *ipinfo;
1868 ctdb_sock_addr addr;
1869 unsigned int i;
1870 int ret;
1872 if (argc != 1) {
1873 usage("ipinfo");
1876 ret = ctdb_sock_addr_from_string(argv[0], &addr, false);
1877 if (ret != 0) {
1878 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
1879 return 1;
1882 ret = ctdb_ctrl_get_public_ip_info(mem_ctx, ctdb->ev, ctdb->client,
1883 ctdb->cmd_pnn, TIMEOUT(), &addr,
1884 &ipinfo);
1885 if (ret != 0) {
1886 if (ret == -1) {
1887 printf("Node %u does not know about IP %s\n",
1888 ctdb->cmd_pnn, argv[0]);
1890 return ret;
1893 printf("Public IP[%s] info on node %u\n",
1894 ctdb_sock_addr_to_string(mem_ctx, &ipinfo->ip.addr, false),
1895 ctdb->cmd_pnn);
1897 printf("IP:%s\nCurrentNode:%u\nNumInterfaces:%u\n",
1898 ctdb_sock_addr_to_string(mem_ctx, &ipinfo->ip.addr, false),
1899 ipinfo->ip.pnn, ipinfo->ifaces->num);
1901 for (i=0; i<ipinfo->ifaces->num; i++) {
1902 struct ctdb_iface *iface;
1904 iface = &ipinfo->ifaces->iface[i];
1905 iface->name[CTDB_IFACE_SIZE] = '\0';
1906 printf("Interface[%u]: Name:%s Link:%s References:%u%s\n",
1907 i+1, iface->name,
1908 iface->link_state == 0 ? "down" : "up",
1909 iface->references,
1910 (i == ipinfo->active_idx) ? " (active)" : "");
1913 return 0;
1916 static int control_ifaces(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1917 int argc, const char **argv)
1919 struct ctdb_iface_list *ifaces;
1920 unsigned int i;
1921 int ret;
1923 if (argc != 0) {
1924 usage("ifaces");
1927 ret = ctdb_ctrl_get_ifaces(mem_ctx, ctdb->ev, ctdb->client,
1928 ctdb->cmd_pnn, TIMEOUT(), &ifaces);
1929 if (ret != 0) {
1930 return ret;
1933 if (ifaces->num == 0) {
1934 printf("No interfaces configured on node %u\n",
1935 ctdb->cmd_pnn);
1936 return 0;
1939 if (options.machinereadable) {
1940 printf("%s%s%s%s%s%s%s\n", options.sep,
1941 "Name", options.sep,
1942 "LinkStatus", options.sep,
1943 "References", options.sep);
1944 } else {
1945 printf("Interfaces on node %u\n", ctdb->cmd_pnn);
1948 for (i=0; i<ifaces->num; i++) {
1949 if (options.machinereadable) {
1950 printf("%s%s%s%u%s%u%s\n", options.sep,
1951 ifaces->iface[i].name, options.sep,
1952 ifaces->iface[i].link_state, options.sep,
1953 ifaces->iface[i].references, options.sep);
1954 } else {
1955 printf("name:%s link:%s references:%u\n",
1956 ifaces->iface[i].name,
1957 ifaces->iface[i].link_state ? "up" : "down",
1958 ifaces->iface[i].references);
1962 return 0;
1965 static int control_setifacelink(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
1966 int argc, const char **argv)
1968 struct ctdb_iface_list *ifaces;
1969 struct ctdb_iface *iface;
1970 unsigned int i;
1971 int ret;
1973 if (argc != 2) {
1974 usage("setifacelink");
1977 if (strlen(argv[0]) > CTDB_IFACE_SIZE) {
1978 fprintf(stderr, "Interface name '%s' too long\n", argv[0]);
1979 return 1;
1982 ret = ctdb_ctrl_get_ifaces(mem_ctx, ctdb->ev, ctdb->client,
1983 ctdb->cmd_pnn, TIMEOUT(), &ifaces);
1984 if (ret != 0) {
1985 fprintf(stderr,
1986 "Failed to get interface information from node %u\n",
1987 ctdb->cmd_pnn);
1988 return ret;
1991 iface = NULL;
1992 for (i=0; i<ifaces->num; i++) {
1993 if (strcmp(ifaces->iface[i].name, argv[0]) == 0) {
1994 iface = &ifaces->iface[i];
1995 break;
1999 if (iface == NULL) {
2000 printf("Interface %s not configured on node %u\n",
2001 argv[0], ctdb->cmd_pnn);
2002 return 1;
2005 if (strcmp(argv[1], "up") == 0) {
2006 iface->link_state = 1;
2007 } else if (strcmp(argv[1], "down") == 0) {
2008 iface->link_state = 0;
2009 } else {
2010 usage("setifacelink");
2011 return 1;
2014 iface->references = 0;
2016 ret = ctdb_ctrl_set_iface_link_state(mem_ctx, ctdb->ev, ctdb->client,
2017 ctdb->cmd_pnn, TIMEOUT(), iface);
2018 if (ret != 0) {
2019 return ret;
2022 return 0;
2025 static int control_process_exists(TALLOC_CTX *mem_ctx,
2026 struct ctdb_context *ctdb,
2027 int argc, const char **argv)
2029 pid_t pid;
2030 uint64_t srvid = 0;
2031 int status;
2032 int ret = 0;
2034 if (argc != 1 && argc != 2) {
2035 usage("process-exists");
2038 pid = atoi(argv[0]);
2039 if (argc == 2) {
2040 srvid = smb_strtoull(argv[1], NULL, 0, &ret, SMB_STR_STANDARD);
2041 if (ret != 0) {
2042 return ret;
2046 if (srvid == 0) {
2047 ret = ctdb_ctrl_process_exists(mem_ctx, ctdb->ev, ctdb->client,
2048 ctdb->cmd_pnn, TIMEOUT(), pid, &status);
2049 } else {
2050 struct ctdb_pid_srvid pid_srvid;
2052 pid_srvid.pid = pid;
2053 pid_srvid.srvid = srvid;
2055 ret = ctdb_ctrl_check_pid_srvid(mem_ctx, ctdb->ev,
2056 ctdb->client, ctdb->cmd_pnn,
2057 TIMEOUT(), &pid_srvid,
2058 &status);
2061 if (ret != 0) {
2062 return ret;
2065 if (srvid == 0) {
2066 printf("PID %d %s\n", pid,
2067 (status == 0 ? "exists" : "does not exist"));
2068 } else {
2069 printf("PID %d with SRVID 0x%"PRIx64" %s\n", pid, srvid,
2070 (status == 0 ? "exists" : "does not exist"));
2072 return status;
2075 static int control_getdbmap(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2076 int argc, const char **argv)
2078 struct ctdb_dbid_map *dbmap;
2079 unsigned int i;
2080 int ret;
2082 if (argc != 0) {
2083 usage("getdbmap");
2086 ret = ctdb_ctrl_get_dbmap(mem_ctx, ctdb->ev, ctdb->client,
2087 ctdb->cmd_pnn, TIMEOUT(), &dbmap);
2088 if (ret != 0) {
2089 return ret;
2092 if (options.machinereadable == 1) {
2093 printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
2094 options.sep,
2095 "ID", options.sep,
2096 "Name", options.sep,
2097 "Path", options.sep,
2098 "Persistent", options.sep,
2099 "Sticky", options.sep,
2100 "Unhealthy", options.sep,
2101 "Readonly", options.sep,
2102 "Replicated", options.sep);
2103 } else {
2104 printf("Number of databases:%d\n", dbmap->num);
2107 for (i=0; i<dbmap->num; i++) {
2108 const char *name;
2109 const char *path;
2110 const char *health;
2111 bool persistent;
2112 bool readonly;
2113 bool sticky;
2114 bool replicated;
2115 uint32_t db_id;
2117 db_id = dbmap->dbs[i].db_id;
2119 ret = ctdb_ctrl_get_dbname(mem_ctx, ctdb->ev, ctdb->client,
2120 ctdb->cmd_pnn, TIMEOUT(), db_id,
2121 &name);
2122 if (ret != 0) {
2123 return ret;
2126 ret = ctdb_ctrl_getdbpath(mem_ctx, ctdb->ev, ctdb->client,
2127 ctdb->cmd_pnn, TIMEOUT(), db_id,
2128 &path);
2129 if (ret != 0) {
2130 return ret;
2133 ret = ctdb_ctrl_db_get_health(mem_ctx, ctdb->ev, ctdb->client,
2134 ctdb->cmd_pnn, TIMEOUT(), db_id,
2135 &health);
2136 if (ret != 0) {
2137 return ret;
2140 persistent = dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT;
2141 readonly = dbmap->dbs[i].flags & CTDB_DB_FLAGS_READONLY;
2142 sticky = dbmap->dbs[i].flags & CTDB_DB_FLAGS_STICKY;
2143 replicated = dbmap->dbs[i].flags & CTDB_DB_FLAGS_REPLICATED;
2145 if (options.machinereadable == 1) {
2146 printf("%s0x%08X%s%s%s%s%s%d%s%d%s%d%s%d%s%d%s\n",
2147 options.sep,
2148 db_id, options.sep,
2149 name, options.sep,
2150 path, options.sep,
2151 !! (persistent), options.sep,
2152 !! (sticky), options.sep,
2153 !! (health), options.sep,
2154 !! (readonly), options.sep,
2155 !! (replicated), options.sep);
2156 } else {
2157 printf("dbid:0x%08x name:%s path:%s%s%s%s%s%s\n",
2158 db_id, name, path,
2159 persistent ? " PERSISTENT" : "",
2160 sticky ? " STICKY" : "",
2161 readonly ? " READONLY" : "",
2162 replicated ? " REPLICATED" : "",
2163 health ? " UNHEALTHY" : "");
2166 talloc_free(discard_const(name));
2167 talloc_free(discard_const(path));
2168 talloc_free(discard_const(health));
2171 return 0;
2174 static int control_getdbstatus(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2175 int argc, const char **argv)
2177 uint32_t db_id;
2178 const char *db_name, *db_path, *db_health;
2179 uint8_t db_flags;
2180 int ret;
2182 if (argc != 1) {
2183 usage("getdbstatus");
2186 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
2187 return 1;
2190 ret = ctdb_ctrl_getdbpath(mem_ctx, ctdb->ev, ctdb->client,
2191 ctdb->cmd_pnn, TIMEOUT(), db_id,
2192 &db_path);
2193 if (ret != 0) {
2194 return ret;
2197 ret = ctdb_ctrl_db_get_health(mem_ctx, ctdb->ev, ctdb->client,
2198 ctdb->cmd_pnn, TIMEOUT(), db_id,
2199 &db_health);
2200 if (ret != 0) {
2201 return ret;
2204 printf("dbid: 0x%08x\nname: %s\npath: %s\n", db_id, db_name, db_path);
2205 printf("PERSISTENT: %s\nREPLICATED: %s\nSTICKY: %s\nREADONLY: %s\n",
2206 (db_flags & CTDB_DB_FLAGS_PERSISTENT ? "yes" : "no"),
2207 (db_flags & CTDB_DB_FLAGS_REPLICATED ? "yes" : "no"),
2208 (db_flags & CTDB_DB_FLAGS_STICKY ? "yes" : "no"),
2209 (db_flags & CTDB_DB_FLAGS_READONLY ? "yes" : "no"));
2210 printf("HEALTH: %s\n", (db_health ? db_health : "OK"));
2211 return 0;
2214 struct dump_record_state {
2215 uint32_t count;
2218 #define ISASCII(x) (isprint(x) && ! strchr("\"\\", (x)))
2220 static void dump_tdb_data(const char *name, TDB_DATA val)
2222 size_t i;
2224 fprintf(stdout, "%s(%zu) = \"", name, val.dsize);
2225 for (i=0; i<val.dsize; i++) {
2226 if (ISASCII(val.dptr[i])) {
2227 fprintf(stdout, "%c", val.dptr[i]);
2228 } else {
2229 fprintf(stdout, "\\%02X", val.dptr[i]);
2232 fprintf(stdout, "\"\n");
2235 static void dump_ltdb_header(struct ctdb_ltdb_header *header)
2237 fprintf(stdout, "dmaster: %u\n", header->dmaster);
2238 fprintf(stdout, "rsn: %" PRIu64 "\n", header->rsn);
2239 fprintf(stdout, "flags: 0x%08x", header->flags);
2240 if (header->flags & CTDB_REC_FLAG_MIGRATED_WITH_DATA) {
2241 fprintf(stdout, " MIGRATED_WITH_DATA");
2243 if (header->flags & CTDB_REC_FLAG_VACUUM_MIGRATED) {
2244 fprintf(stdout, " VACUUM_MIGRATED");
2246 if (header->flags & CTDB_REC_FLAG_AUTOMATIC) {
2247 fprintf(stdout, " AUTOMATIC");
2249 if (header->flags & CTDB_REC_RO_HAVE_DELEGATIONS) {
2250 fprintf(stdout, " RO_HAVE_DELEGATIONS");
2252 if (header->flags & CTDB_REC_RO_HAVE_READONLY) {
2253 fprintf(stdout, " RO_HAVE_READONLY");
2255 if (header->flags & CTDB_REC_RO_REVOKING_READONLY) {
2256 fprintf(stdout, " RO_REVOKING_READONLY");
2258 if (header->flags & CTDB_REC_RO_REVOKE_COMPLETE) {
2259 fprintf(stdout, " RO_REVOKE_COMPLETE");
2261 fprintf(stdout, "\n");
2265 static int dump_record(uint32_t reqid, struct ctdb_ltdb_header *header,
2266 TDB_DATA key, TDB_DATA data, void *private_data)
2268 struct dump_record_state *state =
2269 (struct dump_record_state *)private_data;
2271 state->count += 1;
2273 dump_tdb_data("key", key);
2274 dump_ltdb_header(header);
2275 dump_tdb_data("data", data);
2276 fprintf(stdout, "\n");
2278 return 0;
2281 static int control_catdb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2282 int argc, const char **argv)
2284 struct ctdb_db_context *db;
2285 const char *db_name;
2286 uint32_t db_id;
2287 uint8_t db_flags;
2288 struct dump_record_state state;
2289 int ret;
2291 if (argc != 1) {
2292 usage("catdb");
2295 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
2296 return 1;
2299 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
2300 db_flags, &db);
2301 if (ret != 0) {
2302 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
2303 return ret;
2306 state.count = 0;
2308 ret = ctdb_db_traverse(mem_ctx, ctdb->ev, ctdb->client, db,
2309 ctdb->cmd_pnn, TIMEOUT(),
2310 dump_record, &state);
2312 printf("Dumped %u records\n", state.count);
2314 return ret;
2317 static int control_cattdb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2318 int argc, const char **argv)
2320 struct ctdb_db_context *db;
2321 const char *db_name;
2322 uint32_t db_id;
2323 uint8_t db_flags;
2324 struct dump_record_state state;
2325 int ret;
2327 if (argc != 1) {
2328 usage("cattdb");
2331 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
2332 return 1;
2335 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
2336 db_flags, &db);
2337 if (ret != 0) {
2338 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
2339 return ret;
2342 state.count = 0;
2343 ret = ctdb_db_traverse_local(db, true, true, dump_record, &state);
2345 printf("Dumped %u record(s)\n", state.count);
2347 return ret;
2350 static int control_getcapabilities(TALLOC_CTX *mem_ctx,
2351 struct ctdb_context *ctdb,
2352 int argc, const char **argv)
2354 uint32_t caps;
2355 int ret;
2357 if (argc != 0) {
2358 usage("getcapabilities");
2361 ret = ctdb_ctrl_get_capabilities(mem_ctx, ctdb->ev, ctdb->client,
2362 ctdb->cmd_pnn, TIMEOUT(), &caps);
2363 if (ret != 0) {
2364 return ret;
2367 if (options.machinereadable == 1) {
2368 printf("%s%s%s%s%s\n",
2369 options.sep,
2370 "LEADER", options.sep,
2371 "LMASTER", options.sep);
2372 printf("%s%d%s%d%s\n", options.sep,
2373 !! (caps & CTDB_CAP_RECMASTER), options.sep,
2374 !! (caps & CTDB_CAP_LMASTER), options.sep);
2375 } else {
2376 printf("LEADER: %s\n",
2377 (caps & CTDB_CAP_RECMASTER) ? "YES" : "NO");
2378 printf("LMASTER: %s\n",
2379 (caps & CTDB_CAP_LMASTER) ? "YES" : "NO");
2382 return 0;
2385 static int control_pnn(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2386 int argc, const char **argv)
2388 printf("%u\n", ctdb_client_pnn(ctdb->client));
2389 return 0;
2392 static int control_lvs(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2393 int argc, const char **argv)
2395 char *t, *lvs_helper = NULL;
2397 if (argc != 1) {
2398 usage("lvs");
2401 t = getenv("CTDB_LVS_HELPER");
2402 if (t != NULL) {
2403 lvs_helper = talloc_strdup(mem_ctx, t);
2404 } else {
2405 lvs_helper = talloc_asprintf(mem_ctx, "%s/ctdb_lvs",
2406 CTDB_HELPER_BINDIR);
2409 if (lvs_helper == NULL) {
2410 fprintf(stderr, "Unable to set LVS helper\n");
2411 return 1;
2414 return run_helper(mem_ctx, "LVS helper", lvs_helper, argc, argv);
2417 static int control_setdebug(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2418 int argc, const char **argv)
2420 int log_level;
2421 int ret;
2422 bool found;
2424 if (argc != 1) {
2425 usage("setdebug");
2428 found = debug_level_parse(argv[0], &log_level);
2429 if (! found) {
2430 fprintf(stderr,
2431 "Invalid debug level '%s'. Valid levels are:\n",
2432 argv[0]);
2433 fprintf(stderr, "\tERROR | WARNING | NOTICE | INFO | DEBUG\n");
2434 return 1;
2437 ret = ctdb_ctrl_setdebug(mem_ctx, ctdb->ev, ctdb->client,
2438 ctdb->cmd_pnn, TIMEOUT(), log_level);
2439 if (ret != 0) {
2440 return ret;
2443 return 0;
2446 static int control_getdebug(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2447 int argc, const char **argv)
2449 int loglevel;
2450 const char *log_str;
2451 int ret;
2453 if (argc != 0) {
2454 usage("getdebug");
2457 ret = ctdb_ctrl_getdebug(mem_ctx, ctdb->ev, ctdb->client,
2458 ctdb->cmd_pnn, TIMEOUT(), &loglevel);
2459 if (ret != 0) {
2460 return ret;
2463 log_str = debug_level_to_string(loglevel);
2464 printf("%s\n", log_str);
2466 return 0;
2469 static int control_attach(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2470 int argc, const char **argv)
2472 const char *db_name;
2473 uint8_t db_flags = 0;
2474 int ret;
2476 if (argc < 1 || argc > 2) {
2477 usage("attach");
2480 db_name = argv[0];
2481 if (argc == 2) {
2482 if (strcmp(argv[1], "persistent") == 0) {
2483 db_flags = CTDB_DB_FLAGS_PERSISTENT;
2484 } else if (strcmp(argv[1], "readonly") == 0) {
2485 db_flags = CTDB_DB_FLAGS_READONLY;
2486 } else if (strcmp(argv[1], "sticky") == 0) {
2487 db_flags = CTDB_DB_FLAGS_STICKY;
2488 } else if (strcmp(argv[1], "replicated") == 0) {
2489 db_flags = CTDB_DB_FLAGS_REPLICATED;
2490 } else {
2491 usage("attach");
2495 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
2496 db_flags, NULL);
2497 if (ret != 0) {
2498 return ret;
2501 return 0;
2504 static int control_detach(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2505 int argc, const char **argv)
2507 const char *db_name;
2508 uint32_t db_id;
2509 uint8_t db_flags;
2510 struct ctdb_node_map *nodemap;
2511 int recmode;
2512 unsigned int j;
2513 int ret, ret2, i;
2515 if (argc < 1) {
2516 usage("detach");
2519 ret = ctdb_ctrl_get_recmode(mem_ctx, ctdb->ev, ctdb->client,
2520 ctdb->cmd_pnn, TIMEOUT(), &recmode);
2521 if (ret != 0) {
2522 return ret;
2525 if (recmode == CTDB_RECOVERY_ACTIVE) {
2526 fprintf(stderr, "Database cannot be detached"
2527 " when recovery is active\n");
2528 return 1;
2531 nodemap = get_nodemap(ctdb, false);
2532 if (nodemap == NULL) {
2533 return 1;
2536 for (j=0; j<nodemap->num; j++) {
2537 uint32_t value;
2539 if (nodemap->node[j].flags & NODE_FLAGS_DISCONNECTED) {
2540 continue;
2542 if (nodemap->node[j].flags & NODE_FLAGS_DELETED) {
2543 continue;
2545 if (nodemap->node[j].flags & NODE_FLAGS_INACTIVE) {
2546 fprintf(stderr, "Database cannot be detached on"
2547 " inactive (stopped or banned) node %u\n",
2548 nodemap->node[j].pnn);
2549 return 1;
2552 ret = ctdb_ctrl_get_tunable(mem_ctx, ctdb->ev, ctdb->client,
2553 nodemap->node[j].pnn, TIMEOUT(),
2554 "AllowClientDBAttach", &value);
2555 if (ret != 0) {
2556 fprintf(stderr,
2557 "Unable to get tunable AllowClientDBAttach"
2558 " from node %u\n", nodemap->node[j].pnn);
2559 return ret;
2562 if (value == 1) {
2563 fprintf(stderr,
2564 "Database access is still active on node %u."
2565 " Set AllowclientDBAttach=0 on all nodes.\n",
2566 nodemap->node[j].pnn);
2567 return 1;
2571 ret2 = 0;
2572 for (i=0; i<argc; i++) {
2573 if (! db_exists(mem_ctx, ctdb, argv[i], &db_id, &db_name,
2574 &db_flags)) {
2575 continue;
2578 if (db_flags &
2579 (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
2580 fprintf(stderr,
2581 "Only volatile databases can be detached\n");
2582 return 1;
2585 ret = ctdb_detach(ctdb->ev, ctdb->client, TIMEOUT(), db_id);
2586 if (ret != 0) {
2587 fprintf(stderr, "Database %s detach failed\n", db_name);
2588 ret2 = ret;
2592 return ret2;
2595 static int control_dumpmemory(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2596 int argc, const char **argv)
2598 const char *mem_str;
2599 ssize_t n;
2600 int ret;
2602 ret = ctdb_ctrl_dump_memory(mem_ctx, ctdb->ev, ctdb->client,
2603 ctdb->cmd_pnn, TIMEOUT(), &mem_str);
2604 if (ret != 0) {
2605 return ret;
2608 n = write(1, mem_str, strlen(mem_str));
2609 if (n < 0 || (size_t)n != strlen(mem_str)) {
2610 fprintf(stderr, "Failed to write talloc summary\n");
2611 return 1;
2614 return 0;
2617 static void dump_memory(uint64_t srvid, TDB_DATA data, void *private_data)
2619 bool *done = (bool *)private_data;
2620 size_t len;
2621 ssize_t n;
2623 len = strnlen((const char *)data.dptr, data.dsize);
2624 n = write(1, data.dptr, len);
2625 if (n < 0 || (size_t)n != len) {
2626 fprintf(stderr, "Failed to write talloc summary\n");
2629 *done = true;
2632 static int control_rddumpmemory(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2633 int argc, const char **argv)
2635 struct ctdb_srvid_message msg = { 0 };
2636 int ret;
2637 bool done = false;
2639 msg.pnn = ctdb->pnn;
2640 msg.srvid = next_srvid(ctdb);
2642 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
2643 msg.srvid, dump_memory, &done);
2644 if (ret != 0) {
2645 return ret;
2648 ret = ctdb_message_mem_dump(mem_ctx, ctdb->ev, ctdb->client,
2649 ctdb->cmd_pnn, &msg);
2650 if (ret != 0) {
2651 return ret;
2654 ctdb_client_wait(ctdb->ev, &done);
2655 return 0;
2658 static int control_getpid(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2659 int argc, const char **argv)
2661 pid_t pid;
2662 int ret;
2664 ret = ctdb_ctrl_get_pid(mem_ctx, ctdb->ev, ctdb->client,
2665 ctdb->cmd_pnn, TIMEOUT(), &pid);
2666 if (ret != 0) {
2667 return ret;
2670 printf("%u\n", pid);
2671 return 0;
2674 static int check_flags(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2675 const char *desc, uint32_t flag, bool set_flag)
2677 struct ctdb_node_map *nodemap;
2678 bool flag_is_set;
2680 nodemap = get_nodemap(ctdb, false);
2681 if (nodemap == NULL) {
2682 return 1;
2685 flag_is_set = nodemap->node[ctdb->cmd_pnn].flags & flag;
2686 if (set_flag == flag_is_set) {
2687 if (set_flag) {
2688 fprintf(stderr, "Node %u is already %s\n",
2689 ctdb->cmd_pnn, desc);
2690 } else {
2691 fprintf(stderr, "Node %u is not %s\n",
2692 ctdb->cmd_pnn, desc);
2694 return 0;
2697 return 1;
2700 static void wait_for_flags(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2701 uint32_t flag, bool set_flag)
2703 struct ctdb_node_map *nodemap;
2704 bool flag_is_set;
2706 while (1) {
2707 nodemap = get_nodemap(ctdb, true);
2708 if (nodemap == NULL) {
2709 fprintf(stderr,
2710 "Failed to get nodemap, trying again\n");
2711 sleep(1);
2712 continue;
2715 flag_is_set = nodemap->node[ctdb->cmd_pnn].flags & flag;
2716 if (flag_is_set == set_flag) {
2717 break;
2720 sleep(1);
2724 struct ipreallocate_state {
2725 int status;
2726 bool done;
2729 static void ipreallocate_handler(uint64_t srvid, TDB_DATA data,
2730 void *private_data)
2732 struct ipreallocate_state *state =
2733 (struct ipreallocate_state *)private_data;
2735 if (data.dsize != sizeof(int)) {
2736 /* Ignore packet */
2737 return;
2740 state->status = *(int *)data.dptr;
2741 state->done = true;
2744 static int ipreallocate(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb)
2746 struct ctdb_srvid_message msg = { 0 };
2747 struct ipreallocate_state state;
2748 int ret;
2750 msg.pnn = ctdb->pnn;
2751 msg.srvid = next_srvid(ctdb);
2753 state.done = false;
2754 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
2755 msg.srvid,
2756 ipreallocate_handler, &state);
2757 if (ret != 0) {
2758 return ret;
2761 while (true) {
2762 ret = ctdb_message_takeover_run(mem_ctx, ctdb->ev,
2763 ctdb->client,
2764 CTDB_BROADCAST_CONNECTED,
2765 &msg);
2766 if (ret != 0) {
2767 goto fail;
2770 ret = ctdb_client_wait_timeout(ctdb->ev, &state.done,
2771 TIMEOUT());
2772 if (ret != 0) {
2773 continue;
2776 if (state.status >= 0) {
2777 ret = 0;
2778 } else {
2779 ret = state.status;
2781 break;
2784 fail:
2785 ctdb_client_remove_message_handler(ctdb->ev, ctdb->client,
2786 msg.srvid, &state);
2787 return ret;
2790 static int control_disable(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2791 int argc, const char **argv)
2793 int ret;
2795 if (argc != 0) {
2796 usage("disable");
2799 ret = check_flags(mem_ctx, ctdb, "disabled",
2800 NODE_FLAGS_PERMANENTLY_DISABLED, true);
2801 if (ret == 0) {
2802 return 0;
2805 ret = ctdb_ctrl_disable_node(mem_ctx,
2806 ctdb->ev,
2807 ctdb->client,
2808 ctdb->cmd_pnn,
2809 TIMEOUT());
2810 if (ret != 0) {
2811 fprintf(stderr, "Failed to disable node %u\n", ctdb->cmd_pnn);
2812 return ret;
2815 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_PERMANENTLY_DISABLED, true);
2816 return ipreallocate(mem_ctx, ctdb);
2819 static int control_enable(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2820 int argc, const char **argv)
2822 int ret;
2824 if (argc != 0) {
2825 usage("enable");
2828 ret = check_flags(mem_ctx, ctdb, "disabled",
2829 NODE_FLAGS_PERMANENTLY_DISABLED, false);
2830 if (ret == 0) {
2831 return 0;
2834 ret = ctdb_ctrl_enable_node(mem_ctx,
2835 ctdb->ev,
2836 ctdb->client,
2837 ctdb->cmd_pnn,
2838 TIMEOUT());
2839 if (ret != 0) {
2840 fprintf(stderr, "Failed to enable node %u\n", ctdb->cmd_pnn);
2841 return ret;
2844 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_PERMANENTLY_DISABLED, false);
2845 return ipreallocate(mem_ctx, ctdb);
2848 static int control_stop(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2849 int argc, const char **argv)
2851 int ret;
2853 if (argc != 0) {
2854 usage("stop");
2857 ret = check_flags(mem_ctx, ctdb, "stopped",
2858 NODE_FLAGS_STOPPED, true);
2859 if (ret == 0) {
2860 return 0;
2863 ret = ctdb_ctrl_stop_node(mem_ctx, ctdb->ev, ctdb->client,
2864 ctdb->cmd_pnn, TIMEOUT());
2865 if (ret != 0) {
2866 fprintf(stderr, "Failed to stop node %u\n", ctdb->cmd_pnn);
2867 return ret;
2870 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_STOPPED, true);
2871 return ipreallocate(mem_ctx, ctdb);
2874 static int control_continue(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2875 int argc, const char **argv)
2877 int ret;
2879 if (argc != 0) {
2880 usage("continue");
2883 ret = check_flags(mem_ctx, ctdb, "stopped",
2884 NODE_FLAGS_STOPPED, false);
2885 if (ret == 0) {
2886 return 0;
2889 ret = ctdb_ctrl_continue_node(mem_ctx, ctdb->ev, ctdb->client,
2890 ctdb->cmd_pnn, TIMEOUT());
2891 if (ret != 0) {
2892 fprintf(stderr, "Failed to continue stopped node %u\n",
2893 ctdb->cmd_pnn);
2894 return ret;
2897 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_STOPPED, false);
2898 return ipreallocate(mem_ctx, ctdb);
2901 static int control_ban(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2902 int argc, const char **argv)
2904 struct ctdb_ban_state ban_state;
2905 int ret = 0;
2907 if (argc != 1) {
2908 usage("ban");
2911 ret = check_flags(mem_ctx, ctdb, "banned",
2912 NODE_FLAGS_BANNED, true);
2913 if (ret == 0) {
2914 return 0;
2917 ban_state.pnn = ctdb->cmd_pnn;
2918 ban_state.time = smb_strtoul(argv[0], NULL, 0, &ret, SMB_STR_STANDARD);
2919 if (ret != 0) {
2920 return ret;
2923 if (ban_state.time == 0) {
2924 fprintf(stderr, "Ban time cannot be zero\n");
2925 return EINVAL;
2928 ret = ctdb_ctrl_set_ban_state(mem_ctx, ctdb->ev, ctdb->client,
2929 ctdb->cmd_pnn, TIMEOUT(), &ban_state);
2930 if (ret != 0) {
2931 fprintf(stderr, "Failed to ban node %u\n", ctdb->cmd_pnn);
2932 return ret;
2935 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_BANNED, true);
2936 return ipreallocate(mem_ctx, ctdb);
2940 static int control_unban(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2941 int argc, const char **argv)
2943 struct ctdb_ban_state ban_state;
2944 int ret;
2946 if (argc != 0) {
2947 usage("unban");
2950 ret = check_flags(mem_ctx, ctdb, "banned",
2951 NODE_FLAGS_BANNED, false);
2952 if (ret == 0) {
2953 return 0;
2956 ban_state.pnn = ctdb->cmd_pnn;
2957 ban_state.time = 0;
2959 ret = ctdb_ctrl_set_ban_state(mem_ctx, ctdb->ev, ctdb->client,
2960 ctdb->cmd_pnn, TIMEOUT(), &ban_state);
2961 if (ret != 0) {
2962 fprintf(stderr, "Failed to unban node %u\n", ctdb->cmd_pnn);
2963 return ret;
2966 wait_for_flags(mem_ctx, ctdb, NODE_FLAGS_BANNED, false);
2967 return ipreallocate(mem_ctx, ctdb);
2971 static void wait_for_shutdown(void *private_data)
2973 bool *done = (bool *)private_data;
2975 *done = true;
2978 static int control_shutdown(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
2979 int argc, const char **argv)
2981 int ret;
2982 bool done = false;
2984 if (argc != 0) {
2985 usage("shutdown");
2988 if (ctdb->pnn == ctdb->cmd_pnn) {
2989 ctdb_client_set_disconnect_callback(ctdb->client,
2990 wait_for_shutdown,
2991 &done);
2994 ret = ctdb_ctrl_shutdown(mem_ctx, ctdb->ev, ctdb->client,
2995 ctdb->cmd_pnn, TIMEOUT());
2996 if (ret != 0) {
2997 fprintf(stderr, "Unable to shutdown node %u\n", ctdb->cmd_pnn);
2998 return ret;
3001 if (ctdb->pnn == ctdb->cmd_pnn) {
3002 ctdb_client_wait(ctdb->ev, &done);
3005 return 0;
3008 static int get_generation(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3009 uint32_t *generation)
3011 uint32_t leader;
3012 int recmode;
3013 struct ctdb_vnn_map *vnnmap;
3014 int ret;
3016 again:
3017 ret = get_leader(mem_ctx, ctdb, &leader);
3018 if (ret != 0) {
3019 fprintf(stderr, "Failed to find leader\n");
3020 return ret;
3023 ret = ctdb_ctrl_get_recmode(mem_ctx,
3024 ctdb->ev,
3025 ctdb->client,
3026 leader,
3027 TIMEOUT(),
3028 &recmode);
3029 if (ret != 0) {
3030 fprintf(stderr,
3031 "Failed to get recovery mode from node %u\n",
3032 leader);
3033 return ret;
3036 if (recmode == CTDB_RECOVERY_ACTIVE) {
3037 sleep(1);
3038 goto again;
3041 ret = ctdb_ctrl_getvnnmap(mem_ctx,
3042 ctdb->ev,
3043 ctdb->client,
3044 leader,
3045 TIMEOUT(),
3046 &vnnmap);
3047 if (ret != 0) {
3048 fprintf(stderr,
3049 "Failed to get generation from node %u\n",
3050 leader);
3051 return ret;
3054 if (vnnmap->generation == INVALID_GENERATION) {
3055 talloc_free(vnnmap);
3056 sleep(1);
3057 goto again;
3060 *generation = vnnmap->generation;
3061 talloc_free(vnnmap);
3062 return 0;
3066 static int control_recover(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3067 int argc, const char **argv)
3069 uint32_t generation, next_generation;
3070 int ret;
3072 if (argc != 0) {
3073 usage("recover");
3076 ret = get_generation(mem_ctx, ctdb, &generation);
3077 if (ret != 0) {
3078 return ret;
3081 ret = ctdb_ctrl_set_recmode(mem_ctx, ctdb->ev, ctdb->client,
3082 ctdb->cmd_pnn, TIMEOUT(),
3083 CTDB_RECOVERY_ACTIVE);
3084 if (ret != 0) {
3085 fprintf(stderr, "Failed to set recovery mode active\n");
3086 return ret;
3089 while (1) {
3090 ret = get_generation(mem_ctx, ctdb, &next_generation);
3091 if (ret != 0) {
3092 fprintf(stderr,
3093 "Failed to confirm end of recovery\n");
3094 return ret;
3097 if (next_generation != generation) {
3098 break;
3101 sleep (1);
3104 return 0;
3107 static int control_ipreallocate(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3108 int argc, const char **argv)
3110 if (argc != 0) {
3111 usage("ipreallocate");
3114 return ipreallocate(mem_ctx, ctdb);
3117 static int control_gratarp(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3118 int argc, const char **argv)
3120 struct ctdb_addr_info addr_info;
3121 int ret;
3123 if (argc != 2) {
3124 usage("gratarp");
3127 ret = ctdb_sock_addr_from_string(argv[0], &addr_info.addr, false);
3128 if (ret != 0) {
3129 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3130 return 1;
3132 addr_info.iface = argv[1];
3134 ret = ctdb_ctrl_send_gratuitous_arp(mem_ctx, ctdb->ev, ctdb->client,
3135 ctdb->cmd_pnn, TIMEOUT(),
3136 &addr_info);
3137 if (ret != 0) {
3138 fprintf(stderr, "Unable to send gratuitous arp from node %u\n",
3139 ctdb->cmd_pnn);
3140 return ret;
3143 return 0;
3146 static int control_tickle(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3147 int argc, const char **argv)
3149 ctdb_sock_addr src, dst;
3150 int ret;
3152 if (argc != 0 && argc != 2) {
3153 usage("tickle");
3156 if (argc == 0) {
3157 struct ctdb_connection_list *clist;
3158 unsigned int i;
3159 unsigned int num_failed;
3161 /* Client first but the src/dst logic is confused */
3162 ret = ctdb_connection_list_read(mem_ctx, 0, false, &clist);
3163 if (ret != 0) {
3164 return ret;
3167 num_failed = 0;
3168 for (i = 0; i < clist->num; i++) {
3169 ret = ctdb_sys_send_tcp(&clist->conn[i].src,
3170 &clist->conn[i].dst,
3171 0, 0, 0);
3172 if (ret != 0) {
3173 num_failed += 1;
3177 TALLOC_FREE(clist);
3179 if (num_failed > 0) {
3180 fprintf(stderr, "Failed to send %d tickles\n",
3181 num_failed);
3182 return 1;
3185 return 0;
3189 ret = ctdb_sock_addr_from_string(argv[0], &src, true);
3190 if (ret != 0) {
3191 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3192 return 1;
3195 ret = ctdb_sock_addr_from_string(argv[1], &dst, true);
3196 if (ret != 0) {
3197 fprintf(stderr, "Invalid IP address %s\n", argv[1]);
3198 return 1;
3201 ret = ctdb_sys_send_tcp(&src, &dst, 0, 0, 0);
3202 if (ret != 0) {
3203 fprintf(stderr, "Failed to send tickle ack\n");
3204 return ret;
3207 return 0;
3210 static int control_gettickles(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3211 int argc, const char **argv)
3213 ctdb_sock_addr addr;
3214 struct ctdb_tickle_list *tickles;
3215 unsigned port = 0;
3216 unsigned int i;
3217 int ret = 0;
3219 if (argc < 1 || argc > 2) {
3220 usage("gettickles");
3223 if (argc == 2) {
3224 port = smb_strtoul(argv[1], NULL, 10, &ret, SMB_STR_STANDARD);
3225 if (ret != 0) {
3226 return ret;
3230 ret = ctdb_sock_addr_from_string(argv[0], &addr, false);
3231 if (ret != 0) {
3232 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3233 return 1;
3235 ctdb_sock_addr_set_port(&addr, port);
3237 ret = ctdb_ctrl_get_tcp_tickle_list(mem_ctx, ctdb->ev, ctdb->client,
3238 ctdb->cmd_pnn, TIMEOUT(), &addr,
3239 &tickles);
3240 if (ret != 0) {
3241 fprintf(stderr, "Failed to get list of connections\n");
3242 return ret;
3245 if (options.machinereadable) {
3246 printf("%s%s%s%s%s%s%s%s%s\n",
3247 options.sep,
3248 "Source IP", options.sep,
3249 "Port", options.sep,
3250 "Destination IP", options.sep,
3251 "Port", options.sep);
3252 for (i=0; i<tickles->num; i++) {
3253 printf("%s%s%s%u%s%s%s%u%s\n", options.sep,
3254 ctdb_sock_addr_to_string(
3255 mem_ctx, &tickles->conn[i].src, false),
3256 options.sep,
3257 ntohs(tickles->conn[i].src.ip.sin_port),
3258 options.sep,
3259 ctdb_sock_addr_to_string(
3260 mem_ctx, &tickles->conn[i].dst, false),
3261 options.sep,
3262 ntohs(tickles->conn[i].dst.ip.sin_port),
3263 options.sep);
3265 } else {
3266 printf("Connections for IP: %s\n",
3267 ctdb_sock_addr_to_string(mem_ctx,
3268 &tickles->addr, false));
3269 printf("Num connections: %u\n", tickles->num);
3270 for (i=0; i<tickles->num; i++) {
3271 printf("SRC: %s DST: %s\n",
3272 ctdb_sock_addr_to_string(
3273 mem_ctx, &tickles->conn[i].src, true),
3274 ctdb_sock_addr_to_string(
3275 mem_ctx, &tickles->conn[i].dst, true));
3279 talloc_free(tickles);
3280 return 0;
3283 typedef void (*clist_request_func)(struct ctdb_req_control *request,
3284 struct ctdb_connection *conn);
3286 typedef int (*clist_reply_func)(struct ctdb_reply_control *reply);
3288 struct process_clist_state {
3289 struct ctdb_connection_list *clist;
3290 int count;
3291 unsigned int num_failed, num_total;
3292 clist_reply_func reply_func;
3295 static void process_clist_done(struct tevent_req *subreq);
3297 static struct tevent_req *process_clist_send(
3298 TALLOC_CTX *mem_ctx,
3299 struct ctdb_context *ctdb,
3300 struct ctdb_connection_list *clist,
3301 clist_request_func request_func,
3302 clist_reply_func reply_func)
3304 struct tevent_req *req, *subreq;
3305 struct process_clist_state *state;
3306 struct ctdb_req_control request;
3307 unsigned int i;
3309 req = tevent_req_create(mem_ctx, &state, struct process_clist_state);
3310 if (req == NULL) {
3311 return NULL;
3314 state->clist = clist;
3315 state->reply_func = reply_func;
3317 for (i = 0; i < clist->num; i++) {
3318 request_func(&request, &clist->conn[i]);
3319 subreq = ctdb_client_control_send(state, ctdb->ev,
3320 ctdb->client, ctdb->cmd_pnn,
3321 TIMEOUT(), &request);
3322 if (tevent_req_nomem(subreq, req)) {
3323 return tevent_req_post(req, ctdb->ev);
3325 tevent_req_set_callback(subreq, process_clist_done, req);
3328 return req;
3331 static void process_clist_done(struct tevent_req *subreq)
3333 struct tevent_req *req = tevent_req_callback_data(
3334 subreq, struct tevent_req);
3335 struct process_clist_state *state = tevent_req_data(
3336 req, struct process_clist_state);
3337 struct ctdb_reply_control *reply;
3338 int ret;
3339 bool status;
3341 status = ctdb_client_control_recv(subreq, NULL, state, &reply);
3342 TALLOC_FREE(subreq);
3343 if (! status) {
3344 state->num_failed += 1;
3345 goto done;
3348 ret = state->reply_func(reply);
3349 if (ret != 0) {
3350 state->num_failed += 1;
3351 goto done;
3354 done:
3355 state->num_total += 1;
3356 if (state->num_total == state->clist->num) {
3357 tevent_req_done(req);
3361 static int process_clist_recv(struct tevent_req *req)
3363 struct process_clist_state *state = tevent_req_data(
3364 req, struct process_clist_state);
3366 return state->num_failed;
3369 static int control_addtickle(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3370 int argc, const char **argv)
3372 struct ctdb_connection conn;
3373 int ret;
3375 if (argc != 0 && argc != 2) {
3376 usage("addtickle");
3379 if (argc == 0) {
3380 struct ctdb_connection_list *clist;
3381 struct tevent_req *req;
3383 /* Client first but the src/dst logic is confused */
3384 ret = ctdb_connection_list_read(mem_ctx, 0, false, &clist);
3385 if (ret != 0) {
3386 return ret;
3388 if (clist->num == 0) {
3389 return 0;
3392 req = process_clist_send(mem_ctx, ctdb, clist,
3393 ctdb_req_control_tcp_add_delayed_update,
3394 ctdb_reply_control_tcp_add_delayed_update);
3395 if (req == NULL) {
3396 talloc_free(clist);
3397 return ENOMEM;
3400 tevent_req_poll(req, ctdb->ev);
3401 talloc_free(clist);
3403 ret = process_clist_recv(req);
3404 if (ret != 0) {
3405 fprintf(stderr, "Failed to add %d tickles\n", ret);
3406 return 1;
3409 return 0;
3412 ret = ctdb_sock_addr_from_string(argv[0], &conn.src, true);
3413 if (ret != 0) {
3414 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3415 return 1;
3417 ret = ctdb_sock_addr_from_string(argv[1], &conn.dst, true);
3418 if (ret != 0) {
3419 fprintf(stderr, "Invalid IP address %s\n", argv[1]);
3420 return 1;
3423 ret = ctdb_ctrl_tcp_add_delayed_update(mem_ctx, ctdb->ev,
3424 ctdb->client, ctdb->cmd_pnn,
3425 TIMEOUT(), &conn);
3426 if (ret != 0) {
3427 fprintf(stderr, "Failed to register connection\n");
3428 return ret;
3431 return 0;
3434 static int control_deltickle(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3435 int argc, const char **argv)
3437 struct ctdb_connection conn;
3438 int ret;
3440 if (argc != 0 && argc != 2) {
3441 usage("deltickle");
3444 if (argc == 0) {
3445 struct ctdb_connection_list *clist;
3446 struct tevent_req *req;
3448 /* Client first but the src/dst logic is confused */
3449 ret = ctdb_connection_list_read(mem_ctx, 0, false, &clist);
3450 if (ret != 0) {
3451 return ret;
3453 if (clist->num == 0) {
3454 return 0;
3457 req = process_clist_send(mem_ctx, ctdb, clist,
3458 ctdb_req_control_tcp_remove,
3459 ctdb_reply_control_tcp_remove);
3460 if (req == NULL) {
3461 talloc_free(clist);
3462 return ENOMEM;
3465 tevent_req_poll(req, ctdb->ev);
3466 talloc_free(clist);
3468 ret = process_clist_recv(req);
3469 if (ret != 0) {
3470 fprintf(stderr, "Failed to remove %d tickles\n", ret);
3471 return 1;
3474 return 0;
3477 ret = ctdb_sock_addr_from_string(argv[0], &conn.src, true);
3478 if (ret != 0) {
3479 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3480 return 1;
3482 ret = ctdb_sock_addr_from_string(argv[1], &conn.dst, true);
3483 if (ret != 0) {
3484 fprintf(stderr, "Invalid IP address %s\n", argv[1]);
3485 return 1;
3488 ret = ctdb_ctrl_tcp_remove(mem_ctx, ctdb->ev, ctdb->client,
3489 ctdb->cmd_pnn, TIMEOUT(), &conn);
3490 if (ret != 0) {
3491 fprintf(stderr, "Failed to unregister connection\n");
3492 return ret;
3495 return 0;
3498 static int control_listnodes(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3499 int argc, const char **argv)
3501 struct ctdb_node_map *nodemap;
3502 unsigned int i;
3504 if (argc != 0) {
3505 usage("listnodes");
3508 nodemap = read_nodes_file(mem_ctx, CTDB_UNKNOWN_PNN);
3509 if (nodemap == NULL) {
3510 return 1;
3513 for (i=0; i<nodemap->num; i++) {
3514 if (nodemap->node[i].flags & NODE_FLAGS_DELETED) {
3515 continue;
3518 if (options.machinereadable) {
3519 printf("%s%u%s%s%s\n", options.sep,
3520 nodemap->node[i].pnn, options.sep,
3521 ctdb_sock_addr_to_string(
3522 mem_ctx, &nodemap->node[i].addr, false),
3523 options.sep);
3524 } else {
3525 printf("%s\n",
3526 ctdb_sock_addr_to_string(
3527 mem_ctx, &nodemap->node[i].addr, false));
3531 return 0;
3534 static bool nodemap_identical(struct ctdb_node_map *nodemap1,
3535 struct ctdb_node_map *nodemap2)
3537 unsigned int i;
3539 if (nodemap1->num != nodemap2->num) {
3540 return false;
3543 for (i=0; i<nodemap1->num; i++) {
3544 struct ctdb_node_and_flags *n1, *n2;
3546 n1 = &nodemap1->node[i];
3547 n2 = &nodemap2->node[i];
3549 if ((n1->pnn != n2->pnn) ||
3550 (n1->flags != n2->flags) ||
3551 ! ctdb_sock_addr_same_ip(&n1->addr, &n2->addr)) {
3552 return false;
3556 return true;
3559 static int check_node_file_changes(TALLOC_CTX *mem_ctx,
3560 struct ctdb_node_map *nm,
3561 struct ctdb_node_map *fnm,
3562 bool *reload)
3564 unsigned int i;
3565 bool check_failed = false;
3567 *reload = false;
3569 for (i=0; i<nm->num; i++) {
3570 if (i >= fnm->num) {
3571 fprintf(stderr,
3572 "Node %u (%s) missing from nodes file\n",
3573 nm->node[i].pnn,
3574 ctdb_sock_addr_to_string(
3575 mem_ctx, &nm->node[i].addr, false));
3576 check_failed = true;
3577 continue;
3579 if (nm->node[i].flags & NODE_FLAGS_DELETED &&
3580 fnm->node[i].flags & NODE_FLAGS_DELETED) {
3581 /* Node remains deleted */
3582 continue;
3585 if (! (nm->node[i].flags & NODE_FLAGS_DELETED) &&
3586 ! (fnm->node[i].flags & NODE_FLAGS_DELETED)) {
3587 /* Node not newly nor previously deleted */
3588 if (! ctdb_same_ip(&nm->node[i].addr,
3589 &fnm->node[i].addr)) {
3590 fprintf(stderr,
3591 "Node %u has changed IP address"
3592 " (was %s, now %s)\n",
3593 nm->node[i].pnn,
3594 ctdb_sock_addr_to_string(
3595 mem_ctx,
3596 &nm->node[i].addr, false),
3597 ctdb_sock_addr_to_string(
3598 mem_ctx,
3599 &fnm->node[i].addr, false));
3600 check_failed = true;
3601 } else {
3602 if (nm->node[i].flags & NODE_FLAGS_DISCONNECTED) {
3603 fprintf(stderr,
3604 "WARNING: Node %u is disconnected."
3605 " You MUST fix this node manually!\n",
3606 nm->node[i].pnn);
3609 continue;
3612 if (fnm->node[i].flags & NODE_FLAGS_DELETED) {
3613 /* Node is being deleted */
3614 printf("Node %u is DELETED\n", nm->node[i].pnn);
3615 *reload = true;
3616 if (! (nm->node[i].flags & NODE_FLAGS_DISCONNECTED)) {
3617 fprintf(stderr,
3618 "ERROR: Node %u is still connected\n",
3619 nm->node[i].pnn);
3620 check_failed = true;
3622 continue;
3625 if (nm->node[i].flags & NODE_FLAGS_DELETED) {
3626 /* Node was previously deleted */
3627 printf("Node %u is UNDELETED\n", nm->node[i].pnn);
3628 *reload = true;
3632 if (check_failed) {
3633 fprintf(stderr,
3634 "ERROR: Nodes will not be reloaded due to previous error\n");
3635 return 1;
3638 /* Leftover nodes in file are NEW */
3639 for (; i < fnm->num; i++) {
3640 printf("Node %u is NEW\n", fnm->node[i].pnn);
3641 *reload = true;
3644 return 0;
3647 struct disable_recoveries_state {
3648 uint32_t *pnn_list;
3649 unsigned int node_count;
3650 bool *reply;
3651 int status;
3652 bool done;
3655 static void disable_recoveries_handler(uint64_t srvid, TDB_DATA data,
3656 void *private_data)
3658 struct disable_recoveries_state *state =
3659 (struct disable_recoveries_state *)private_data;
3660 unsigned int i;
3661 int ret;
3663 if (data.dsize != sizeof(int)) {
3664 /* Ignore packet */
3665 return;
3668 /* ret will be a PNN (i.e. >=0) on success, or negative on error */
3669 ret = *(int *)data.dptr;
3670 if (ret < 0) {
3671 state->status = ret;
3672 state->done = true;
3673 return;
3675 for (i=0; i<state->node_count; i++) {
3676 if (state->pnn_list[i] == (uint32_t)ret) {
3677 state->reply[i] = true;
3678 break;
3682 state->done = true;
3683 for (i=0; i<state->node_count; i++) {
3684 if (! state->reply[i]) {
3685 state->done = false;
3686 break;
3691 static int disable_recoveries(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3692 uint32_t timeout, uint32_t *pnn_list, int count)
3694 struct ctdb_disable_message disable = { 0 };
3695 struct disable_recoveries_state state;
3696 int ret, i;
3698 disable.pnn = ctdb->pnn;
3699 disable.srvid = next_srvid(ctdb);
3700 disable.timeout = timeout;
3702 state.pnn_list = pnn_list;
3703 state.node_count = count;
3704 state.done = false;
3705 state.status = 0;
3706 state.reply = talloc_zero_array(mem_ctx, bool, count);
3707 if (state.reply == NULL) {
3708 return ENOMEM;
3711 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
3712 disable.srvid,
3713 disable_recoveries_handler,
3714 &state);
3715 if (ret != 0) {
3716 return ret;
3719 for (i=0; i<count; i++) {
3720 ret = ctdb_message_disable_recoveries(mem_ctx, ctdb->ev,
3721 ctdb->client,
3722 pnn_list[i],
3723 &disable);
3724 if (ret != 0) {
3725 goto fail;
3729 ret = ctdb_client_wait_timeout(ctdb->ev, &state.done, TIMEOUT());
3730 if (ret == ETIME) {
3731 fprintf(stderr, "Timed out waiting to disable recoveries\n");
3732 } else {
3733 ret = (state.status >= 0 ? 0 : 1);
3736 fail:
3737 ctdb_client_remove_message_handler(ctdb->ev, ctdb->client,
3738 disable.srvid, &state);
3739 return ret;
3742 static int control_reloadnodes(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3743 int argc, const char **argv)
3745 struct ctdb_node_map *nodemap = NULL;
3746 struct ctdb_node_map *file_nodemap;
3747 struct ctdb_node_map *remote_nodemap;
3748 struct ctdb_req_control request;
3749 struct ctdb_reply_control **reply;
3750 bool reload;
3751 unsigned int i;
3752 int count;
3753 int ret;
3754 uint32_t *pnn_list;
3756 nodemap = get_nodemap(ctdb, false);
3757 if (nodemap == NULL) {
3758 return 1;
3761 file_nodemap = read_nodes_file(mem_ctx, ctdb->pnn);
3762 if (file_nodemap == NULL) {
3763 return 1;
3766 for (i=0; i<nodemap->num; i++) {
3767 if (nodemap->node[i].flags & NODE_FLAGS_DISCONNECTED) {
3768 continue;
3771 ret = ctdb_ctrl_get_nodes_file(mem_ctx, ctdb->ev, ctdb->client,
3772 nodemap->node[i].pnn, TIMEOUT(),
3773 &remote_nodemap);
3774 if (ret != 0) {
3775 fprintf(stderr,
3776 "ERROR: Failed to get nodes file from node %u\n",
3777 nodemap->node[i].pnn);
3778 return ret;
3781 if (! nodemap_identical(file_nodemap, remote_nodemap)) {
3782 fprintf(stderr,
3783 "ERROR: Nodes file on node %u differs"
3784 " from current node (%u)\n",
3785 nodemap->node[i].pnn, ctdb->pnn);
3786 return 1;
3790 ret = check_node_file_changes(mem_ctx, nodemap, file_nodemap, &reload);
3791 if (ret != 0) {
3792 return ret;
3795 if (! reload) {
3796 fprintf(stderr, "No change in nodes file,"
3797 " skipping unnecessary reload\n");
3798 return 0;
3801 count = list_of_connected_nodes(nodemap, CTDB_UNKNOWN_PNN,
3802 mem_ctx, &pnn_list);
3803 if (count <= 0) {
3804 fprintf(stderr, "Memory allocation error\n");
3805 return 1;
3808 ret = disable_recoveries(mem_ctx, ctdb, 2*options.timelimit,
3809 pnn_list, count);
3810 if (ret != 0) {
3811 fprintf(stderr, "Failed to disable recoveries\n");
3812 return ret;
3815 ctdb_req_control_reload_nodes_file(&request);
3816 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
3817 pnn_list, count, TIMEOUT(),
3818 &request, NULL, &reply);
3819 if (ret != 0) {
3820 bool failed = false;
3821 int j;
3823 for (j=0; j<count; j++) {
3824 ret = ctdb_reply_control_reload_nodes_file(reply[j]);
3825 if (ret != 0) {
3826 fprintf(stderr,
3827 "Node %u failed to reload nodes\n",
3828 pnn_list[j]);
3829 failed = true;
3832 if (failed) {
3833 fprintf(stderr,
3834 "You MUST fix failed nodes manually!\n");
3838 ret = disable_recoveries(mem_ctx, ctdb, 0, pnn_list, count);
3839 if (ret != 0) {
3840 fprintf(stderr, "Failed to enable recoveries\n");
3841 return ret;
3844 return 0;
3847 static int moveip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3848 ctdb_sock_addr *addr, uint32_t pnn)
3850 struct ctdb_public_ip_list *pubip_list;
3851 struct ctdb_public_ip pubip;
3852 struct ctdb_node_map *nodemap;
3853 struct ctdb_req_control request;
3854 uint32_t *pnn_list;
3855 unsigned int i;
3856 int ret, count;
3857 uint32_t *connected_pnn = NULL;
3858 int connected_count;
3860 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
3861 pnn, TIMEOUT(), false, &pubip_list);
3862 if (ret != 0) {
3863 fprintf(stderr, "Failed to get Public IPs from node %u\n",
3864 pnn);
3865 return ret;
3868 for (i=0; i<pubip_list->num; i++) {
3869 if (ctdb_same_ip(addr, &pubip_list->ip[i].addr)) {
3870 break;
3874 if (i == pubip_list->num) {
3875 fprintf(stderr, "Node %u CANNOT host IP address %s\n",
3876 pnn, ctdb_sock_addr_to_string(mem_ctx, addr, false));
3877 return 1;
3880 nodemap = get_nodemap(ctdb, false);
3881 if (nodemap == NULL) {
3882 return 1;
3885 count = list_of_active_nodes(nodemap, pnn, mem_ctx, &pnn_list);
3886 if (count <= 0) {
3887 fprintf(stderr, "Memory allocation error\n");
3888 return 1;
3891 connected_count = list_of_connected_nodes(nodemap,
3892 CTDB_UNKNOWN_PNN,
3893 mem_ctx,
3894 &connected_pnn);
3895 if (connected_count <= 0) {
3896 fprintf(stderr, "Memory allocation error\n");
3897 return 1;
3901 * Disable takeover runs on all connected nodes. A reply
3902 * indicating success is needed from each node so all nodes
3903 * will need to be active.
3905 * A check could be added to not allow reloading of IPs when
3906 * there are disconnected nodes. However, this should
3907 * probably be left up to the administrator.
3909 ret = disable_takeover_runs(mem_ctx,
3910 ctdb,
3911 2*options.timelimit,
3912 connected_pnn,
3913 connected_count);
3914 if (ret != 0) {
3915 fprintf(stderr, "Failed to disable takeover runs\n");
3916 return ret;
3919 pubip.pnn = pnn;
3920 pubip.addr = *addr;
3921 ctdb_req_control_release_ip(&request, &pubip);
3923 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
3924 pnn_list, count, TIMEOUT(),
3925 &request, NULL, NULL);
3926 if (ret != 0) {
3927 fprintf(stderr, "Failed to release IP on nodes\n");
3928 return ret;
3931 ret = ctdb_ctrl_takeover_ip(mem_ctx, ctdb->ev, ctdb->client,
3932 pnn, TIMEOUT(), &pubip);
3933 if (ret != 0) {
3934 fprintf(stderr, "Failed to takeover IP on node %u\n", pnn);
3935 return ret;
3939 * It isn't strictly necessary to wait until takeover runs are
3940 * re-enabled but doing so can't hurt.
3942 ret = disable_takeover_runs(mem_ctx,
3943 ctdb,
3945 connected_pnn,
3946 connected_count);
3947 if (ret != 0) {
3948 fprintf(stderr, "Failed to enable takeover runs\n");
3949 return ret;
3952 return send_ipreallocated_control_to_nodes(mem_ctx,
3953 ctdb,
3954 connected_pnn,
3955 connected_count);
3958 static int control_moveip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
3959 int argc, const char **argv)
3961 ctdb_sock_addr addr;
3962 uint32_t pnn;
3963 int retries = 0;
3964 int ret = 0;
3966 if (argc != 2) {
3967 usage("moveip");
3970 ret = ctdb_sock_addr_from_string(argv[0], &addr, false);
3971 if (ret != 0) {
3972 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
3973 return 1;
3976 pnn = smb_strtoul(argv[1], NULL, 10, &ret, SMB_STR_STANDARD);
3977 if (pnn == CTDB_UNKNOWN_PNN || ret != 0) {
3978 fprintf(stderr, "Invalid PNN %s\n", argv[1]);
3979 return 1;
3982 while (retries < 5) {
3983 ret = moveip(mem_ctx, ctdb, &addr, pnn);
3984 if (ret == 0) {
3985 break;
3988 sleep(3);
3989 retries++;
3992 if (ret != 0) {
3993 fprintf(stderr, "Failed to move IP %s to node %u\n",
3994 argv[0], pnn);
3995 return ret;
3998 return 0;
4001 static int rebalancenode(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4002 uint32_t pnn)
4004 int ret;
4006 ret = ctdb_message_rebalance_node(mem_ctx, ctdb->ev, ctdb->client,
4007 CTDB_BROADCAST_CONNECTED, pnn);
4008 if (ret != 0) {
4009 fprintf(stderr,
4010 "Failed to ask leader to distribute IPs\n");
4011 return ret;
4014 return 0;
4017 static int control_addip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4018 int argc, const char **argv)
4020 ctdb_sock_addr addr;
4021 struct ctdb_public_ip_list *pubip_list;
4022 struct ctdb_addr_info addr_info;
4023 unsigned int mask, i;
4024 int ret, retries = 0;
4026 if (argc != 2) {
4027 usage("addip");
4030 ret = ctdb_sock_addr_mask_from_string(argv[0], &addr, &mask);
4031 if (ret != 0) {
4032 fprintf(stderr, "Invalid IP/Mask %s\n", argv[0]);
4033 return 1;
4036 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
4037 ctdb->cmd_pnn, TIMEOUT(),
4038 false, &pubip_list);
4039 if (ret != 0) {
4040 fprintf(stderr, "Failed to get Public IPs from node %u\n",
4041 ctdb->cmd_pnn);
4042 return 1;
4045 for (i=0; i<pubip_list->num; i++) {
4046 if (ctdb_same_ip(&addr, &pubip_list->ip[i].addr)) {
4047 fprintf(stderr, "Node already knows about IP %s\n",
4048 ctdb_sock_addr_to_string(mem_ctx,
4049 &addr, false));
4050 return 0;
4054 addr_info.addr = addr;
4055 addr_info.mask = mask;
4056 addr_info.iface = argv[1];
4058 while (retries < 5) {
4059 ret = ctdb_ctrl_add_public_ip(mem_ctx, ctdb->ev, ctdb->client,
4060 ctdb->cmd_pnn, TIMEOUT(),
4061 &addr_info);
4062 if (ret == 0) {
4063 break;
4066 sleep(3);
4067 retries++;
4070 if (ret != 0) {
4071 fprintf(stderr, "Failed to add public IP to node %u."
4072 " Giving up\n", ctdb->cmd_pnn);
4073 return ret;
4076 ret = rebalancenode(mem_ctx, ctdb, ctdb->cmd_pnn);
4077 if (ret != 0) {
4078 return ret;
4082 * CTDB_CONTROL_ADD_PUBLIC_IP will implicitly trigger
4083 * CTDB_SRVID_TAKEOVER_RUN broadcast to all connected nodes.
4085 * That means CTDB_{CONTROL,EVENT,SRVID}_IPREALLOCATED is
4086 * triggered at the end of the takeover run...
4088 * So we don't need to call ipreallocate() nor
4089 * send_ipreallocated_control_to_nodes() here...
4092 return 0;
4095 static int control_delip(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4096 int argc, const char **argv)
4098 ctdb_sock_addr addr;
4099 struct ctdb_public_ip_list *pubip_list;
4100 struct ctdb_addr_info addr_info;
4101 unsigned int i;
4102 int ret;
4104 if (argc != 1) {
4105 usage("delip");
4108 ret = ctdb_sock_addr_from_string(argv[0], &addr, false);
4109 if (ret != 0) {
4110 fprintf(stderr, "Invalid IP address %s\n", argv[0]);
4111 return 1;
4114 ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
4115 ctdb->cmd_pnn, TIMEOUT(),
4116 false, &pubip_list);
4117 if (ret != 0) {
4118 fprintf(stderr, "Failed to get Public IPs from node %u\n",
4119 ctdb->cmd_pnn);
4120 return 1;
4123 for (i=0; i<pubip_list->num; i++) {
4124 if (ctdb_same_ip(&addr, &pubip_list->ip[i].addr)) {
4125 break;
4129 if (i == pubip_list->num) {
4130 fprintf(stderr, "Node does not know about IP address %s\n",
4131 ctdb_sock_addr_to_string(mem_ctx, &addr, false));
4132 return 0;
4135 addr_info.addr = addr;
4136 addr_info.mask = 0;
4137 addr_info.iface = NULL;
4139 ret = ctdb_ctrl_del_public_ip(mem_ctx, ctdb->ev, ctdb->client,
4140 ctdb->cmd_pnn, TIMEOUT(), &addr_info);
4141 if (ret != 0) {
4142 fprintf(stderr, "Failed to delete public IP from node %u\n",
4143 ctdb->cmd_pnn);
4144 return ret;
4148 * CTDB_CONTROL_DEL_PUBLIC_IP only marks the public ip
4149 * with pending_delete if it's still in use.
4151 * Any later takeover run will really move the public ip
4152 * away from the local node and finally removes it.
4154 * That means CTDB_{CONTROL,EVENT,SRVID}_IPREALLOCATED is
4155 * triggered at the end of the takeover run that actually
4156 * moves the public ip away.
4158 * So we don't need to call ipreallocate() nor
4159 * send_ipreallocated_control_to_nodes() here...
4162 return 0;
4165 #define DB_VERSION 3
4166 #define MAX_DB_NAME 64
4167 #define MAX_REC_BUFFER_SIZE (100*1000)
4169 struct db_header {
4170 unsigned long version;
4171 time_t timestamp;
4172 unsigned long flags;
4173 unsigned long nbuf;
4174 unsigned long nrec;
4175 char name[MAX_DB_NAME];
4178 struct backup_state {
4179 TALLOC_CTX *mem_ctx;
4180 struct ctdb_rec_buffer *recbuf;
4181 uint32_t db_id;
4182 int fd;
4183 unsigned int nbuf, nrec;
4186 static int backup_handler(uint32_t reqid, struct ctdb_ltdb_header *header,
4187 TDB_DATA key, TDB_DATA data, void *private_data)
4189 struct backup_state *state = (struct backup_state *)private_data;
4190 size_t len;
4191 int ret;
4193 if (state->recbuf == NULL) {
4194 state->recbuf = ctdb_rec_buffer_init(state->mem_ctx,
4195 state->db_id);
4196 if (state->recbuf == NULL) {
4197 return ENOMEM;
4201 ret = ctdb_rec_buffer_add(state->recbuf, state->recbuf, reqid,
4202 header, key, data);
4203 if (ret != 0) {
4204 return ret;
4207 len = ctdb_rec_buffer_len(state->recbuf);
4208 if (len < MAX_REC_BUFFER_SIZE) {
4209 return 0;
4212 ret = ctdb_rec_buffer_write(state->recbuf, state->fd);
4213 if (ret != 0) {
4214 fprintf(stderr, "Failed to write records to backup file\n");
4215 return ret;
4218 state->nbuf += 1;
4219 state->nrec += state->recbuf->count;
4220 TALLOC_FREE(state->recbuf);
4222 return 0;
4225 static int control_backupdb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4226 int argc, const char **argv)
4228 const char *db_name;
4229 struct ctdb_db_context *db;
4230 uint32_t db_id;
4231 uint8_t db_flags;
4232 struct backup_state state;
4233 struct db_header db_hdr;
4234 int fd, ret;
4236 if (argc != 2) {
4237 usage("backupdb");
4240 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
4241 return 1;
4244 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4245 db_flags, &db);
4246 if (ret != 0) {
4247 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4248 return ret;
4251 fd = open(argv[1], O_RDWR|O_CREAT, 0600);
4252 if (fd == -1) {
4253 ret = errno;
4254 fprintf(stderr, "Failed to open file %s for writing\n",
4255 argv[1]);
4256 return ret;
4259 /* Write empty header first */
4260 ZERO_STRUCT(db_hdr);
4261 ret = write(fd, &db_hdr, sizeof(struct db_header));
4262 if (ret == -1) {
4263 ret = errno;
4264 close(fd);
4265 fprintf(stderr, "Failed to write header to file %s\n", argv[1]);
4266 return ret;
4269 state.mem_ctx = mem_ctx;
4270 state.recbuf = NULL;
4271 state.fd = fd;
4272 state.nbuf = 0;
4273 state.nrec = 0;
4275 ret = ctdb_db_traverse_local(db, true, false, backup_handler, &state);
4276 if (ret != 0) {
4277 fprintf(stderr, "Failed to collect records from DB %s\n",
4278 db_name);
4279 close(fd);
4280 return ret;
4283 if (state.recbuf != NULL) {
4284 ret = ctdb_rec_buffer_write(state.recbuf, state.fd);
4285 if (ret != 0) {
4286 fprintf(stderr,
4287 "Failed to write records to backup file\n");
4288 close(fd);
4289 return ret;
4292 state.nbuf += 1;
4293 state.nrec += state.recbuf->count;
4294 TALLOC_FREE(state.recbuf);
4297 db_hdr.version = DB_VERSION;
4298 db_hdr.timestamp = time(NULL);
4299 db_hdr.flags = db_flags;
4300 db_hdr.nbuf = state.nbuf;
4301 db_hdr.nrec = state.nrec;
4302 strncpy(db_hdr.name, db_name, MAX_DB_NAME-1);
4304 lseek(fd, 0, SEEK_SET);
4305 ret = write(fd, &db_hdr, sizeof(struct db_header));
4306 if (ret == -1) {
4307 ret = errno;
4308 close(fd);
4309 fprintf(stderr, "Failed to write header to file %s\n", argv[1]);
4310 return ret;
4313 close(fd);
4314 printf("Database backed up to %s\n", argv[1]);
4315 return 0;
4318 static int control_restoredb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4319 int argc, const char **argv)
4321 const char *db_name = NULL;
4322 struct ctdb_db_context *db;
4323 struct db_header db_hdr;
4324 struct ctdb_node_map *nodemap;
4325 struct ctdb_req_control request;
4326 struct ctdb_reply_control **reply;
4327 struct ctdb_transdb wipedb;
4328 struct ctdb_pulldb_ext pulldb;
4329 struct ctdb_rec_buffer *recbuf;
4330 uint32_t generation;
4331 uint32_t *pnn_list;
4332 char timebuf[128];
4333 ssize_t n;
4334 int fd;
4335 unsigned long i, count;
4336 int ret;
4337 uint8_t db_flags;
4339 if (argc < 1 || argc > 2) {
4340 usage("restoredb");
4343 fd = open(argv[0], O_RDONLY, 0600);
4344 if (fd == -1) {
4345 ret = errno;
4346 fprintf(stderr, "Failed to open file %s for reading\n",
4347 argv[0]);
4348 return ret;
4351 if (argc == 2) {
4352 db_name = argv[1];
4355 n = read(fd, &db_hdr, sizeof(struct db_header));
4356 if (n == -1) {
4357 ret = errno;
4358 close(fd);
4359 fprintf(stderr, "Failed to read db header from file %s\n",
4360 argv[0]);
4361 return ret;
4363 db_hdr.name[sizeof(db_hdr.name)-1] = '\0';
4365 if (db_hdr.version != DB_VERSION) {
4366 fprintf(stderr,
4367 "Wrong version of backup file, expected %u, got %lu\n",
4368 DB_VERSION, db_hdr.version);
4369 close(fd);
4370 return EINVAL;
4373 if (db_name == NULL) {
4374 db_name = db_hdr.name;
4377 strftime(timebuf, sizeof(timebuf)-1, "%Y/%m/%d %H:%M:%S",
4378 localtime(&db_hdr.timestamp));
4379 printf("Restoring database %s from backup @ %s\n", db_name, timebuf);
4381 db_flags = db_hdr.flags & 0xff;
4382 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4383 db_flags, &db);
4384 if (ret != 0) {
4385 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4386 close(fd);
4387 return ret;
4390 nodemap = get_nodemap(ctdb, false);
4391 if (nodemap == NULL) {
4392 fprintf(stderr, "Failed to get nodemap\n");
4393 close(fd);
4394 return ENOMEM;
4397 ret = get_generation(mem_ctx, ctdb, &generation);
4398 if (ret != 0) {
4399 fprintf(stderr, "Failed to get current generation\n");
4400 close(fd);
4401 return ret;
4404 count = list_of_active_nodes(nodemap, CTDB_UNKNOWN_PNN, mem_ctx,
4405 &pnn_list);
4406 if (count <= 0) {
4407 close(fd);
4408 return ENOMEM;
4411 wipedb.db_id = ctdb_db_id(db);
4412 wipedb.tid = generation;
4414 ctdb_req_control_db_freeze(&request, wipedb.db_id);
4415 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4416 ctdb->client, pnn_list, count,
4417 TIMEOUT(), &request, NULL, NULL);
4418 if (ret != 0) {
4419 goto failed;
4423 ctdb_req_control_db_transaction_start(&request, &wipedb);
4424 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4425 pnn_list, count, TIMEOUT(),
4426 &request, NULL, NULL);
4427 if (ret != 0) {
4428 goto failed;
4431 ctdb_req_control_wipe_database(&request, &wipedb);
4432 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4433 pnn_list, count, TIMEOUT(),
4434 &request, NULL, NULL);
4435 if (ret != 0) {
4436 goto failed;
4439 pulldb.db_id = ctdb_db_id(db);
4440 pulldb.lmaster = 0;
4441 pulldb.srvid = SRVID_CTDB_PUSHDB;
4443 ctdb_req_control_db_push_start(&request, &pulldb);
4444 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4445 pnn_list, count, TIMEOUT(),
4446 &request, NULL, NULL);
4447 if (ret != 0) {
4448 goto failed;
4451 for (i=0; i<db_hdr.nbuf; i++) {
4452 struct ctdb_req_message message;
4453 TDB_DATA data;
4454 size_t np;
4456 ret = ctdb_rec_buffer_read(fd, mem_ctx, &recbuf);
4457 if (ret != 0) {
4458 goto failed;
4461 data.dsize = ctdb_rec_buffer_len(recbuf);
4462 data.dptr = talloc_size(mem_ctx, data.dsize);
4463 if (data.dptr == NULL) {
4464 goto failed;
4467 ctdb_rec_buffer_push(recbuf, data.dptr, &np);
4469 message.srvid = pulldb.srvid;
4470 message.data.data = data;
4472 ret = ctdb_client_message_multi(mem_ctx, ctdb->ev,
4473 ctdb->client,
4474 pnn_list, count,
4475 &message, NULL);
4476 if (ret != 0) {
4477 goto failed;
4480 talloc_free(recbuf);
4481 talloc_free(data.dptr);
4484 ctdb_req_control_db_push_confirm(&request, pulldb.db_id);
4485 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4486 pnn_list, count, TIMEOUT(),
4487 &request, NULL, &reply);
4488 if (ret != 0) {
4489 goto failed;
4492 for (i=0; i<count; i++) {
4493 uint32_t num_records;
4495 ret = ctdb_reply_control_db_push_confirm(reply[i],
4496 &num_records);
4497 if (ret != 0) {
4498 fprintf(stderr, "Invalid response from node %u\n",
4499 pnn_list[i]);
4500 goto failed;
4503 if (num_records != db_hdr.nrec) {
4504 fprintf(stderr, "Node %u received %u of %lu records\n",
4505 pnn_list[i], num_records, db_hdr.nrec);
4506 goto failed;
4510 ctdb_req_control_db_set_healthy(&request, wipedb.db_id);
4511 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4512 pnn_list, count, TIMEOUT(),
4513 &request, NULL, NULL);
4514 if (ret != 0) {
4515 goto failed;
4518 ctdb_req_control_db_transaction_commit(&request, &wipedb);
4519 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4520 pnn_list, count, TIMEOUT(),
4521 &request, NULL, NULL);
4522 if (ret != 0) {
4523 goto failed;
4526 ctdb_req_control_db_thaw(&request, wipedb.db_id);
4527 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4528 ctdb->client, pnn_list, count,
4529 TIMEOUT(), &request, NULL, NULL);
4530 if (ret != 0) {
4531 goto failed;
4534 printf("Database %s restored\n", db_name);
4535 close(fd);
4536 return 0;
4539 failed:
4540 close(fd);
4541 ctdb_ctrl_set_recmode(mem_ctx, ctdb->ev, ctdb->client,
4542 ctdb->pnn, TIMEOUT(), CTDB_RECOVERY_ACTIVE);
4543 return ret;
4546 struct dumpdbbackup_state {
4547 ctdb_rec_parser_func_t parser;
4548 struct dump_record_state sub_state;
4551 static int dumpdbbackup_handler(uint32_t reqid,
4552 struct ctdb_ltdb_header *header,
4553 TDB_DATA key, TDB_DATA data,
4554 void *private_data)
4556 struct dumpdbbackup_state *state =
4557 (struct dumpdbbackup_state *)private_data;
4558 struct ctdb_ltdb_header hdr;
4559 int ret;
4561 ret = ctdb_ltdb_header_extract(&data, &hdr);
4562 if (ret != 0) {
4563 return ret;
4566 return state->parser(reqid, &hdr, key, data, &state->sub_state);
4569 static int control_dumpdbbackup(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4570 int argc, const char **argv)
4572 struct db_header db_hdr;
4573 char timebuf[128];
4574 struct dumpdbbackup_state state;
4575 ssize_t n;
4576 unsigned long i;
4577 int fd, ret;
4579 if (argc != 1) {
4580 usage("dumpbackup");
4583 fd = open(argv[0], O_RDONLY, 0600);
4584 if (fd == -1) {
4585 ret = errno;
4586 fprintf(stderr, "Failed to open file %s for reading\n",
4587 argv[0]);
4588 return ret;
4591 n = read(fd, &db_hdr, sizeof(struct db_header));
4592 if (n == -1) {
4593 ret = errno;
4594 close(fd);
4595 fprintf(stderr, "Failed to read db header from file %s\n",
4596 argv[0]);
4597 return ret;
4599 db_hdr.name[sizeof(db_hdr.name)-1] = '\0';
4601 if (db_hdr.version != DB_VERSION) {
4602 fprintf(stderr,
4603 "Wrong version of backup file, expected %u, got %lu\n",
4604 DB_VERSION, db_hdr.version);
4605 close(fd);
4606 return EINVAL;
4609 strftime(timebuf, sizeof(timebuf)-1, "%Y/%m/%d %H:%M:%S",
4610 localtime(&db_hdr.timestamp));
4611 printf("Dumping database %s from backup @ %s\n",
4612 db_hdr.name, timebuf);
4614 state.parser = dump_record;
4615 state.sub_state.count = 0;
4617 for (i=0; i<db_hdr.nbuf; i++) {
4618 struct ctdb_rec_buffer *recbuf;
4620 ret = ctdb_rec_buffer_read(fd, mem_ctx, &recbuf);
4621 if (ret != 0) {
4622 fprintf(stderr, "Failed to read records\n");
4623 close(fd);
4624 return ret;
4627 ret = ctdb_rec_buffer_traverse(recbuf, dumpdbbackup_handler,
4628 &state);
4629 if (ret != 0) {
4630 fprintf(stderr, "Failed to dump records\n");
4631 close(fd);
4632 return ret;
4636 close(fd);
4637 printf("Dumped %u record(s)\n", state.sub_state.count);
4638 return 0;
4641 static int control_wipedb(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4642 int argc, const char **argv)
4644 const char *db_name;
4645 struct ctdb_db_context *db;
4646 uint32_t db_id;
4647 uint8_t db_flags;
4648 struct ctdb_node_map *nodemap;
4649 struct ctdb_req_control request;
4650 struct ctdb_transdb wipedb;
4651 uint32_t generation;
4652 uint32_t *pnn_list;
4653 int count, ret;
4655 if (argc != 1) {
4656 usage("wipedb");
4659 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, &db_flags)) {
4660 return 1;
4663 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
4664 db_flags, &db);
4665 if (ret != 0) {
4666 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
4667 return ret;
4670 nodemap = get_nodemap(ctdb, false);
4671 if (nodemap == NULL) {
4672 fprintf(stderr, "Failed to get nodemap\n");
4673 return ENOMEM;
4676 ret = get_generation(mem_ctx, ctdb, &generation);
4677 if (ret != 0) {
4678 fprintf(stderr, "Failed to get current generation\n");
4679 return ret;
4682 count = list_of_active_nodes(nodemap, CTDB_UNKNOWN_PNN, mem_ctx,
4683 &pnn_list);
4684 if (count <= 0) {
4685 return ENOMEM;
4688 ctdb_req_control_db_freeze(&request, db_id);
4689 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4690 ctdb->client, pnn_list, count,
4691 TIMEOUT(), &request, NULL, NULL);
4692 if (ret != 0) {
4693 goto failed;
4696 wipedb.db_id = db_id;
4697 wipedb.tid = generation;
4699 ctdb_req_control_db_transaction_start(&request, &wipedb);
4700 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4701 pnn_list, count, TIMEOUT(),
4702 &request, NULL, NULL);
4703 if (ret != 0) {
4704 goto failed;
4707 ctdb_req_control_wipe_database(&request, &wipedb);
4708 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4709 pnn_list, count, TIMEOUT(),
4710 &request, NULL, NULL);
4711 if (ret != 0) {
4712 goto failed;
4715 ctdb_req_control_db_set_healthy(&request, db_id);
4716 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4717 pnn_list, count, TIMEOUT(),
4718 &request, NULL, NULL);
4719 if (ret != 0) {
4720 goto failed;
4723 ctdb_req_control_db_transaction_commit(&request, &wipedb);
4724 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
4725 pnn_list, count, TIMEOUT(),
4726 &request, NULL, NULL);
4727 if (ret != 0) {
4728 goto failed;
4731 ctdb_req_control_db_thaw(&request, db_id);
4732 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev,
4733 ctdb->client, pnn_list, count,
4734 TIMEOUT(), &request, NULL, NULL);
4735 if (ret != 0) {
4736 goto failed;
4739 printf("Database %s wiped\n", db_name);
4740 return 0;
4743 failed:
4744 ctdb_ctrl_set_recmode(mem_ctx, ctdb->ev, ctdb->client,
4745 ctdb->pnn, TIMEOUT(), CTDB_RECOVERY_ACTIVE);
4746 return ret;
4749 static int control_leader(TALLOC_CTX *mem_ctx,
4750 struct ctdb_context *ctdb,
4751 int argc,
4752 const char **argv)
4754 uint32_t leader;
4755 int ret;
4757 ret = get_leader(mem_ctx, ctdb, &leader);
4758 if (ret != 0) {
4759 return ret;
4762 print_pnn(leader);
4764 return 0;
4767 static int control_event(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4768 int argc, const char **argv)
4770 char *t, *event_helper = NULL;
4772 t = getenv("CTDB_EVENT_HELPER");
4773 if (t != NULL) {
4774 event_helper = talloc_strdup(mem_ctx, t);
4775 } else {
4776 event_helper = talloc_asprintf(mem_ctx, "%s/ctdb-event",
4777 CTDB_HELPER_BINDIR);
4780 if (event_helper == NULL) {
4781 fprintf(stderr, "Unable to set event daemon helper\n");
4782 return 1;
4785 return run_helper(mem_ctx, "event daemon helper", event_helper,
4786 argc, argv);
4789 static int control_scriptstatus(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4790 int argc, const char **argv)
4792 const char *new_argv[4];
4794 if (argc > 1) {
4795 usage("scriptstatus");
4798 new_argv[0] = "status";
4799 new_argv[1] = "legacy";
4800 new_argv[2] = (argc == 0) ? "monitor" : argv[0];
4801 new_argv[3] = NULL;
4803 (void) control_event(mem_ctx, ctdb, 3, new_argv);
4804 return 0;
4807 static int control_natgw(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4808 int argc, const char **argv)
4810 char *t, *natgw_helper = NULL;
4812 if (argc != 1) {
4813 usage("natgw");
4816 t = getenv("CTDB_NATGW_HELPER");
4817 if (t != NULL) {
4818 natgw_helper = talloc_strdup(mem_ctx, t);
4819 } else {
4820 natgw_helper = talloc_asprintf(mem_ctx, "%s/ctdb_natgw",
4821 CTDB_HELPER_BINDIR);
4824 if (natgw_helper == NULL) {
4825 fprintf(stderr, "Unable to set NAT gateway helper\n");
4826 return 1;
4829 return run_helper(mem_ctx, "NAT gateway helper", natgw_helper,
4830 argc, argv);
4834 * Find the PNN of the current node
4835 * discover the pnn by loading the nodes file and try to bind
4836 * to all addresses one at a time until the ip address is found.
4838 static bool find_node_xpnn(TALLOC_CTX *mem_ctx, uint32_t *pnn)
4840 struct ctdb_node_map *nodemap;
4841 unsigned int i;
4843 nodemap = read_nodes_file(mem_ctx, CTDB_UNKNOWN_PNN);
4844 if (nodemap == NULL) {
4845 return false;
4848 for (i=0; i<nodemap->num; i++) {
4849 if (nodemap->node[i].flags & NODE_FLAGS_DELETED) {
4850 continue;
4852 if (ctdb_sys_have_ip(&nodemap->node[i].addr)) {
4853 if (pnn != NULL) {
4854 *pnn = nodemap->node[i].pnn;
4856 talloc_free(nodemap);
4857 return true;
4861 fprintf(stderr, "Failed to detect PNN of the current node.\n");
4862 talloc_free(nodemap);
4863 return false;
4866 static int control_getreclock(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4867 int argc, const char **argv)
4869 const char *reclock;
4870 int ret;
4872 if (argc != 0) {
4873 usage("getreclock");
4876 ret = ctdb_ctrl_get_reclock_file(mem_ctx, ctdb->ev, ctdb->client,
4877 ctdb->cmd_pnn, TIMEOUT(), &reclock);
4878 if (ret != 0) {
4879 return ret;
4882 if (reclock != NULL) {
4883 printf("%s\n", reclock);
4886 return 0;
4889 static int control_setlmasterrole(TALLOC_CTX *mem_ctx,
4890 struct ctdb_context *ctdb,
4891 int argc, const char **argv)
4893 uint32_t lmasterrole = 0;
4894 int ret;
4896 if (argc != 1) {
4897 usage("setlmasterrole");
4900 if (strcmp(argv[0], "on") == 0) {
4901 lmasterrole = 1;
4902 } else if (strcmp(argv[0], "off") == 0) {
4903 lmasterrole = 0;
4904 } else {
4905 usage("setlmasterrole");
4908 ret = ctdb_ctrl_set_lmasterrole(mem_ctx, ctdb->ev, ctdb->client,
4909 ctdb->cmd_pnn, TIMEOUT(), lmasterrole);
4910 if (ret != 0) {
4911 return ret;
4914 return 0;
4917 static int control_setleaderrole(TALLOC_CTX *mem_ctx,
4918 struct ctdb_context *ctdb,
4919 int argc,
4920 const char **argv)
4922 uint32_t leaderrole = 0;
4923 int ret;
4925 if (argc != 1) {
4926 usage("setleaderrole");
4929 if (strcmp(argv[0], "on") == 0) {
4930 leaderrole = 1;
4931 } else if (strcmp(argv[0], "off") == 0) {
4932 leaderrole = 0;
4933 } else {
4934 usage("setleaderrole");
4937 ret = ctdb_ctrl_set_recmasterrole(mem_ctx,
4938 ctdb->ev,
4939 ctdb->client,
4940 ctdb->cmd_pnn,
4941 TIMEOUT(),
4942 leaderrole);
4943 if (ret != 0) {
4944 return ret;
4947 return 0;
4950 static int control_setdbreadonly(TALLOC_CTX *mem_ctx,
4951 struct ctdb_context *ctdb,
4952 int argc, const char **argv)
4954 uint32_t db_id;
4955 uint8_t db_flags;
4956 int ret;
4958 if (argc != 1) {
4959 usage("setdbreadonly");
4962 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, NULL, &db_flags)) {
4963 return 1;
4966 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
4967 fprintf(stderr, "READONLY can be set only on volatile DB\n");
4968 return 1;
4971 ret = ctdb_ctrl_set_db_readonly(mem_ctx, ctdb->ev, ctdb->client,
4972 ctdb->cmd_pnn, TIMEOUT(), db_id);
4973 if (ret != 0) {
4974 return ret;
4977 return 0;
4980 static int control_setdbsticky(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
4981 int argc, const char **argv)
4983 uint32_t db_id;
4984 uint8_t db_flags;
4985 int ret;
4987 if (argc != 1) {
4988 usage("setdbsticky");
4991 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, NULL, &db_flags)) {
4992 return 1;
4995 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
4996 fprintf(stderr, "STICKY can be set only on volatile DB\n");
4997 return 1;
5000 ret = ctdb_ctrl_set_db_sticky(mem_ctx, ctdb->ev, ctdb->client,
5001 ctdb->cmd_pnn, TIMEOUT(), db_id);
5002 if (ret != 0) {
5003 return ret;
5006 return 0;
5009 static int control_pfetch(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5010 int argc, const char **argv)
5012 const char *db_name;
5013 struct ctdb_db_context *db;
5014 struct ctdb_transaction_handle *h;
5015 uint8_t db_flags;
5016 TDB_DATA key, data;
5017 int ret;
5019 if (argc != 2) {
5020 usage("pfetch");
5023 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5024 return 1;
5027 if (! (db_flags &
5028 (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED))) {
5029 fprintf(stderr, "Transactions not supported on DB %s\n",
5030 db_name);
5031 return 1;
5034 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5035 db_flags, &db);
5036 if (ret != 0) {
5037 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5038 return ret;
5041 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5042 if (ret != 0) {
5043 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5044 return ret;
5047 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
5048 TIMEOUT(), db, true, &h);
5049 if (ret != 0) {
5050 fprintf(stderr, "Failed to start transaction on db %s\n",
5051 db_name);
5052 return ret;
5055 ret = ctdb_transaction_fetch_record(h, key, mem_ctx, &data);
5056 if (ret != 0) {
5057 fprintf(stderr, "Failed to read record for key %s\n",
5058 argv[1]);
5059 ctdb_transaction_cancel(h);
5060 return ret;
5063 printf("%.*s\n", (int)data.dsize, data.dptr);
5065 ctdb_transaction_cancel(h);
5066 return 0;
5069 static int control_pstore(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5070 int argc, const char **argv)
5072 const char *db_name;
5073 struct ctdb_db_context *db;
5074 struct ctdb_transaction_handle *h;
5075 uint8_t db_flags;
5076 TDB_DATA key, data;
5077 int ret;
5079 if (argc != 3) {
5080 usage("pstore");
5083 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5084 return 1;
5087 if (! (db_flags &
5088 (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED))) {
5089 fprintf(stderr, "Transactions not supported on DB %s\n",
5090 db_name);
5091 return 1;
5094 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5095 db_flags, &db);
5096 if (ret != 0) {
5097 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5098 return ret;
5101 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5102 if (ret != 0) {
5103 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5104 return ret;
5107 ret = str_to_data(argv[2], strlen(argv[2]), mem_ctx, &data);
5108 if (ret != 0) {
5109 fprintf(stderr, "Failed to parse value %s\n", argv[2]);
5110 return ret;
5113 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
5114 TIMEOUT(), db, false, &h);
5115 if (ret != 0) {
5116 fprintf(stderr, "Failed to start transaction on db %s\n",
5117 db_name);
5118 return ret;
5121 ret = ctdb_transaction_store_record(h, key, data);
5122 if (ret != 0) {
5123 fprintf(stderr, "Failed to store record for key %s\n",
5124 argv[1]);
5125 ctdb_transaction_cancel(h);
5126 return ret;
5129 ret = ctdb_transaction_commit(h);
5130 if (ret != 0) {
5131 fprintf(stderr, "Failed to commit transaction on db %s\n",
5132 db_name);
5133 ctdb_transaction_cancel(h);
5134 return ret;
5137 return 0;
5140 static int control_pdelete(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5141 int argc, const char **argv)
5143 const char *db_name;
5144 struct ctdb_db_context *db;
5145 struct ctdb_transaction_handle *h;
5146 uint8_t db_flags;
5147 TDB_DATA key;
5148 int ret;
5150 if (argc != 2) {
5151 usage("pdelete");
5154 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5155 return 1;
5158 if (! (db_flags &
5159 (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED))) {
5160 fprintf(stderr, "Transactions not supported on DB %s\n",
5161 db_name);
5162 return 1;
5165 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5166 db_flags, &db);
5167 if (ret != 0) {
5168 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5169 return ret;
5172 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5173 if (ret != 0) {
5174 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5175 return ret;
5178 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
5179 TIMEOUT(), db, false, &h);
5180 if (ret != 0) {
5181 fprintf(stderr, "Failed to start transaction on db %s\n",
5182 db_name);
5183 return ret;
5186 ret = ctdb_transaction_delete_record(h, key);
5187 if (ret != 0) {
5188 fprintf(stderr, "Failed to delete record for key %s\n",
5189 argv[1]);
5190 ctdb_transaction_cancel(h);
5191 return ret;
5194 ret = ctdb_transaction_commit(h);
5195 if (ret != 0) {
5196 fprintf(stderr, "Failed to commit transaction on db %s\n",
5197 db_name);
5198 ctdb_transaction_cancel(h);
5199 return ret;
5202 return 0;
5205 static int ptrans_parse_string(TALLOC_CTX *mem_ctx, const char **ptr, TDB_DATA *data)
5207 const char *t;
5208 size_t n;
5209 int ret;
5211 *data = tdb_null;
5213 /* Skip whitespace */
5214 n = strspn(*ptr, " \t");
5215 t = *ptr + n;
5217 if (t[0] == '"') {
5218 /* Quoted ASCII string - no wide characters! */
5219 t++;
5220 n = strcspn(t, "\"");
5221 if (t[n] == '"') {
5222 if (n > 0) {
5223 ret = str_to_data(t, n, mem_ctx, data);
5224 if (ret != 0) {
5225 return ret;
5228 *ptr = t + n + 1;
5229 } else {
5230 fprintf(stderr, "Unmatched \" in input %s\n", *ptr);
5231 return 1;
5233 } else {
5234 fprintf(stderr, "Unsupported input format in %s\n", *ptr);
5235 return 1;
5238 return 0;
5241 #define MAX_LINE_SIZE 1024
5243 static bool ptrans_get_key_value(TALLOC_CTX *mem_ctx, FILE *file,
5244 TDB_DATA *key, TDB_DATA *value)
5246 char line [MAX_LINE_SIZE]; /* FIXME: make this more flexible? */
5247 const char *ptr;
5248 int ret;
5250 ptr = fgets(line, MAX_LINE_SIZE, file);
5251 if (ptr == NULL) {
5252 return false;
5255 /* Get key */
5256 ret = ptrans_parse_string(mem_ctx, &ptr, key);
5257 if (ret != 0 || ptr == NULL || key->dptr == NULL) {
5258 /* Line Ignored but not EOF */
5259 *key = tdb_null;
5260 return true;
5263 /* Get value */
5264 ret = ptrans_parse_string(mem_ctx, &ptr, value);
5265 if (ret != 0) {
5266 /* Line Ignored but not EOF */
5267 talloc_free(key->dptr);
5268 *key = tdb_null;
5269 return true;
5272 return true;
5275 static int control_ptrans(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5276 int argc, const char **argv)
5278 const char *db_name;
5279 struct ctdb_db_context *db;
5280 struct ctdb_transaction_handle *h;
5281 uint8_t db_flags;
5282 FILE *file;
5283 TDB_DATA key = tdb_null, value = tdb_null;
5284 int ret;
5286 if (argc < 1 || argc > 2) {
5287 usage("ptrans");
5290 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5291 return 1;
5294 if (! (db_flags &
5295 (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED))) {
5296 fprintf(stderr, "Transactions not supported on DB %s\n",
5297 db_name);
5298 return 1;
5301 if (argc == 2) {
5302 file = fopen(argv[1], "r");
5303 if (file == NULL) {
5304 fprintf(stderr, "Failed to open file %s\n", argv[1]);
5305 return 1;
5307 } else {
5308 file = stdin;
5311 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5312 db_flags, &db);
5313 if (ret != 0) {
5314 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5315 goto done;
5318 ret = ctdb_transaction_start(mem_ctx, ctdb->ev, ctdb->client,
5319 TIMEOUT(), db, false, &h);
5320 if (ret != 0) {
5321 fprintf(stderr, "Failed to start transaction on db %s\n",
5322 db_name);
5323 goto done;
5326 while (ptrans_get_key_value(mem_ctx, file, &key, &value)) {
5327 if (key.dsize != 0) {
5328 ret = ctdb_transaction_store_record(h, key, value);
5329 if (ret != 0) {
5330 fprintf(stderr, "Failed to store record\n");
5331 ctdb_transaction_cancel(h);
5332 goto done;
5334 talloc_free(key.dptr);
5335 talloc_free(value.dptr);
5339 ret = ctdb_transaction_commit(h);
5340 if (ret != 0) {
5341 fprintf(stderr, "Failed to commit transaction on db %s\n",
5342 db_name);
5343 ctdb_transaction_cancel(h);
5346 done:
5347 if (file != stdin) {
5348 fclose(file);
5350 return ret;
5353 static int control_tfetch(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5354 int argc, const char **argv)
5356 struct tdb_context *tdb;
5357 TDB_DATA key, data;
5358 struct ctdb_ltdb_header header;
5359 int ret;
5361 if (argc < 2 || argc > 3) {
5362 usage("tfetch");
5365 tdb = tdb_open(argv[0], 0, 0, O_RDWR, 0);
5366 if (tdb == NULL) {
5367 fprintf(stderr, "Failed to open TDB file %s\n", argv[0]);
5368 return 1;
5371 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5372 if (ret != 0) {
5373 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5374 tdb_close(tdb);
5375 return ret;
5378 data = tdb_fetch(tdb, key);
5379 if (data.dptr == NULL) {
5380 fprintf(stderr, "No record for key %s\n", argv[1]);
5381 tdb_close(tdb);
5382 return 1;
5385 if (data.dsize < sizeof(struct ctdb_ltdb_header)) {
5386 fprintf(stderr, "Invalid record for key %s\n", argv[1]);
5387 tdb_close(tdb);
5388 return 1;
5391 tdb_close(tdb);
5393 if (argc == 3) {
5394 int fd;
5395 ssize_t nwritten;
5397 fd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0600);
5398 if (fd == -1) {
5399 fprintf(stderr, "Failed to open output file %s\n",
5400 argv[2]);
5401 goto fail;
5404 nwritten = sys_write(fd, data.dptr, data.dsize);
5405 if (nwritten == -1 ||
5406 (size_t)nwritten != data.dsize) {
5407 fprintf(stderr, "Failed to write record to file\n");
5408 close(fd);
5409 goto fail;
5412 close(fd);
5415 fail:
5416 ret = ctdb_ltdb_header_extract(&data, &header);
5417 if (ret != 0) {
5418 fprintf(stderr, "Failed to parse header from data\n");
5419 return 1;
5422 dump_ltdb_header(&header);
5423 dump_tdb_data("data", data);
5425 return 0;
5428 static int control_tstore(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5429 int argc, const char **argv)
5431 struct tdb_context *tdb;
5432 TDB_DATA key, data[2], value;
5433 struct ctdb_ltdb_header header;
5434 uint8_t header_buf[sizeof(struct ctdb_ltdb_header)];
5435 size_t np;
5436 int ret = 0;
5438 if (argc < 3 || argc > 5) {
5439 usage("tstore");
5442 tdb = tdb_open(argv[0], 0, 0, O_RDWR, 0);
5443 if (tdb == NULL) {
5444 fprintf(stderr, "Failed to open TDB file %s\n", argv[0]);
5445 return 1;
5448 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5449 if (ret != 0) {
5450 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5451 tdb_close(tdb);
5452 return ret;
5455 ret = str_to_data(argv[2], strlen(argv[2]), mem_ctx, &value);
5456 if (ret != 0) {
5457 fprintf(stderr, "Failed to parse value %s\n", argv[2]);
5458 tdb_close(tdb);
5459 return ret;
5462 ZERO_STRUCT(header);
5464 if (argc > 3) {
5465 header.rsn = (uint64_t)smb_strtoull(argv[3],
5466 NULL,
5468 &ret,
5469 SMB_STR_STANDARD);
5470 if (ret != 0) {
5471 return ret;
5474 if (argc > 4) {
5475 header.dmaster = (uint32_t)atol(argv[4]);
5477 if (argc > 5) {
5478 header.flags = (uint32_t)atol(argv[5]);
5481 ctdb_ltdb_header_push(&header, header_buf, &np);
5483 data[0].dsize = np;
5484 data[0].dptr = header_buf;
5486 data[1].dsize = value.dsize;
5487 data[1].dptr = value.dptr;
5489 ret = tdb_storev(tdb, key, data, 2, TDB_REPLACE);
5490 if (ret != 0) {
5491 fprintf(stderr, "Failed to write record %s to file %s\n",
5492 argv[1], argv[0]);
5495 tdb_close(tdb);
5497 return ret;
5500 static int control_readkey(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5501 int argc, const char **argv)
5503 const char *db_name;
5504 struct ctdb_db_context *db;
5505 struct ctdb_record_handle *h;
5506 uint8_t db_flags;
5507 TDB_DATA key, data;
5508 bool readonly = false;
5509 int ret;
5511 if (argc < 2 || argc > 3) {
5512 usage("readkey");
5515 if (argc == 3) {
5516 if (strcmp(argv[2], "readonly") == 0) {
5517 readonly = true;
5518 } else {
5519 usage("readkey");
5523 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5524 return 1;
5527 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
5528 fprintf(stderr, "DB %s is not a volatile database\n",
5529 db_name);
5530 return 1;
5533 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5534 db_flags, &db);
5535 if (ret != 0) {
5536 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5537 return ret;
5540 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5541 if (ret != 0) {
5542 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5543 return ret;
5546 ret = ctdb_fetch_lock(mem_ctx, ctdb->ev, ctdb->client,
5547 db, key, readonly, &h, NULL, &data);
5548 if (ret != 0) {
5549 fprintf(stderr, "Failed to read record for key %s\n",
5550 argv[1]);
5551 } else {
5552 printf("Data: size:%zu ptr:[%.*s]\n", data.dsize,
5553 (int)data.dsize, data.dptr);
5556 talloc_free(h);
5557 return ret;
5560 static int control_writekey(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5561 int argc, const char **argv)
5563 const char *db_name;
5564 struct ctdb_db_context *db;
5565 struct ctdb_record_handle *h;
5566 uint8_t db_flags;
5567 TDB_DATA key, data;
5568 int ret;
5570 if (argc != 3) {
5571 usage("writekey");
5574 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5575 return 1;
5578 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
5579 fprintf(stderr, "DB %s is not a volatile database\n",
5580 db_name);
5581 return 1;
5584 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5585 db_flags, &db);
5586 if (ret != 0) {
5587 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5588 return ret;
5591 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5592 if (ret != 0) {
5593 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5594 return ret;
5597 ret = str_to_data(argv[2], strlen(argv[2]), mem_ctx, &data);
5598 if (ret != 0) {
5599 fprintf(stderr, "Failed to parse value %s\n", argv[2]);
5600 return ret;
5603 ret = ctdb_fetch_lock(mem_ctx, ctdb->ev, ctdb->client,
5604 db, key, false, &h, NULL, NULL);
5605 if (ret != 0) {
5606 fprintf(stderr, "Failed to lock record for key %s\n", argv[0]);
5607 return ret;
5610 ret = ctdb_store_record(h, data);
5611 if (ret != 0) {
5612 fprintf(stderr, "Failed to store record for key %s\n",
5613 argv[1]);
5616 talloc_free(h);
5617 return ret;
5620 static int control_deletekey(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5621 int argc, const char **argv)
5623 const char *db_name;
5624 struct ctdb_db_context *db;
5625 struct ctdb_record_handle *h;
5626 uint8_t db_flags;
5627 TDB_DATA key, data;
5628 int ret;
5630 if (argc != 2) {
5631 usage("deletekey");
5634 if (! db_exists(mem_ctx, ctdb, argv[0], NULL, &db_name, &db_flags)) {
5635 return 1;
5638 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
5639 fprintf(stderr, "DB %s is not a volatile database\n",
5640 db_name);
5641 return 1;
5644 ret = ctdb_attach(ctdb->ev, ctdb->client, TIMEOUT(), db_name,
5645 db_flags, &db);
5646 if (ret != 0) {
5647 fprintf(stderr, "Failed to attach to DB %s\n", db_name);
5648 return ret;
5651 ret = str_to_data(argv[1], strlen(argv[1]), mem_ctx, &key);
5652 if (ret != 0) {
5653 fprintf(stderr, "Failed to parse key %s\n", argv[1]);
5654 return ret;
5657 ret = ctdb_fetch_lock(mem_ctx, ctdb->ev, ctdb->client,
5658 db, key, false, &h, NULL, &data);
5659 if (ret != 0) {
5660 fprintf(stderr, "Failed to fetch record for key %s\n",
5661 argv[1]);
5662 return ret;
5665 ret = ctdb_delete_record(h);
5666 if (ret != 0) {
5667 fprintf(stderr, "Failed to delete record for key %s\n",
5668 argv[1]);
5671 talloc_free(h);
5672 return ret;
5675 static int control_checktcpport(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5676 int argc, const char **argv)
5678 struct sockaddr_in sin;
5679 unsigned int port;
5680 int s, v;
5681 int ret;
5683 if (argc != 1) {
5684 usage("chktcpport");
5687 port = atoi(argv[0]);
5689 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
5690 if (s == -1) {
5691 fprintf(stderr, "Failed to open local socket\n");
5692 return errno;
5695 v = fcntl(s, F_GETFL, 0);
5696 if (v == -1 || fcntl(s, F_SETFL, v | O_NONBLOCK)) {
5697 fprintf(stderr, "Unable to set socket non-blocking\n");
5698 close(s);
5699 return errno;
5702 bzero(&sin, sizeof(sin));
5703 sin.sin_family = AF_INET;
5704 sin.sin_port = htons(port);
5705 ret = bind(s, (struct sockaddr *)&sin, sizeof(sin));
5706 close(s);
5707 if (ret == -1) {
5708 fprintf(stderr, "Failed to bind to TCP port %u\n", port);
5709 return errno;
5712 return 0;
5715 static int control_getdbseqnum(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5716 int argc, const char **argv)
5718 uint32_t db_id;
5719 const char *db_name;
5720 uint64_t seqnum;
5721 int ret;
5723 if (argc != 1) {
5724 usage("getdbseqnum");
5727 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, NULL)) {
5728 return 1;
5731 ret = ctdb_ctrl_get_db_seqnum(mem_ctx, ctdb->ev, ctdb->client,
5732 ctdb->cmd_pnn, TIMEOUT(), db_id,
5733 &seqnum);
5734 if (ret != 0) {
5735 fprintf(stderr, "Failed to get sequence number for DB %s\n",
5736 db_name);
5737 return ret;
5740 printf("0x%"PRIx64"\n", seqnum);
5741 return 0;
5744 static int control_nodestatus(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5745 int argc, const char **argv)
5747 const char *nodestring = NULL;
5748 struct ctdb_node_map *nodemap_in;
5749 struct ctdb_node_map *nodemap;
5750 unsigned int i;
5751 int ret;
5752 bool print_hdr = false;
5754 if (argc > 1) {
5755 usage("nodestatus");
5758 if (argc == 1) {
5759 nodestring = argv[0];
5760 if (strcmp(nodestring, "all") == 0) {
5761 print_hdr = true;
5765 if (! parse_nodestring(mem_ctx, ctdb, nodestring, &nodemap_in)) {
5766 return 1;
5769 nodemap = get_nodemap_unknown(mem_ctx, ctdb, nodemap_in);
5770 if (nodemap == NULL) {
5771 return 1;
5774 if (options.machinereadable) {
5775 print_nodemap_machine(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn);
5776 } else {
5777 print_nodemap(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn, print_hdr);
5780 ret = 0;
5781 for (i=0; i<nodemap->num; i++) {
5782 uint32_t flags = nodemap->node[i].flags;
5784 if ((flags & NODE_FLAGS_DELETED) != 0) {
5785 continue;
5788 ret |= flags;
5791 return ret;
5794 const struct {
5795 const char *name;
5796 uint32_t offset;
5797 } db_stats_fields[] = {
5798 #define DBSTATISTICS_FIELD(n) { #n, offsetof(struct ctdb_db_statistics, n) }
5799 DBSTATISTICS_FIELD(db_ro_delegations),
5800 DBSTATISTICS_FIELD(db_ro_revokes),
5801 DBSTATISTICS_FIELD(locks.num_calls),
5802 DBSTATISTICS_FIELD(locks.num_current),
5803 DBSTATISTICS_FIELD(locks.num_pending),
5804 DBSTATISTICS_FIELD(locks.num_failed),
5807 static void print_dbstatistics(const char *db_name,
5808 struct ctdb_db_statistics *s)
5810 size_t i;
5811 const char *prefix = NULL;
5812 int preflen = 0;
5814 printf("DB Statistics %s\n", db_name);
5816 for (i=0; i<ARRAY_SIZE(db_stats_fields); i++) {
5817 if (strchr(db_stats_fields[i].name, '.') != NULL) {
5818 preflen = strcspn(db_stats_fields[i].name, ".") + 1;
5819 if (! prefix ||
5820 strncmp(prefix, db_stats_fields[i].name, preflen) != 0) {
5821 prefix = db_stats_fields[i].name;
5822 printf(" %*.*s\n", preflen-1, preflen-1,
5823 db_stats_fields[i].name);
5825 } else {
5826 preflen = 0;
5828 printf(" %*s%-22s%*s%10u\n", preflen ? 4 : 0, "",
5829 db_stats_fields[i].name+preflen, preflen ? 0 : 4, "",
5830 *(uint32_t *)(db_stats_fields[i].offset+(uint8_t *)s));
5833 printf(" hop_count_buckets:");
5834 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
5835 printf(" %d", s->hop_count_bucket[i]);
5837 printf("\n");
5839 printf(" lock_buckets:");
5840 for (i=0; i<MAX_COUNT_BUCKETS; i++) {
5841 printf(" %d", s->locks.buckets[i]);
5843 printf("\n");
5845 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
5846 "locks_latency MIN/AVG/MAX",
5847 s->locks.latency.min, LATENCY_AVG(s->locks.latency),
5848 s->locks.latency.max, s->locks.latency.num);
5850 printf(" %-30s %.6f/%.6f/%.6f sec out of %d\n",
5851 "vacuum_latency MIN/AVG/MAX",
5852 s->vacuum.latency.min, LATENCY_AVG(s->vacuum.latency),
5853 s->vacuum.latency.max, s->vacuum.latency.num);
5855 printf(" Num Hot Keys: %d\n", s->num_hot_keys);
5856 for (i=0; i<s->num_hot_keys; i++) {
5857 size_t j;
5858 printf(" Count:%d Key:", s->hot_keys[i].count);
5859 for (j=0; j<s->hot_keys[i].key.dsize; j++) {
5860 printf("%02x", s->hot_keys[i].key.dptr[j] & 0xff);
5862 printf("\n");
5866 static int control_dbstatistics(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
5867 int argc, const char **argv)
5869 uint32_t db_id;
5870 const char *db_name;
5871 struct ctdb_db_statistics *dbstats;
5872 int ret;
5874 if (argc != 1) {
5875 usage("dbstatistics");
5878 if (! db_exists(mem_ctx, ctdb, argv[0], &db_id, &db_name, NULL)) {
5879 return 1;
5882 ret = ctdb_ctrl_get_db_statistics(mem_ctx, ctdb->ev, ctdb->client,
5883 ctdb->cmd_pnn, TIMEOUT(), db_id,
5884 &dbstats);
5885 if (ret != 0) {
5886 fprintf(stderr, "Failed to get statistics for DB %s\n",
5887 db_name);
5888 return ret;
5891 print_dbstatistics(db_name, dbstats);
5892 return 0;
5895 struct disable_takeover_runs_state {
5896 uint32_t *pnn_list;
5897 unsigned int node_count;
5898 bool *reply;
5899 int status;
5900 bool done;
5903 static void disable_takeover_run_handler(uint64_t srvid, TDB_DATA data,
5904 void *private_data)
5906 struct disable_takeover_runs_state *state =
5907 (struct disable_takeover_runs_state *)private_data;
5908 unsigned int i;
5909 int ret;
5911 if (data.dsize != sizeof(int)) {
5912 /* Ignore packet */
5913 return;
5916 /* ret will be a PNN (i.e. >=0) on success, or negative on error */
5917 ret = *(int *)data.dptr;
5918 if (ret < 0) {
5919 state->status = ret;
5920 state->done = true;
5921 return;
5923 for (i=0; i<state->node_count; i++) {
5924 if (state->pnn_list[i] == (uint32_t)ret) {
5925 state->reply[i] = true;
5926 break;
5930 state->done = true;
5931 for (i=0; i<state->node_count; i++) {
5932 if (! state->reply[i]) {
5933 state->done = false;
5934 break;
5939 static int disable_takeover_runs(TALLOC_CTX *mem_ctx,
5940 struct ctdb_context *ctdb, uint32_t timeout,
5941 uint32_t *pnn_list, int count)
5943 struct ctdb_disable_message disable = { 0 };
5944 struct disable_takeover_runs_state state;
5945 int ret, i;
5947 disable.pnn = ctdb->pnn;
5948 disable.srvid = next_srvid(ctdb);
5949 disable.timeout = timeout;
5951 state.pnn_list = pnn_list;
5952 state.node_count = count;
5953 state.done = false;
5954 state.status = 0;
5955 state.reply = talloc_zero_array(mem_ctx, bool, count);
5956 if (state.reply == NULL) {
5957 return ENOMEM;
5960 ret = ctdb_client_set_message_handler(ctdb->ev, ctdb->client,
5961 disable.srvid,
5962 disable_takeover_run_handler,
5963 &state);
5964 if (ret != 0) {
5965 return ret;
5968 for (i=0; i<count; i++) {
5969 ret = ctdb_message_disable_takeover_runs(mem_ctx, ctdb->ev,
5970 ctdb->client,
5971 pnn_list[i],
5972 &disable);
5973 if (ret != 0) {
5974 goto fail;
5978 ret = ctdb_client_wait_timeout(ctdb->ev, &state.done, TIMEOUT());
5979 if (ret == ETIME) {
5980 fprintf(stderr, "Timed out waiting to disable takeover runs\n");
5981 } else {
5982 ret = (state.status >= 0 ? 0 : 1);
5985 fail:
5986 ctdb_client_remove_message_handler(ctdb->ev, ctdb->client,
5987 disable.srvid, &state);
5988 return ret;
5991 static int send_ipreallocated_control_to_nodes(TALLOC_CTX *mem_ctx,
5992 struct ctdb_context *ctdb,
5993 uint32_t *pnn_list,
5994 int count)
5996 struct ctdb_req_control request;
5997 int ret;
5999 ctdb_req_control_ipreallocated(&request);
6000 ret = ctdb_client_control_multi(mem_ctx,
6001 ctdb->ev,
6002 ctdb->client,
6003 pnn_list,
6004 count,
6005 TIMEOUT(),
6006 &request,
6007 NULL, /* perr_list */
6008 NULL); /* preply */
6009 if (ret != 0) {
6010 fprintf(stderr, "Failed to send ipreallocated\n");
6011 return ret;
6014 return 0;
6017 static int control_reloadips(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
6018 int argc, const char **argv)
6020 const char *nodestring = NULL;
6021 struct ctdb_node_map *nodemap, *nodemap2;
6022 struct ctdb_req_control request;
6023 uint32_t *pnn_list, *pnn_list2;
6024 int ret, count, count2;
6026 if (argc > 1) {
6027 usage("reloadips");
6030 if (argc == 1) {
6031 nodestring = argv[0];
6034 nodemap = get_nodemap(ctdb, false);
6035 if (nodemap == NULL) {
6036 return 1;
6039 if (! parse_nodestring(mem_ctx, ctdb, nodestring, &nodemap2)) {
6040 return 1;
6043 count = list_of_connected_nodes(nodemap, CTDB_UNKNOWN_PNN,
6044 mem_ctx, &pnn_list);
6045 if (count <= 0) {
6046 fprintf(stderr, "Memory allocation error\n");
6047 return 1;
6050 count2 = list_of_active_nodes(nodemap2, CTDB_UNKNOWN_PNN,
6051 mem_ctx, &pnn_list2);
6052 if (count2 <= 0) {
6053 fprintf(stderr, "Memory allocation error\n");
6054 return 1;
6057 /* Disable takeover runs on all connected nodes. A reply
6058 * indicating success is needed from each node so all nodes
6059 * will need to be active.
6061 * A check could be added to not allow reloading of IPs when
6062 * there are disconnected nodes. However, this should
6063 * probably be left up to the administrator.
6065 ret = disable_takeover_runs(mem_ctx, ctdb, 2*options.timelimit,
6066 pnn_list, count);
6067 if (ret != 0) {
6068 fprintf(stderr, "Failed to disable takeover runs\n");
6069 return ret;
6072 /* Now tell all the desired nodes to reload their public IPs.
6073 * Keep trying this until it succeeds. This assumes all
6074 * failures are transient, which might not be true...
6076 ctdb_req_control_reload_public_ips(&request);
6077 ret = ctdb_client_control_multi(mem_ctx, ctdb->ev, ctdb->client,
6078 pnn_list2, count2, TIMEOUT(),
6079 &request, NULL, NULL);
6080 if (ret != 0) {
6081 fprintf(stderr, "Failed to reload IPs on some nodes.\n");
6084 /* It isn't strictly necessary to wait until takeover runs are
6085 * re-enabled but doing so can't hurt.
6087 ret = disable_takeover_runs(mem_ctx, ctdb, 0, pnn_list, count);
6088 if (ret != 0) {
6089 fprintf(stderr, "Failed to enable takeover runs\n");
6090 return ret;
6093 return ipreallocate(mem_ctx, ctdb);
6097 static const struct ctdb_cmd {
6098 const char *name;
6099 int (*fn)(TALLOC_CTX *, struct ctdb_context *, int, const char **);
6100 bool without_daemon; /* can be run without daemon running ? */
6101 bool remote; /* can be run on remote nodes */
6102 const char *msg;
6103 const char *args;
6104 } ctdb_commands[] = {
6105 { "version", control_version, true, false,
6106 "show version of ctdb", NULL },
6107 { "status", control_status, false, true,
6108 "show node status", NULL },
6109 { "uptime", control_uptime, false, true,
6110 "show node uptime", NULL },
6111 { "ping", control_ping, false, true,
6112 "ping a node", NULL },
6113 { "runstate", control_runstate, false, true,
6114 "get/check runstate of a node",
6115 "[setup|first_recovery|startup|running]" },
6116 { "getvar", control_getvar, false, true,
6117 "get a tunable variable", "<name>" },
6118 { "setvar", control_setvar, false, true,
6119 "set a tunable variable", "<name> <value>" },
6120 { "listvars", control_listvars, false, true,
6121 "list tunable variables", NULL },
6122 { "statistics", control_statistics, false, true,
6123 "show ctdb statistics", NULL },
6124 { "statisticsreset", control_statistics_reset, false, true,
6125 "reset ctdb statistics", NULL },
6126 { "stats", control_stats, false, true,
6127 "show rolling statistics", "[count]" },
6128 { "ip", control_ip, false, true,
6129 "show public ips", "[all]" },
6130 { "ipinfo", control_ipinfo, false, true,
6131 "show public ip details", "<ip>" },
6132 { "ifaces", control_ifaces, false, true,
6133 "show interfaces", NULL },
6134 { "setifacelink", control_setifacelink, false, true,
6135 "set interface link status", "<iface> up|down" },
6136 { "process-exists", control_process_exists, false, true,
6137 "check if a process exists on a node", "<pid> [<srvid>]" },
6138 { "getdbmap", control_getdbmap, false, true,
6139 "show attached databases", NULL },
6140 { "getdbstatus", control_getdbstatus, false, true,
6141 "show database status", "<dbname|dbid>" },
6142 { "catdb", control_catdb, false, false,
6143 "dump cluster-wide ctdb database", "<dbname|dbid>" },
6144 { "cattdb", control_cattdb, false, false,
6145 "dump local ctdb database", "<dbname|dbid>" },
6146 { "getcapabilities", control_getcapabilities, false, true,
6147 "show node capabilities", NULL },
6148 { "pnn", control_pnn, false, false,
6149 "show the pnn of the current node", NULL },
6150 { "lvs", control_lvs, false, false,
6151 "show lvs configuration", "leader|list|status" },
6152 { "setdebug", control_setdebug, false, true,
6153 "set debug level", "ERROR|WARNING|NOTICE|INFO|DEBUG" },
6154 { "getdebug", control_getdebug, false, true,
6155 "get debug level", NULL },
6156 { "attach", control_attach, false, false,
6157 "attach a database", "<dbname> [persistent|replicated]" },
6158 { "detach", control_detach, false, false,
6159 "detach database(s)", "<dbname|dbid> ..." },
6160 { "dumpmemory", control_dumpmemory, false, true,
6161 "dump ctdbd memory map", NULL },
6162 { "rddumpmemory", control_rddumpmemory, false, true,
6163 "dump recoverd memory map", NULL },
6164 { "getpid", control_getpid, false, true,
6165 "get ctdbd process ID", NULL },
6166 { "disable", control_disable, false, true,
6167 "disable a node", NULL },
6168 { "enable", control_enable, false, true,
6169 "enable a node", NULL },
6170 { "stop", control_stop, false, true,
6171 "stop a node", NULL },
6172 { "continue", control_continue, false, true,
6173 "continue a stopped node", NULL },
6174 { "ban", control_ban, false, true,
6175 "ban a node", "<bantime>"},
6176 { "unban", control_unban, false, true,
6177 "unban a node", NULL },
6178 { "shutdown", control_shutdown, false, true,
6179 "shutdown ctdb daemon", NULL },
6180 { "recover", control_recover, false, true,
6181 "force recovery", NULL },
6182 { "sync", control_ipreallocate, false, true,
6183 "run ip reallocation (deprecated)", NULL },
6184 { "ipreallocate", control_ipreallocate, false, true,
6185 "run ip reallocation", NULL },
6186 { "gratarp", control_gratarp, false, true,
6187 "send a gratuitous arp", "<ip> <interface>" },
6188 { "tickle", control_tickle, true, false,
6189 "send a tcp tickle ack", "<srcip:port> <dstip:port>" },
6190 { "gettickles", control_gettickles, false, true,
6191 "get the list of tickles", "<ip> [<port>]" },
6192 { "addtickle", control_addtickle, false, true,
6193 "add a tickle", "<ip>:<port> <ip>:<port>" },
6194 { "deltickle", control_deltickle, false, true,
6195 "delete a tickle", "<ip>:<port> <ip>:<port>" },
6196 { "listnodes", control_listnodes, true, true,
6197 "list nodes in the cluster", NULL },
6198 { "reloadnodes", control_reloadnodes, false, false,
6199 "reload the nodes file all nodes", NULL },
6200 { "moveip", control_moveip, false, false,
6201 "move an ip address to another node", "<ip> <node>" },
6202 { "addip", control_addip, false, true,
6203 "add an ip address to a node", "<ip/mask> <iface>" },
6204 { "delip", control_delip, false, true,
6205 "delete an ip address from a node", "<ip>" },
6206 { "backupdb", control_backupdb, false, false,
6207 "backup a database into a file", "<dbname|dbid> <file>" },
6208 { "restoredb", control_restoredb, false, false,
6209 "restore a database from a file", "<file> [dbname]" },
6210 { "dumpdbbackup", control_dumpdbbackup, true, false,
6211 "dump database from a backup file", "<file>" },
6212 { "wipedb", control_wipedb, false, false,
6213 "wipe the contents of a database.", "<dbname|dbid>"},
6214 { "leader", control_leader, false, true,
6215 "show the pnn of the leader", NULL },
6216 { "event", control_event, true, false,
6217 "event and event script commands", NULL },
6218 { "scriptstatus", control_scriptstatus, true, false,
6219 "show event script status",
6220 "[init|setup|startup|monitor|takeip|releaseip|ipreallocated]" },
6221 { "natgw", control_natgw, false, false,
6222 "show natgw configuration", "leader|list|status" },
6223 { "getreclock", control_getreclock, false, true,
6224 "get recovery lock file", NULL },
6225 { "setlmasterrole", control_setlmasterrole, false, true,
6226 "set LMASTER role", "on|off" },
6227 { "setleaderrole", control_setleaderrole, false, true,
6228 "set LEADER role", "on|off"},
6229 { "setdbreadonly", control_setdbreadonly, false, true,
6230 "enable readonly records", "<dbname|dbid>" },
6231 { "setdbsticky", control_setdbsticky, false, true,
6232 "enable sticky records", "<dbname|dbid>"},
6233 { "pfetch", control_pfetch, false, false,
6234 "fetch record from persistent database", "<dbname|dbid> <key>" },
6235 { "pstore", control_pstore, false, false,
6236 "write record to persistent database", "<dbname|dbid> <key> <value>" },
6237 { "pdelete", control_pdelete, false, false,
6238 "delete record from persistent database", "<dbname|dbid> <key>" },
6239 { "ptrans", control_ptrans, false, false,
6240 "update a persistent database (from file or stdin)", "<dbname|dbid> [<file>]" },
6241 { "tfetch", control_tfetch, false, true,
6242 "fetch a record", "<tdb-file> <key> [<file>]" },
6243 { "tstore", control_tstore, false, true,
6244 "store a record", "<tdb-file> <key> <data> [<rsn> <dmaster> <flags>]" },
6245 { "readkey", control_readkey, false, false,
6246 "read value of a database key", "<dbname|dbid> <key> [readonly]" },
6247 { "writekey", control_writekey, false, false,
6248 "write value for a database key", "<dbname|dbid> <key> <value>" },
6249 { "deletekey", control_deletekey, false, false,
6250 "delete a database key", "<dbname|dbid> <key>" },
6251 { "checktcpport", control_checktcpport, true, false,
6252 "check if a service is bound to a specific tcp port or not", "<port>" },
6253 { "getdbseqnum", control_getdbseqnum, false, false,
6254 "get database sequence number", "<dbname|dbid>" },
6255 { "nodestatus", control_nodestatus, false, true,
6256 "show and return node status", "[all|<pnn-list>]" },
6257 { "dbstatistics", control_dbstatistics, false, true,
6258 "show database statistics", "<dbname|dbid>" },
6259 { "reloadips", control_reloadips, false, false,
6260 "reload the public addresses file", "[all|<pnn-list>]" },
6263 static const struct ctdb_cmd *match_command(const char *command)
6265 const struct ctdb_cmd *cmd;
6266 size_t i;
6268 for (i=0; i<ARRAY_SIZE(ctdb_commands); i++) {
6269 cmd = &ctdb_commands[i];
6270 if (strcmp(command, cmd->name) == 0) {
6271 return cmd;
6275 return NULL;
6280 * Show usage message
6282 static void usage_full(void)
6284 size_t i;
6286 poptPrintHelp(pc, stdout, 0);
6287 printf("\nCommands:\n");
6288 for (i=0; i<ARRAY_SIZE(ctdb_commands); i++) {
6289 printf(" %-15s %-27s %s\n",
6290 ctdb_commands[i].name,
6291 ctdb_commands[i].args ? ctdb_commands[i].args : "",
6292 ctdb_commands[i].msg);
6296 static void usage(const char *command)
6298 const struct ctdb_cmd *cmd;
6300 if (command == NULL) {
6301 usage_full();
6302 exit(1);
6305 cmd = match_command(command);
6306 if (cmd == NULL) {
6307 usage_full();
6308 } else {
6309 poptPrintUsage(pc, stdout, 0);
6310 printf("\nCommands:\n");
6311 printf(" %-15s %-27s %s\n",
6312 cmd->name, cmd->args ? cmd->args : "", cmd->msg);
6315 exit(1);
6318 struct poptOption cmdline_options[] = {
6319 POPT_AUTOHELP
6321 .longName = "debug",
6322 .shortName = 'd',
6323 .argInfo = POPT_ARG_STRING,
6324 .arg = &options.debuglevelstr,
6325 .val = 0,
6326 .descrip = "debug level",
6329 .longName = "timelimit",
6330 .shortName = 't',
6331 .argInfo = POPT_ARG_INT,
6332 .arg = &options.timelimit,
6333 .val = 0,
6334 .descrip = "timelimit (in seconds)",
6337 .longName = "node",
6338 .shortName = 'n',
6339 .argInfo = POPT_ARG_INT,
6340 .arg = &options.pnn,
6341 .val = 0,
6342 .descrip = "node specification - integer",
6345 .longName = NULL,
6346 .shortName = 'Y',
6347 .argInfo = POPT_ARG_NONE,
6348 .arg = &options.machinereadable,
6349 .val = 0,
6350 .descrip = "enable machine readable output",
6353 .longName = "separator",
6354 .shortName = 'x',
6355 .argInfo = POPT_ARG_STRING,
6356 .arg = &options.sep,
6357 .val = 0,
6358 .descrip = "specify separator for machine readable output",
6359 .argDescrip = "CHAR",
6362 .shortName = 'X',
6363 .argInfo = POPT_ARG_NONE,
6364 .arg = &options.machineparsable,
6365 .val = 0,
6366 .descrip = "enable machine parsable output with separator |",
6369 .longName = "verbose",
6370 .shortName = 'v',
6371 .argInfo = POPT_ARG_NONE,
6372 .arg = &options.verbose,
6373 .val = 0,
6374 .descrip = "enable verbose output",
6377 .longName = "maxruntime",
6378 .shortName = 'T',
6379 .argInfo = POPT_ARG_INT,
6380 .arg = &options.maxruntime,
6381 .val = 0,
6382 .descrip = "die if runtime exceeds this limit (in seconds)",
6384 POPT_TABLEEND
6387 static int process_command(const struct ctdb_cmd *cmd, int argc,
6388 const char **argv)
6390 TALLOC_CTX *tmp_ctx;
6391 struct ctdb_context *ctdb;
6392 const char *ctdb_socket;
6393 int ret;
6394 bool status;
6395 uint64_t srvid_offset;
6397 tmp_ctx = talloc_new(NULL);
6398 if (tmp_ctx == NULL) {
6399 fprintf(stderr, "Memory allocation error\n");
6400 goto fail;
6403 if (cmd->without_daemon) {
6404 if (options.pnn != -1) {
6405 fprintf(stderr,
6406 "Cannot specify node for command %s\n",
6407 cmd->name);
6408 goto fail;
6411 ret = cmd->fn(tmp_ctx, NULL, argc-1, argv+1);
6412 talloc_free(tmp_ctx);
6413 return ret;
6416 ctdb = talloc_zero(tmp_ctx, struct ctdb_context);
6417 if (ctdb == NULL) {
6418 fprintf(stderr, "Memory allocation error\n");
6419 goto fail;
6422 ctdb->ev = tevent_context_init(ctdb);
6423 if (ctdb->ev == NULL) {
6424 fprintf(stderr, "Failed to initialize tevent\n");
6425 goto fail;
6428 ctdb_socket = path_socket(ctdb, "ctdbd");
6429 if (ctdb_socket == NULL) {
6430 fprintf(stderr, "Memory allocation error\n");
6431 goto fail;
6434 ret = ctdb_client_init(ctdb, ctdb->ev, ctdb_socket, &ctdb->client);
6435 if (ret != 0) {
6436 fprintf(stderr, "Failed to connect to CTDB daemon (%s)\n",
6437 ctdb_socket);
6439 if (!find_node_xpnn(ctdb, NULL)) {
6440 fprintf(stderr, "Is this node part of CTDB cluster?\n");
6442 goto fail;
6445 ctdb->pnn = ctdb_client_pnn(ctdb->client);
6446 srvid_offset = getpid() & 0xFFFF;
6447 ctdb->srvid = SRVID_CTDB_TOOL | (srvid_offset << 16);
6449 if (options.pnn != -1) {
6450 status = verify_pnn(ctdb, options.pnn);
6451 if (! status) {
6452 goto fail;
6455 ctdb->cmd_pnn = options.pnn;
6456 } else {
6457 ctdb->cmd_pnn = ctdb->pnn;
6460 if (! cmd->remote && ctdb->pnn != ctdb->cmd_pnn) {
6461 fprintf(stderr, "Node cannot be specified for command %s\n",
6462 cmd->name);
6463 goto fail;
6466 ctdb->leader_pnn = CTDB_UNKNOWN_PNN;
6467 ret = ctdb_client_set_message_handler(ctdb->ev,
6468 ctdb->client,
6469 CTDB_SRVID_LEADER,
6470 leader_handler,
6471 ctdb);
6472 if (ret != 0) {
6473 fprintf(stderr, "Failed to setup leader handler\n");
6474 goto fail;
6477 ret = cmd->fn(tmp_ctx, ctdb, argc-1, argv+1);
6478 talloc_free(tmp_ctx);
6479 return ret;
6481 fail:
6482 talloc_free(tmp_ctx);
6483 return 1;
6486 static void signal_handler(int sig)
6488 fprintf(stderr, "Maximum runtime exceeded - exiting\n");
6491 static void alarm_handler(int sig)
6493 /* Kill any child processes */
6494 signal(SIGTERM, signal_handler);
6495 kill(0, SIGTERM);
6497 _exit(1);
6500 int main(int argc, const char *argv[])
6502 int opt;
6503 const char **extra_argv;
6504 int extra_argc;
6505 const struct ctdb_cmd *cmd;
6506 const char *test_mode;
6507 int loglevel;
6508 bool ok;
6509 int ret = 0;
6511 setlinebuf(stdout);
6513 /* Set default options */
6514 options.debuglevelstr = NULL;
6515 options.timelimit = 10;
6516 options.sep = "|";
6517 options.maxruntime = 0;
6518 options.pnn = -1;
6520 pc = poptGetContext(argv[0], argc, argv, cmdline_options,
6521 POPT_CONTEXT_KEEP_FIRST);
6522 while ((opt = poptGetNextOpt(pc)) != -1) {
6523 fprintf(stderr, "Invalid option %s: %s\n",
6524 poptBadOption(pc, 0), poptStrerror(opt));
6525 exit(1);
6528 if (options.maxruntime == 0) {
6529 const char *ctdb_timeout;
6531 ctdb_timeout = getenv("CTDB_TIMEOUT");
6532 if (ctdb_timeout != NULL) {
6533 options.maxruntime = smb_strtoul(ctdb_timeout,
6534 NULL,
6536 &ret,
6537 SMB_STR_STANDARD);
6538 if (ret != 0) {
6539 fprintf(stderr, "Invalid value CTDB_TIMEOUT\n");
6540 exit(1);
6542 } else {
6543 options.maxruntime = 120;
6547 if (options.machineparsable) {
6548 options.machinereadable = 1;
6551 /* setup the remaining options for the commands */
6552 extra_argc = 0;
6553 extra_argv = poptGetArgs(pc);
6554 if (extra_argv) {
6555 extra_argv++;
6556 while (extra_argv[extra_argc]) extra_argc++;
6559 if (extra_argc < 1) {
6560 usage(NULL);
6563 cmd = match_command(extra_argv[0]);
6564 if (cmd == NULL) {
6565 fprintf(stderr, "Unknown command '%s'\n", extra_argv[0]);
6566 exit(1);
6569 /* Enable logging */
6570 setup_logging("ctdb", DEBUG_STDERR);
6571 ok = debug_level_parse(options.debuglevelstr, &loglevel);
6572 if (!ok) {
6573 loglevel = DEBUG_ERR;
6575 debuglevel_set(loglevel);
6577 /* Stop process group kill in alarm_handler() from killing tests */
6578 test_mode = getenv("CTDB_TEST_MODE");
6579 if (test_mode != NULL) {
6580 const char *have_setpgid = getenv("CTDB_TOOL_SETPGID");
6581 if (have_setpgid == NULL) {
6582 setpgid(0, 0);
6583 setenv("CTDB_TOOL_SETPGID", "1", 1);
6587 signal(SIGALRM, alarm_handler);
6588 alarm(options.maxruntime);
6590 ret = process_command(cmd, extra_argc, extra_argv);
6591 if (ret == -1) {
6592 ret = 1;
6595 (void)poptFreeContext(pc);
6597 return ret;