r26540: Revert my previous commit after concerns raised by Andrew.
[ira/wip.git] / source4 / cluster / ctdb / tools / ctdb.c
1 /* 
2    ctdb control tool
3
4    Copyright (C) Andrew Tridgell  2007
5
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.
10    
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.
15    
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/>.
18 */
19
20 #include "includes.h"
21 #include "lib/events/events.h"
22 #include "system/filesys.h"
23 #include "system/network.h"
24 #include "popt.h"
25 #include "cmdline.h"
26 #include "../include/ctdb.h"
27 #include "../include/ctdb_private.h"
28
29 static void usage(void);
30
31 static struct {
32         int timelimit;
33         uint32_t vnn;
34         int machinereadable;
35 } options;
36
37 #define TIMELIMIT() timeval_current_ofs(options.timelimit, 0)
38
39 /*
40   see if a process exists
41  */
42 static int control_process_exists(struct ctdb_context *ctdb, int argc, const char **argv)
43 {
44         uint32_t vnn, pid;
45         int ret;
46         if (argc < 1) {
47                 usage();
48         }
49
50         if (sscanf(argv[0], "%u:%u", &vnn, &pid) != 2) {
51                 printf("Badly formed vnn:pid\n");
52                 return -1;
53         }
54
55         ret = ctdb_ctrl_process_exists(ctdb, vnn, pid);
56         if (ret == 0) {
57                 printf("%u:%u exists\n", vnn, pid);
58         } else {
59                 printf("%u:%u does not exist\n", vnn, pid);
60         }
61         return ret;
62 }
63
64 /*
65   display statistics structure
66  */
67 static void show_statistics(struct ctdb_statistics *s)
68 {
69         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
70         int i;
71         const char *prefix=NULL;
72         int preflen=0;
73         const struct {
74                 const char *name;
75                 uint32_t offset;
76         } fields[] = {
77 #define STATISTICS_FIELD(n) { #n, offsetof(struct ctdb_statistics, n) }
78                 STATISTICS_FIELD(num_clients),
79                 STATISTICS_FIELD(frozen),
80                 STATISTICS_FIELD(recovering),
81                 STATISTICS_FIELD(client_packets_sent),
82                 STATISTICS_FIELD(client_packets_recv),
83                 STATISTICS_FIELD(node_packets_sent),
84                 STATISTICS_FIELD(node_packets_recv),
85                 STATISTICS_FIELD(keepalive_packets_sent),
86                 STATISTICS_FIELD(keepalive_packets_recv),
87                 STATISTICS_FIELD(node.req_call),
88                 STATISTICS_FIELD(node.reply_call),
89                 STATISTICS_FIELD(node.req_dmaster),
90                 STATISTICS_FIELD(node.reply_dmaster),
91                 STATISTICS_FIELD(node.reply_error),
92                 STATISTICS_FIELD(node.req_message),
93                 STATISTICS_FIELD(node.req_control),
94                 STATISTICS_FIELD(node.reply_control),
95                 STATISTICS_FIELD(client.req_call),
96                 STATISTICS_FIELD(client.req_message),
97                 STATISTICS_FIELD(client.req_control),
98                 STATISTICS_FIELD(timeouts.call),
99                 STATISTICS_FIELD(timeouts.control),
100                 STATISTICS_FIELD(timeouts.traverse),
101                 STATISTICS_FIELD(total_calls),
102                 STATISTICS_FIELD(pending_calls),
103                 STATISTICS_FIELD(lockwait_calls),
104                 STATISTICS_FIELD(pending_lockwait_calls),
105                 STATISTICS_FIELD(memory_used),
106                 STATISTICS_FIELD(max_hop_count),
107         };
108         printf("CTDB version %u\n", CTDB_VERSION);
109         for (i=0;i<ARRAY_SIZE(fields);i++) {
110                 if (strchr(fields[i].name, '.')) {
111                         preflen = strcspn(fields[i].name, ".")+1;
112                         if (!prefix || strncmp(prefix, fields[i].name, preflen) != 0) {
113                                 prefix = fields[i].name;
114                                 printf(" %*.*s\n", preflen-1, preflen-1, fields[i].name);
115                         }
116                 } else {
117                         preflen = 0;
118                 }
119                 printf(" %*s%-22s%*s%10u\n", 
120                        preflen?4:0, "",
121                        fields[i].name+preflen, 
122                        preflen?0:4, "",
123                        *(uint32_t *)(fields[i].offset+(uint8_t *)s));
124         }
125         printf(" %-30s     %.6f sec\n", "max_call_latency", s->max_call_latency);
126         printf(" %-30s     %.6f sec\n", "max_lockwait_latency", s->max_lockwait_latency);
127         talloc_free(tmp_ctx);
128 }
129
130 /*
131   display remote ctdb statistics combined from all nodes
132  */
133 static int control_statistics_all(struct ctdb_context *ctdb)
134 {
135         int ret, i;
136         struct ctdb_statistics statistics;
137         uint32_t *nodes;
138         uint32_t num_nodes;
139
140         nodes = ctdb_get_connected_nodes(ctdb, TIMELIMIT(), ctdb, &num_nodes);
141         CTDB_NO_MEMORY(ctdb, nodes);
142         
143         ZERO_STRUCT(statistics);
144
145         for (i=0;i<num_nodes;i++) {
146                 struct ctdb_statistics s1;
147                 int j;
148                 uint32_t *v1 = (uint32_t *)&s1;
149                 uint32_t *v2 = (uint32_t *)&statistics;
150                 uint32_t num_ints = 
151                         offsetof(struct ctdb_statistics, __last_counter) / sizeof(uint32_t);
152                 ret = ctdb_ctrl_statistics(ctdb, nodes[i], &s1);
153                 if (ret != 0) {
154                         printf("Unable to get statistics from node %u\n", nodes[i]);
155                         return ret;
156                 }
157                 for (j=0;j<num_ints;j++) {
158                         v2[j] += v1[j];
159                 }
160                 statistics.max_hop_count = 
161                         MAX(statistics.max_hop_count, s1.max_hop_count);
162                 statistics.max_call_latency = 
163                         MAX(statistics.max_call_latency, s1.max_call_latency);
164                 statistics.max_lockwait_latency = 
165                         MAX(statistics.max_lockwait_latency, s1.max_lockwait_latency);
166         }
167         talloc_free(nodes);
168         printf("Gathered statistics for %u nodes\n", num_nodes);
169         show_statistics(&statistics);
170         return 0;
171 }
172
173 /*
174   display remote ctdb statistics
175  */
176 static int control_statistics(struct ctdb_context *ctdb, int argc, const char **argv)
177 {
178         int ret;
179         struct ctdb_statistics statistics;
180
181         if (options.vnn == CTDB_BROADCAST_ALL) {
182                 return control_statistics_all(ctdb);
183         }
184
185         ret = ctdb_ctrl_statistics(ctdb, options.vnn, &statistics);
186         if (ret != 0) {
187                 printf("Unable to get statistics from node %u\n", options.vnn);
188                 return ret;
189         }
190         show_statistics(&statistics);
191         return 0;
192 }
193
194
195 /*
196   reset remote ctdb statistics
197  */
198 static int control_statistics_reset(struct ctdb_context *ctdb, int argc, const char **argv)
199 {
200         int ret;
201
202         ret = ctdb_statistics_reset(ctdb, options.vnn);
203         if (ret != 0) {
204                 printf("Unable to reset statistics on node %u\n", options.vnn);
205                 return ret;
206         }
207         return 0;
208 }
209
210
211 /*
212   display remote ctdb status
213  */
214 static int control_status(struct ctdb_context *ctdb, int argc, const char **argv)
215 {
216         int i, ret;
217         struct ctdb_vnn_map *vnnmap=NULL;
218         struct ctdb_node_map *nodemap=NULL;
219         uint32_t recmode, recmaster;
220         uint32_t myvnn;
221
222         myvnn = ctdb_ctrl_getvnn(ctdb, TIMELIMIT(), options.vnn);
223
224         ret = ctdb_ctrl_getnodemap(ctdb, TIMELIMIT(), options.vnn, ctdb, &nodemap);
225         if (ret != 0) {
226                 printf("Unable to get nodemap from node %u\n", options.vnn);
227                 return ret;
228         }
229
230         if(options.machinereadable){
231                 printf(":Node:IP:Disonnected:Disabled:Permanently Disabled:\n");
232                 for(i=0;i<nodemap->num;i++){
233                         printf(":%d:%s:%d:%d:%d:\n", nodemap->nodes[i].vnn,
234                                 inet_ntoa(nodemap->nodes[i].sin.sin_addr),
235                                !!(nodemap->nodes[i].flags&NODE_FLAGS_DISCONNECTED),
236                                !!(nodemap->nodes[i].flags&NODE_FLAGS_UNHEALTHY),
237                                !!(nodemap->nodes[i].flags&NODE_FLAGS_PERMANENTLY_DISABLED));
238                 }
239                 return 0;
240         }
241
242         printf("Number of nodes:%d\n", nodemap->num);
243         for(i=0;i<nodemap->num;i++){
244                 static const struct {
245                         uint32_t flag;
246                         const char *name;
247                 } flag_names[] = {
248                         { NODE_FLAGS_DISCONNECTED,          "DISCONNECTED" },
249                         { NODE_FLAGS_PERMANENTLY_DISABLED,  "DISABLED" },
250                         { NODE_FLAGS_BANNED,                "BANNED" },
251                         { NODE_FLAGS_UNHEALTHY,             "UNHEALTHY" },
252                 };
253                 char *flags_str = NULL;
254                 int j;
255                 for (j=0;j<ARRAY_SIZE(flag_names);j++) {
256                         if (nodemap->nodes[i].flags & flag_names[j].flag) {
257                                 if (flags_str == NULL) {
258                                         flags_str = talloc_strdup(ctdb, flag_names[j].name);
259                                 } else {
260                                         flags_str = talloc_asprintf_append(flags_str, "|%s",
261                                                                            flag_names[j].name);
262                                 }
263                                 CTDB_NO_MEMORY_FATAL(ctdb, flags_str);
264                         }
265                 }
266                 if (flags_str == NULL) {
267                         flags_str = talloc_strdup(ctdb, "OK");
268                         CTDB_NO_MEMORY_FATAL(ctdb, flags_str);
269                 }
270                 printf("vnn:%d %-16s %s%s\n", nodemap->nodes[i].vnn,
271                        inet_ntoa(nodemap->nodes[i].sin.sin_addr),
272                        flags_str,
273                        nodemap->nodes[i].vnn == myvnn?" (THIS NODE)":"");
274                 talloc_free(flags_str);
275         }
276
277         ret = ctdb_ctrl_getvnnmap(ctdb, TIMELIMIT(), options.vnn, ctdb, &vnnmap);
278         if (ret != 0) {
279                 printf("Unable to get vnnmap from node %u\n", options.vnn);
280                 return ret;
281         }
282         printf("Generation:%d\n",vnnmap->generation);
283         printf("Size:%d\n",vnnmap->size);
284         for(i=0;i<vnnmap->size;i++){
285                 printf("hash:%d lmaster:%d\n", i, vnnmap->map[i]);
286         }
287
288         ret = ctdb_ctrl_getrecmode(ctdb, TIMELIMIT(), options.vnn, &recmode);
289         if (ret != 0) {
290                 printf("Unable to get recmode from node %u\n", options.vnn);
291                 return ret;
292         }
293         printf("Recovery mode:%s (%d)\n",recmode==CTDB_RECOVERY_NORMAL?"NORMAL":"RECOVERY",recmode);
294
295         ret = ctdb_ctrl_getrecmaster(ctdb, TIMELIMIT(), options.vnn, &recmaster);
296         if (ret != 0) {
297                 printf("Unable to get recmaster from node %u\n", options.vnn);
298                 return ret;
299         }
300         printf("Recovery master:%d\n",recmaster);
301
302         return 0;
303 }
304
305 /*
306   kill a tcp connection
307  */
308 static int kill_tcp(struct ctdb_context *ctdb, int argc, const char **argv)
309 {
310         int i, ret, numrst;
311         struct sockaddr_in src, dst;
312
313         if (argc < 3) {
314                 usage();
315         }
316
317         if (!parse_ip_port(argv[0], &src)) {
318                 printf("Bad IP:port '%s'\n", argv[0]);
319                 return -1;
320         }
321
322         if (!parse_ip_port(argv[1], &dst)) {
323                 printf("Bad IP:port '%s'\n", argv[1]);
324                 return -1;
325         }
326
327         numrst = strtoul(argv[2], NULL, 0);
328
329         for (i=0;i<numrst;i++) {
330                 ret = ctdb_sys_kill_tcp(ctdb->ev, &src, &dst);
331
332                 printf("ret:%d\n", ret);
333                 if (ret==0) {
334                         return 0;
335                 }
336         }
337
338         return -1;
339 }
340
341 /*
342   send a tcp tickle ack
343  */
344 static int tickle_tcp(struct ctdb_context *ctdb, int argc, const char **argv)
345 {
346         int ret;
347         struct sockaddr_in src, dst;
348
349         if (argc < 2) {
350                 usage();
351         }
352
353         if (!parse_ip_port(argv[0], &src)) {
354                 printf("Bad IP:port '%s'\n", argv[0]);
355                 return -1;
356         }
357
358         if (!parse_ip_port(argv[1], &dst)) {
359                 printf("Bad IP:port '%s'\n", argv[1]);
360                 return -1;
361         }
362
363         ret = ctdb_sys_send_tcp(&src, &dst, 0, 0, 0);
364         if (ret==0) {
365                 return 0;
366         }
367         printf("Error while sending tickle ack\n");
368
369         return -1;
370 }
371
372 /*
373   display public ip status
374  */
375 static int control_ip(struct ctdb_context *ctdb, int argc, const char **argv)
376 {
377         int i, ret;
378         struct ctdb_all_public_ips *ips;
379         uint32_t myvnn;
380
381         myvnn = ctdb_ctrl_getvnn(ctdb, TIMELIMIT(), options.vnn);
382
383         ret = ctdb_ctrl_get_public_ips(ctdb, TIMELIMIT(), options.vnn, ctdb, &ips);
384         if (ret != 0) {
385                 printf("Unable to get public ips from node %u\n", options.vnn);
386                 return ret;
387         }
388
389         if(options.machinereadable){
390                 printf(":Public IP:Node:\n");
391                 for(i=0;i<ips->num;i++){
392                         printf(":%s:%d:\n",
393                         inet_ntoa(ips->ips[i].sin.sin_addr),
394                         ips->ips[i].takeover_vnn);
395                 }
396                 return 0;
397         }
398
399
400         printf("Number of nodes:%d\n", ips->num);
401         for(i=0;i<ips->num;i++){
402                 printf("%-16s %d\n",
403                         inet_ntoa(ips->ips[i].sin.sin_addr),
404                         ips->ips[i].takeover_vnn);
405         }
406
407         return 0;
408 }
409
410 /*
411   display pid of a ctdb daemon
412  */
413 static int control_getpid(struct ctdb_context *ctdb, int argc, const char **argv)
414 {
415         uint32_t pid;
416         int ret;
417
418         ret = ctdb_ctrl_getpid(ctdb, TIMELIMIT(), options.vnn, &pid);
419         if (ret != 0) {
420                 printf("Unable to get daemon pid from node %u\n", options.vnn);
421                 return ret;
422         }
423         printf("Pid:%d\n", pid);
424
425         return 0;
426 }
427
428 /*
429   disable a remote node
430  */
431 static int control_disable(struct ctdb_context *ctdb, int argc, const char **argv)
432 {
433         int ret;
434
435         ret = ctdb_ctrl_modflags(ctdb, TIMELIMIT(), options.vnn, NODE_FLAGS_PERMANENTLY_DISABLED, 0);
436         if (ret != 0) {
437                 printf("Unable to disable node %u\n", options.vnn);
438                 return ret;
439         }
440
441         return 0;
442 }
443
444 /*
445   enable a disabled remote node
446  */
447 static int control_enable(struct ctdb_context *ctdb, int argc, const char **argv)
448 {
449         int ret;
450
451         ret = ctdb_ctrl_modflags(ctdb, TIMELIMIT(), options.vnn, 0, NODE_FLAGS_PERMANENTLY_DISABLED);
452         if (ret != 0) {
453                 printf("Unable to enable node %u\n", options.vnn);
454                 return ret;
455         }
456
457         return 0;
458 }
459
460 /*
461   ban a node from the cluster
462  */
463 static int control_ban(struct ctdb_context *ctdb, int argc, const char **argv)
464 {
465         int ret;
466         uint32_t recmaster;
467         struct ctdb_ban_info b;
468         TDB_DATA data;
469         uint32_t ban_time;
470
471         if (argc < 1) {
472                 usage();
473         }
474
475         ban_time = strtoul(argv[0], NULL, 0);
476
477         ret = ctdb_ctrl_getrecmaster(ctdb, TIMELIMIT(), options.vnn, &recmaster);
478         if (ret != 0) {
479                 DEBUG(0,("Failed to find the recmaster\n"));
480                 return -1;
481         }
482
483         b.vnn = options.vnn;
484         b.ban_time = ban_time;
485
486         data.dptr = (uint8_t *)&b;
487         data.dsize = sizeof(b);
488
489         ret = ctdb_send_message(ctdb, recmaster, CTDB_SRVID_BAN_NODE, data);
490         if (ret != 0) {
491                 DEBUG(0,("Failed to tell the recmaster to ban node %u\n", options.vnn));
492                 return -1;
493         }
494         
495         return 0;
496 }
497
498
499 /*
500   unban a node from the cluster
501  */
502 static int control_unban(struct ctdb_context *ctdb, int argc, const char **argv)
503 {
504         int ret;
505         uint32_t recmaster;
506         TDB_DATA data;
507
508         ret = ctdb_ctrl_getrecmaster(ctdb, TIMELIMIT(), options.vnn, &recmaster);
509         if (ret != 0) {
510                 DEBUG(0,("Failed to find the recmaster\n"));
511                 return -1;
512         }
513
514         data.dptr = (uint8_t *)&options.vnn;
515         data.dsize = sizeof(uint32_t);
516
517         ret = ctdb_send_message(ctdb, recmaster, CTDB_SRVID_UNBAN_NODE, data);
518         if (ret != 0) {
519                 DEBUG(0,("Failed to tell the recmaster to unban node %u\n", options.vnn));
520                 return -1;
521         }
522         
523         return 0;
524 }
525
526
527 /*
528   shutdown a daemon
529  */
530 static int control_shutdown(struct ctdb_context *ctdb, int argc, const char **argv)
531 {
532         int ret;
533
534         ret = ctdb_ctrl_shutdown(ctdb, TIMELIMIT(), options.vnn);
535         if (ret != 0) {
536                 printf("Unable to shutdown node %u\n", options.vnn);
537                 return ret;
538         }
539
540         return 0;
541 }
542
543 /*
544   trigger a recovery
545  */
546 static int control_recover(struct ctdb_context *ctdb, int argc, const char **argv)
547 {
548         int ret;
549
550         ret = ctdb_ctrl_freeze(ctdb, TIMELIMIT(), options.vnn);
551         if (ret != 0) {
552                 printf("Unable to freeze node\n");
553                 return ret;
554         }
555
556         ret = ctdb_ctrl_setrecmode(ctdb, TIMELIMIT(), options.vnn, CTDB_RECOVERY_ACTIVE);
557         if (ret != 0) {
558                 printf("Unable to set recovery mode\n");
559                 return ret;
560         }
561
562         return 0;
563 }
564
565
566 /*
567   display monitoring mode of a remote node
568  */
569 static int control_getmonmode(struct ctdb_context *ctdb, int argc, const char **argv)
570 {
571         uint32_t monmode;
572         int ret;
573
574         ret = ctdb_ctrl_getmonmode(ctdb, TIMELIMIT(), options.vnn, &monmode);
575         if (ret != 0) {
576                 printf("Unable to get monmode from node %u\n", options.vnn);
577                 return ret;
578         }
579         printf("Monitoring mode:%s (%d)\n",monmode==CTDB_MONITORING_ACTIVE?"ACTIVE":"DISABLED",monmode);
580
581         return 0;
582 }
583
584 /*
585   set the monitoring mode of a remote node
586  */
587 static int control_setmonmode(struct ctdb_context *ctdb, int argc, const char **argv)
588 {
589         uint32_t monmode;
590         int ret;
591
592         if (argc < 1) {
593                 usage();
594         }
595
596         monmode = strtoul(argv[0], NULL, 0);
597
598         ret = ctdb_ctrl_setmonmode(ctdb, TIMELIMIT(), options.vnn, monmode);
599         if (ret != 0) {
600                 printf("Unable to set monmode on node %u\n", options.vnn);
601                 return ret;
602         }
603
604         return 0;
605 }
606
607 /*
608   display remote list of keys/data for a db
609  */
610 static int control_catdb(struct ctdb_context *ctdb, int argc, const char **argv)
611 {
612         const char *db_name;
613         struct ctdb_db_context *ctdb_db;
614         int ret;
615
616         if (argc < 1) {
617                 usage();
618         }
619
620         db_name = argv[0];
621         ctdb_db = ctdb_attach(ctdb, db_name);
622
623         if (ctdb_db == NULL) {
624                 DEBUG(0,("Unable to attach to database '%s'\n", db_name));
625                 return -1;
626         }
627
628         /* traverse and dump the cluster tdb */
629         ret = ctdb_dump_db(ctdb_db, stdout);
630         if (ret == -1) {
631                 printf("Unable to dump database\n");
632                 return -1;
633         }
634         talloc_free(ctdb_db);
635
636         printf("Dumped %d records\n", ret);
637         return 0;
638 }
639
640
641 /*
642   display a list of the databases on a remote ctdb
643  */
644 static int control_getdbmap(struct ctdb_context *ctdb, int argc, const char **argv)
645 {
646         int i, ret;
647         struct ctdb_dbid_map *dbmap=NULL;
648
649         ret = ctdb_ctrl_getdbmap(ctdb, TIMELIMIT(), options.vnn, ctdb, &dbmap);
650         if (ret != 0) {
651                 printf("Unable to get dbids from node %u\n", options.vnn);
652                 return ret;
653         }
654
655         printf("Number of databases:%d\n", dbmap->num);
656         for(i=0;i<dbmap->num;i++){
657                 const char *path;
658                 const char *name;
659
660                 ctdb_ctrl_getdbpath(ctdb, TIMELIMIT(), options.vnn, dbmap->dbids[i], ctdb, &path);
661                 ctdb_ctrl_getdbname(ctdb, TIMELIMIT(), options.vnn, dbmap->dbids[i], ctdb, &name);
662                 printf("dbid:0x%08x name:%s path:%s\n", dbmap->dbids[i], name, path);
663         }
664
665         return 0;
666 }
667
668 /*
669   ping a node
670  */
671 static int control_ping(struct ctdb_context *ctdb, int argc, const char **argv)
672 {
673         int ret;
674         struct timeval tv = timeval_current();
675         ret = ctdb_ctrl_ping(ctdb, options.vnn);
676         if (ret == -1) {
677                 printf("Unable to get ping response from node %u\n", options.vnn);
678         } else {
679                 printf("response from %u time=%.6f sec  (%d clients)\n", 
680                        options.vnn, timeval_elapsed(&tv), ret);
681         }
682         return 0;
683 }
684
685
686 /*
687   get a tunable
688  */
689 static int control_getvar(struct ctdb_context *ctdb, int argc, const char **argv)
690 {
691         const char *name;
692         uint32_t value;
693         int ret;
694
695         if (argc < 1) {
696                 usage();
697         }
698
699         name = argv[0];
700         ret = ctdb_ctrl_get_tunable(ctdb, TIMELIMIT(), options.vnn, name, &value);
701         if (ret == -1) {
702                 printf("Unable to get tunable variable '%s'\n", name);
703                 return -1;
704         }
705
706         printf("%-19s = %u\n", name, value);
707         return 0;
708 }
709
710 /*
711   set a tunable
712  */
713 static int control_setvar(struct ctdb_context *ctdb, int argc, const char **argv)
714 {
715         const char *name;
716         uint32_t value;
717         int ret;
718
719         if (argc < 2) {
720                 usage();
721         }
722
723         name = argv[0];
724         value = strtoul(argv[1], NULL, 0);
725
726         ret = ctdb_ctrl_set_tunable(ctdb, TIMELIMIT(), options.vnn, name, value);
727         if (ret == -1) {
728                 printf("Unable to set tunable variable '%s'\n", name);
729                 return -1;
730         }
731         return 0;
732 }
733
734 /*
735   list all tunables
736  */
737 static int control_listvars(struct ctdb_context *ctdb, int argc, const char **argv)
738 {
739         uint32_t count;
740         const char **list;
741         int ret, i;
742
743         ret = ctdb_ctrl_list_tunables(ctdb, TIMELIMIT(), options.vnn, ctdb, &list, &count);
744         if (ret == -1) {
745                 printf("Unable to list tunable variables\n");
746                 return -1;
747         }
748
749         for (i=0;i<count;i++) {
750                 control_getvar(ctdb, 1, &list[i]);
751         }
752
753         talloc_free(list);
754         
755         return 0;
756 }
757
758 /*
759   display debug level on a node
760  */
761 static int control_getdebug(struct ctdb_context *ctdb, int argc, const char **argv)
762 {
763         int ret;
764         uint32_t level;
765
766         ret = ctdb_ctrl_get_debuglevel(ctdb, options.vnn, &level);
767         if (ret != 0) {
768                 printf("Unable to get debuglevel response from node %u\n", 
769                        options.vnn);
770         } else {
771                 printf("Node %u is at debug level %u\n", options.vnn, level);
772         }
773         return 0;
774 }
775
776
777 /*
778   set debug level on a node or all nodes
779  */
780 static int control_setdebug(struct ctdb_context *ctdb, int argc, const char **argv)
781 {
782         int ret;
783         uint32_t level;
784
785         if (argc < 1) {
786                 usage();
787         }
788
789         level = strtoul(argv[0], NULL, 0);
790
791         ret = ctdb_ctrl_set_debuglevel(ctdb, options.vnn, level);
792         if (ret != 0) {
793                 printf("Unable to set debug level on node %u\n", options.vnn);
794         }
795         return 0;
796 }
797
798
799 /*
800   freeze a node
801  */
802 static int control_freeze(struct ctdb_context *ctdb, int argc, const char **argv)
803 {
804         int ret;
805
806         ret = ctdb_ctrl_freeze(ctdb, TIMELIMIT(), options.vnn);
807         if (ret != 0) {
808                 printf("Unable to freeze node %u\n", options.vnn);
809         }               
810         return 0;
811 }
812
813 /*
814   thaw a node
815  */
816 static int control_thaw(struct ctdb_context *ctdb, int argc, const char **argv)
817 {
818         int ret;
819
820         ret = ctdb_ctrl_thaw(ctdb, TIMELIMIT(), options.vnn);
821         if (ret != 0) {
822                 printf("Unable to thaw node %u\n", options.vnn);
823         }               
824         return 0;
825 }
826
827
828 /*
829   attach to a database
830  */
831 static int control_attach(struct ctdb_context *ctdb, int argc, const char **argv)
832 {
833         const char *db_name;
834         struct ctdb_db_context *ctdb_db;
835
836         if (argc < 1) {
837                 usage();
838         }
839         db_name = argv[0];
840
841         ctdb_db = ctdb_attach(ctdb, db_name);
842         if (ctdb_db == NULL) {
843                 DEBUG(0,("Unable to attach to database '%s'\n", db_name));
844                 return -1;
845         }
846
847         return 0;
848 }
849
850 /*
851   dump memory usage
852  */
853 static int control_dumpmemory(struct ctdb_context *ctdb, int argc, const char **argv)
854 {
855         return ctdb_control(ctdb, options.vnn, 0, CTDB_CONTROL_DUMP_MEMORY,
856                             CTDB_CTRL_FLAG_NOREPLY, tdb_null, NULL, NULL, NULL, NULL, NULL);
857 }
858
859
860 static const struct {
861         const char *name;
862         int (*fn)(struct ctdb_context *, int, const char **);
863         bool auto_all;
864         const char *msg;
865         const char *args;
866 } ctdb_commands[] = {
867         { "status",          control_status,            true,  "show node status" },
868         { "ping",            control_ping,              true,  "ping all nodes" },
869         { "getvar",          control_getvar,            true,  "get a tunable variable",               "<name>"},
870         { "setvar",          control_setvar,            true,  "set a tunable variable",               "<name> <value>"},
871         { "listvars",        control_listvars,          true,  "list tunable variables"},
872         { "statistics",      control_statistics,        false, "show statistics" },
873         { "statisticsreset", control_statistics_reset,  true,  "reset statistics"},
874         { "ip",              control_ip,                true,  "show which public ip's that ctdb manages" },
875         { "process-exists",  control_process_exists,    true,  "check if a process exists on a node",  "<pid>"},
876         { "getdbmap",        control_getdbmap,          true,  "show the database map" },
877         { "catdb",           control_catdb,             true,  "dump a database" ,                     "<dbname>"},
878         { "getmonmode",      control_getmonmode,        true,  "show monitoring mode" },
879         { "setmonmode",      control_setmonmode,        true,  "set monitoring mode", "<0|1>" },
880         { "setdebug",        control_setdebug,          true,  "set debug level",                      "<debuglevel>" },
881         { "getdebug",        control_getdebug,          true,  "get debug level" },
882         { "attach",          control_attach,            true,  "attach to a database",                 "<dbname>" },
883         { "dumpmemory",      control_dumpmemory,        true,  "dump memory map to logs" },
884         { "getpid",          control_getpid,            true,  "get ctdbd process ID" },
885         { "disable",         control_disable,           true,  "disable a nodes public IP" },
886         { "enable",          control_enable,            true,  "enable a nodes public IP" },
887         { "ban",             control_ban,               true,  "ban a node from the cluster",          "<bantime|0>"},
888         { "unban",           control_unban,             true,  "unban a node from the cluster" },
889         { "shutdown",        control_shutdown,          true,  "shutdown ctdbd" },
890         { "recover",         control_recover,           true,  "force recovery" },
891         { "freeze",          control_freeze,            true,  "freeze all databases" },
892         { "thaw",            control_thaw,              true,  "thaw all databases" },
893         { "killtcp",         kill_tcp,                  false, "kill a tcp connection. Try <num> times.", "<srcip:port> <dstip:port> <num>" },
894         { "tickle",          tickle_tcp,                false, "send a tcp tickle ack", "<srcip:port> <dstip:port>" },
895 };
896
897 /*
898   show usage message
899  */
900 static void usage(void)
901 {
902         int i;
903         printf(
904 "Usage: ctdb [options] <control>\n" \
905 "Options:\n" \
906 "   -n <node>          choose node number, or 'all' (defaults to local node)\n"
907 "   -Y                 generate machinereadable output\n"
908 "   -t <timelimit>     set timelimit for control in seconds (default %u)\n", options.timelimit);
909         printf("Controls:\n");
910         for (i=0;i<ARRAY_SIZE(ctdb_commands);i++) {
911                 printf("  %-15s %-27s  %s\n", 
912                        ctdb_commands[i].name, 
913                        ctdb_commands[i].args?ctdb_commands[i].args:"",
914                        ctdb_commands[i].msg);
915         }
916         exit(1);
917 }
918
919
920 /*
921   main program
922 */
923 int main(int argc, const char *argv[])
924 {
925         struct ctdb_context *ctdb;
926         char *nodestring = NULL;
927         struct poptOption popt_options[] = {
928                 POPT_AUTOHELP
929                 POPT_CTDB_CMDLINE
930                 { "timelimit", 't', POPT_ARG_INT, &options.timelimit, 0, "timelimit", "integer" },
931                 { "node",      'n', POPT_ARG_STRING, &nodestring, 0, "node", "integer|all" },
932                 { "machinereadable", 'Y', POPT_ARG_NONE, &options.machinereadable, 0, "enable machinereadable output", NULL },
933                 POPT_TABLEEND
934         };
935         int opt;
936         const char **extra_argv;
937         int extra_argc = 0;
938         int ret=-1, i;
939         poptContext pc;
940         struct event_context *ev;
941         const char *control;
942
943         /* set some defaults */
944         options.timelimit = 3;
945         options.vnn = CTDB_CURRENT_NODE;
946
947         pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST);
948
949         while ((opt = poptGetNextOpt(pc)) != -1) {
950                 switch (opt) {
951                 default:
952                         fprintf(stderr, "Invalid option %s: %s\n", 
953                                 poptBadOption(pc, 0), poptStrerror(opt));
954                         exit(1);
955                 }
956         }
957
958         /* setup the remaining options for the main program to use */
959         extra_argv = poptGetArgs(pc);
960         if (extra_argv) {
961                 extra_argv++;
962                 while (extra_argv[extra_argc]) extra_argc++;
963         }
964
965         if (extra_argc < 1) {
966                 usage();
967         }
968
969         /* setup the node number to contact */
970         if (nodestring != NULL) {
971                 if (strcmp(nodestring, "all") == 0) {
972                         options.vnn = CTDB_BROADCAST_ALL;
973                 } else {
974                         options.vnn = strtoul(nodestring, NULL, 0);
975                 }
976         }
977
978         control = extra_argv[0];
979
980         ev = event_context_init(NULL);
981
982         /* initialise ctdb */
983         ctdb = ctdb_cmdline_client(ev);
984         if (ctdb == NULL) {
985                 printf("Failed to init ctdb\n");
986                 exit(1);
987         }
988
989         for (i=0;i<ARRAY_SIZE(ctdb_commands);i++) {
990                 if (strcmp(control, ctdb_commands[i].name) == 0) {
991                         int j;
992
993                         if (options.vnn == CTDB_CURRENT_NODE) {
994                                 options.vnn = ctdb_ctrl_getvnn(ctdb, TIMELIMIT(), options.vnn);         
995                         }
996
997                         if (ctdb_commands[i].auto_all && 
998                             options.vnn == CTDB_BROADCAST_ALL) {
999                                 uint32_t *nodes;
1000                                 uint32_t num_nodes;
1001                                 ret = 0;
1002
1003                                 nodes = ctdb_get_connected_nodes(ctdb, TIMELIMIT(), ctdb, &num_nodes);
1004                                 CTDB_NO_MEMORY(ctdb, nodes);
1005         
1006                                 for (j=0;j<num_nodes;j++) {
1007                                         options.vnn = nodes[j];
1008                                         ret |= ctdb_commands[i].fn(ctdb, extra_argc-1, extra_argv+1);
1009                                 }
1010                                 talloc_free(nodes);
1011                         } else {
1012                                 ret = ctdb_commands[i].fn(ctdb, extra_argc-1, extra_argv+1);
1013                         }
1014                         break;
1015                 }
1016         }
1017
1018         if (i == ARRAY_SIZE(ctdb_commands)) {
1019                 printf("Unknown control '%s'\n", control);
1020                 exit(1);
1021         }
1022
1023         return ret;
1024 }