+ sendline(fd, "%d %d", CONTROL, REQ_RESTART);
+ if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RESTART || result) {
+ fprintf(stderr, "Could not restart tinc daemon\n");
+ return 1;
+ }
+ return 0;
+ }
+
+ if(!strcasecmp(argv[optind], "retry")) {
+ sendline(fd, "%d %d", CONTROL, REQ_RETRY);
+ if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
+ fprintf(stderr, "Could not retry outgoing connections\n");
+ return 1;
+ }
+ return 0;
+ }
+
+ if(!strcasecmp(argv[optind], "dump")) {
+ if(argc < optind + 2) {
+ fprintf(stderr, "Not enough arguments.\n");
+ usage(true);
+ return 1;
+ }
+
+ bool do_graph = false;
+
+ if(!strcasecmp(argv[optind+1], "nodes"))
+ sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
+ else if(!strcasecmp(argv[optind+1], "edges"))
+ sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
+ else if(!strcasecmp(argv[optind+1], "subnets"))
+ sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
+ else if(!strcasecmp(argv[optind+1], "connections"))
+ sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
+ else if(!strcasecmp(argv[optind+1], "graph")) {
+ sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
+ sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
+ do_graph = true;
+ printf("digraph {\n");
+ } else {
+ fprintf(stderr, "Unknown dump type '%s'.\n", argv[optind+1]);
+ usage(true);
+ return 1;
+ }
+
+ while(recvline(fd, line, sizeof line)) {
+ char node1[4096], node2[4096];
+ int n = sscanf(line, "%d %d %s to %s", &code, &req, node1, node2);
+ if(n == 2) {
+ if(do_graph && req == REQ_DUMP_NODES)
+ continue;
+ else {
+ if(do_graph)
+ printf("}\n");
+ return 0;
+ }
+ }
+ if(n < 2)
+ break;
+
+ if(!do_graph)
+ printf("%s\n", line + 5);
+ else {
+ if(req == REQ_DUMP_NODES)
+ printf(" %s [label = \"%s\"];\n", node1, node1);
+ else
+ printf(" %s -> %s;\n", node1, node2);
+ }
+ }
+
+ fprintf(stderr, "Error receiving dump\n");
+ return 1;
+ }
+
+ if(!strcasecmp(argv[optind], "purge")) {
+ sendline(fd, "%d %d", CONTROL, REQ_PURGE);
+ if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
+ fprintf(stderr, "Could not purge tinc daemon\n");
+ return 1;
+ }
+ return 0;
+ }
+
+ if(!strcasecmp(argv[optind], "debug")) {
+ int debuglevel, origlevel;
+
+ if(argc != optind + 2) {
+ fprintf(stderr, "Invalid arguments.\n");
+ return 1;
+ }
+ debuglevel = atoi(argv[optind+1]);
+
+ sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
+ if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
+ fprintf(stderr, "Could not purge tinc daemon\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Old level %d, new level %d\n", origlevel, debuglevel);
+ return 0;
+ }
+
+ if(!strcasecmp(argv[optind], "connect")) {
+ if(argc != optind + 2) {
+ fprintf(stderr, "Invalid arguments.\n");
+ return 1;
+ }
+ char *name = argv[optind + 1];
+
+ sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, name);
+ if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
+ fprintf(stderr, "Could not connect to %s\n", name);
+ return 1;
+ }
+ return 0;
+ }
+
+ if(!strcasecmp(argv[optind], "disconnect")) {
+ if(argc != optind + 2) {
+ fprintf(stderr, "Invalid arguments.\n");
+ return 1;
+ }
+ char *name = argv[optind + 1];
+
+ sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, name);
+ if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
+ fprintf(stderr, "Could not disconnect %s\n", name);
+ return 1;
+ }
+ return 0;
+ }
+
+#ifdef HAVE_CURSES
+ if(!strcasecmp(argv[optind], "top")) {
+ top(fd);
+ return 0;
+ }
+#endif
+
+ if(!strcasecmp(argv[optind], "pcap")) {
+ pcap(fd, stdout);
+ return 0;