2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2014 Guus Sliepen <guus@tinc-vpn.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "readline/readline.h"
26 #include "readline/history.h"
31 #include "control_common.h"
35 #include "invitation.h"
44 #define MSG_NOSIGNAL 0
47 static char **orig_argv;
50 /* If nonzero, display usage information and exit. */
51 static bool show_help = false;
53 /* If nonzero, print the version on standard output and exit. */
54 static bool show_version = false;
56 static char *name = NULL;
57 static char controlcookie[1025];
58 char *tinc_conf = NULL;
59 char *hosts_dir = NULL;
62 // Horrible global variables...
69 static bool force = false;
71 bool confbasegiven = false;
72 bool netnamegiven = false;
73 char *scriptinterpreter = NULL;
74 char *scriptextension = "";
77 static struct option const long_options[] = {
78 {"config", required_argument, NULL, 'c'},
79 {"net", required_argument, NULL, 'n'},
80 {"help", no_argument, NULL, 1},
81 {"version", no_argument, NULL, 2},
82 {"pidfile", required_argument, NULL, 3},
83 {"force", no_argument, NULL, 4},
87 static void version(void) {
88 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
89 BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
90 printf("Copyright (C) 1998-2012 Ivo Timmermans, Guus Sliepen and others.\n"
91 "See the AUTHORS file for a complete list.\n\n"
92 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
93 "and you are welcome to redistribute it under certain conditions;\n"
94 "see the file COPYING for details.\n");
97 static void usage(bool status) {
99 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
101 printf("Usage: %s [options] command\n\n", program_name);
102 printf("Valid options are:\n"
103 " -c, --config=DIR Read configuration options from DIR.\n"
104 " -n, --net=NETNAME Connect to net NETNAME.\n"
105 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
106 " --help Display this help and exit.\n"
107 " --version Output version information and exit.\n"
109 "Valid commands are:\n"
110 " init [name] Create initial configuration files.\n"
111 " get VARIABLE Print current value of VARIABLE\n"
112 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
113 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
114 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
115 " start [tincd options] Start tincd.\n"
116 " stop Stop tincd.\n"
117 " restart [tincd options] Restart tincd.\n"
118 " reload Partially reload configuration of running tincd.\n"
119 " pid Show PID of currently running tincd.\n"
120 " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
121 " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
122 " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
123 " dump Dump a list of one of the following things:\n"
124 " [reachable] nodes - all known nodes in the VPN\n"
125 " edges - all known connections in the VPN\n"
126 " subnets - all known subnets in the VPN\n"
127 " connections - all meta connections with ourself\n"
128 " [di]graph - graph of the VPN in dotty format\n"
129 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
130 " purge Purge unreachable nodes\n"
131 " debug N Set debug level\n"
132 " retry Retry all outgoing connections\n"
133 " disconnect NODE Close meta connection with NODE\n"
135 " top Show real-time statistics\n"
137 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
138 " log [level] Dump log output [up to the specified level]\n"
139 " export Export host configuration of local node to standard output\n"
140 " export-all Export all host configuration files to standard output\n"
141 " import [--force] Import host configuration file(s) from standard input\n"
142 " exchange [--force] Same as export followed by import\n"
143 " exchange-all [--force] Same as export-all followed by import\n"
144 " invite NODE [...] Generate an invitation for NODE\n"
145 " join INVITATION Join a VPN using an INVITIATION\n"
146 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
148 printf("Report bugs to tinc@tinc-vpn.org.\n");
152 static bool parse_options(int argc, char **argv) {
154 int option_index = 0;
156 while((r = getopt_long(argc, argv, "+c:n:", long_options, &option_index)) != EOF) {
158 case 0: /* long option */
161 case 'c': /* config file */
162 confbase = xstrdup(optarg);
163 confbasegiven = true;
166 case 'n': /* net name given */
167 netname = xstrdup(optarg);
170 case 1: /* show help */
174 case 2: /* show version */
178 case 3: /* open control socket here */
179 pidfilename = xstrdup(optarg);
186 case '?': /* wrong options */
195 if(!netname && (netname = getenv("NETNAME")))
196 netname = xstrdup(netname);
198 /* netname "." is special: a "top-level name" */
200 if(netname && (!*netname || !strcmp(netname, "."))) {
205 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
206 fprintf(stderr, "Invalid character in netname!\n");
213 /* Open a file with the desired permissions, minus the umask.
214 Also, if we want to create an executable file, we call fchmod()
215 to set the executable bits. */
217 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
218 mode_t mask = umask(0);
221 FILE *f = fopen(filename, mode);
223 if((perms & 0444) && f)
224 fchmod(fileno(f), perms);
230 static void disable_old_keys(const char *filename, const char *what) {
231 char tmpfile[PATH_MAX] = "";
233 bool disabled = false;
238 r = fopen(filename, "r");
242 snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
244 struct stat st = {.st_mode = 0600};
245 fstat(fileno(r), &st);
246 w = fopenmask(tmpfile, "w", st.st_mode);
248 while(fgets(buf, sizeof buf, r)) {
249 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
250 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
256 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
262 if(block || ed25519pubkey)
264 if(fputs(buf, w) < 0) {
270 if(block && !strncmp(buf, "-----END ", 9))
277 if(ferror(r) || fclose(r) < 0)
282 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
289 // We cannot atomically replace files on Windows.
290 char bakfile[PATH_MAX] = "";
291 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
292 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
293 rename(bakfile, filename);
295 if(rename(tmpfile, filename)) {
297 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
302 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
309 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
315 /* Check stdin and stdout */
317 /* Ask for a file and/or directory name. */
318 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
320 if(fgets(buf, sizeof buf, stdin) == NULL) {
321 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
325 size_t len = strlen(buf);
334 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
336 if(filename[0] != '/') {
338 /* The directory is a relative path or a filename. */
339 directory = get_current_dir_name();
340 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
344 disable_old_keys(filename, what);
346 /* Open it first to keep the inode busy */
348 r = fopenmask(filename, mode, perms);
351 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
359 Generate a public/private Ed25519 keypair, and ask for a file to store
362 static bool ed25519_keygen(bool ask) {
365 char *pubname, *privname;
367 fprintf(stderr, "Generating Ed25519 keypair:\n");
369 if(!(key = ecdsa_generate())) {
370 fprintf(stderr, "Error during key generation!\n");
373 fprintf(stderr, "Done.\n");
375 xasprintf(&privname, "%s" SLASH "ed25519_key.priv", confbase);
376 f = ask_and_open(privname, "private Ed25519 key", "a", ask, 0600);
382 if(!ecdsa_write_pem_private_key(key, f)) {
383 fprintf(stderr, "Error writing private key!\n");
392 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
394 xasprintf(&pubname, "%s" SLASH "ed25519_key.pub", confbase);
396 f = ask_and_open(pubname, "public Ed25519 key", "a", ask, 0666);
402 char *pubkey = ecdsa_get_base64_public_key(key);
403 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
413 Generate a public/private RSA keypair, and ask for a file to store
416 static bool rsa_keygen(int bits, bool ask) {
419 char *pubname, *privname;
421 // Make sure the key size is a multiple of 8 bits.
424 // Force them to be between 1024 and 8192 bits long.
430 fprintf(stderr, "Generating %d bits keys:\n", bits);
432 if(!(key = rsa_generate(bits, 0x10001))) {
433 fprintf(stderr, "Error during key generation!\n");
436 fprintf(stderr, "Done.\n");
438 xasprintf(&privname, "%s" SLASH "rsa_key.priv", confbase);
439 f = ask_and_open(privname, "private RSA key", "a", ask, 0600);
445 if(!rsa_write_pem_private_key(key, f)) {
446 fprintf(stderr, "Error writing private key!\n");
455 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
457 xasprintf(&pubname, "%s" SLASH "rsa_key.pub", confbase);
459 f = ask_and_open(pubname, "public RSA key", "a", ask, 0666);
465 if(!rsa_write_pem_public_key(key, f)) {
466 fprintf(stderr, "Error writing public key!\n");
481 bool recvline(int fd, char *line, size_t len) {
482 char *newline = NULL;
487 while(!(newline = memchr(buffer, '\n', blen))) {
488 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
489 if(result == -1 && sockerrno == EINTR)
496 if(newline - buffer >= len)
499 len = newline - buffer;
501 memcpy(line, buffer, len);
503 memmove(buffer, newline + 1, blen - len - 1);
509 bool recvdata(int fd, char *data, size_t len) {
514 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
515 if(result == -1 && sockerrno == EINTR)
522 memcpy(data, buffer, len);
523 memmove(buffer, buffer + len, blen - len);
529 bool sendline(int fd, char *format, ...) {
530 static char buffer[4096];
535 va_start(ap, format);
536 blen = vsnprintf(buffer, sizeof buffer, format, ap);
539 if(blen < 1 || blen >= sizeof buffer)
546 int result = send(fd, p, blen, MSG_NOSIGNAL);
547 if(result == -1 && sockerrno == EINTR)
558 static void pcap(int fd, FILE *out, int snaplen) {
559 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
567 uint32_t tz_accuracy;
574 snaplen ?: sizeof data,
587 fwrite(&header, sizeof header, 1, out);
591 while(recvline(fd, line, sizeof line)) {
593 int n = sscanf(line, "%d %d %d", &code, &req, &len);
594 gettimeofday(&tv, NULL);
595 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
597 if(!recvdata(fd, data, len))
599 packet.tv_sec = tv.tv_sec;
600 packet.tv_usec = tv.tv_usec;
602 packet.origlen = len;
603 fwrite(&packet, sizeof packet, 1, out);
604 fwrite(data, len, 1, out);
609 static void logcontrol(int fd, FILE *out, int level) {
610 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
614 while(recvline(fd, line, sizeof line)) {
616 int n = sscanf(line, "%d %d %d", &code, &req, &len);
617 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
619 if(!recvdata(fd, data, len))
621 fwrite(data, len, 1, out);
628 static bool remove_service(void) {
629 SC_HANDLE manager = NULL;
630 SC_HANDLE service = NULL;
631 SERVICE_STATUS status = {0};
633 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
635 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
639 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
642 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
646 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
647 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
649 fprintf(stderr, "%s service stopped\n", identname);
651 if(!DeleteService(service)) {
652 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
656 fprintf(stderr, "%s service removed\n", identname);
662 bool connect_tincd(bool verbose) {
667 struct timeval tv = {0, 0};
668 if(select(fd + 1, &r, NULL, NULL, &tv)) {
669 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
677 FILE *f = fopen(pidfilename, "r");
680 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
687 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
689 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
697 struct sockaddr_un sa;
698 sa.sun_family = AF_UNIX;
699 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
701 fd = socket(AF_UNIX, SOCK_STREAM, 0);
704 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
708 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
710 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
716 struct addrinfo hints = {
717 .ai_family = AF_UNSPEC,
718 .ai_socktype = SOCK_STREAM,
719 .ai_protocol = IPPROTO_TCP,
723 struct addrinfo *res = NULL;
725 if(getaddrinfo(host, port, &hints, &res) || !res) {
727 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
731 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
734 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
739 unsigned long arg = 0;
741 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
743 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
747 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
749 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
759 static const int one = 1;
760 setsockopt(c, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
766 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
768 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
774 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
776 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
778 fprintf(stderr, "Could not fully establish control socket connection\n");
788 static int cmd_start(int argc, char *argv[]) {
789 if(connect_tincd(false)) {
791 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
793 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
798 char *slash = strrchr(program_name, '/');
801 if ((c = strrchr(program_name, '\\')) > slash)
806 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
811 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
816 Windows has no real concept of an "argv array". A command line is just one string.
817 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
818 it uses quotes to handle spaces in arguments.
819 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
820 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
821 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
823 xasprintf(&arg0, "\"%s\"", arg0);
825 nargv[nargc++] = arg0;
826 for(int i = 1; i < optind; i++)
827 nargv[nargc++] = orig_argv[i];
828 for(int i = 1; i < argc; i++)
829 nargv[nargc++] = argv[i];
833 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
838 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
844 exit(execvp(c, nargv));
848 int status = -1, result;
850 signal(SIGINT, SIG_IGN);
852 result = waitpid(pid, &status, 0);
854 signal(SIGINT, SIG_DFL);
857 if(result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
858 fprintf(stderr, "Error starting %s\n", c);
866 static int cmd_stop(int argc, char *argv[]) {
868 fprintf(stderr, "Too many arguments!\n");
873 if(!connect_tincd(true)) {
875 if(kill(pid, SIGTERM)) {
876 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
880 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
881 waitpid(pid, NULL, 0);
888 sendline(fd, "%d %d", CONTROL, REQ_STOP);
890 while(recvline(fd, line, sizeof line)) {
891 // Wait for tincd to close the connection...
894 if(!remove_service())
904 static int cmd_restart(int argc, char *argv[]) {
906 return cmd_start(argc, argv);
909 static int cmd_reload(int argc, char *argv[]) {
911 fprintf(stderr, "Too many arguments!\n");
915 if(!connect_tincd(true))
918 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
919 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
920 fprintf(stderr, "Could not reload configuration.\n");
928 static int cmd_dump(int argc, char *argv[]) {
929 bool only_reachable = false;
931 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
932 if(strcasecmp(argv[2], "nodes")) {
933 fprintf(stderr, "`reachable' only supported for nodes.\n");
937 only_reachable = true;
943 fprintf(stderr, "Invalid number of arguments.\n");
948 if(!connect_tincd(true))
953 if(!strcasecmp(argv[1], "nodes"))
954 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
955 else if(!strcasecmp(argv[1], "edges"))
956 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
957 else if(!strcasecmp(argv[1], "subnets"))
958 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
959 else if(!strcasecmp(argv[1], "connections"))
960 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
961 else if(!strcasecmp(argv[1], "graph")) {
962 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
963 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
965 } else if(!strcasecmp(argv[1], "digraph")) {
966 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
967 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
970 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
977 else if(do_graph == 2)
978 printf("digraph {\n");
980 while(recvline(fd, line, sizeof line)) {
981 char node1[4096], node2[4096];
982 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
984 if(do_graph && req == REQ_DUMP_NODES)
1001 char local_host[4096];
1002 char local_port[4096];
1005 int cipher, digest, maclength, compression, distance, socket, weight;
1006 short int pmtu, minmtu, maxmtu;
1007 unsigned int options, status_int;
1008 node_status_t status;
1009 long int last_state_change;
1012 case REQ_DUMP_NODES: {
1013 int n = sscanf(line, "%*d %*d %s %s port %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", node, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
1015 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1019 memcpy(&status, &status_int, sizeof status);
1022 const char *color = "black";
1023 if(!strcmp(host, "MYSELF"))
1025 else if(!status.reachable)
1027 else if(strcmp(via, node))
1029 else if(!status.validkey)
1033 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1035 if(only_reachable && !status.reachable)
1037 printf("%s at %s port %s cipher %d digest %d maclength %d compression %d options %x status %04x nexthop %s via %s distance %d pmtu %hd (min %hd max %hd)\n",
1038 node, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1042 case REQ_DUMP_EDGES: {
1043 int n = sscanf(line, "%*d %*d %s %s %s port %s %s port %s %x %d", from, to, host, port, local_host, local_port, &options, &weight);
1045 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1050 float w = 1 + 65536.0 / weight;
1051 if(do_graph == 1 && strcmp(node1, node2) > 0)
1052 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1053 else if(do_graph == 2)
1054 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1056 printf("%s to %s at %s port %s local %s port %s options %x weight %d\n", from, to, host, port, local_host, local_port, options, weight);
1060 case REQ_DUMP_SUBNETS: {
1061 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1063 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1066 printf("%s owner %s\n", strip_weight(subnet), node);
1069 case REQ_DUMP_CONNECTIONS: {
1070 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1072 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1075 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1079 fprintf(stderr, "Unable to parse dump from tincd.\n");
1084 fprintf(stderr, "Error receiving dump.\n");
1088 static int cmd_purge(int argc, char *argv[]) {
1090 fprintf(stderr, "Too many arguments!\n");
1094 if(!connect_tincd(true))
1097 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1098 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1099 fprintf(stderr, "Could not purge old information.\n");
1106 static int cmd_debug(int argc, char *argv[]) {
1108 fprintf(stderr, "Invalid number of arguments.\n");
1112 if(!connect_tincd(true))
1115 int debuglevel = atoi(argv[1]);
1118 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1119 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1120 fprintf(stderr, "Could not set debug level.\n");
1124 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1128 static int cmd_retry(int argc, char *argv[]) {
1130 fprintf(stderr, "Too many arguments!\n");
1134 if(!connect_tincd(true))
1137 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1138 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1139 fprintf(stderr, "Could not retry outgoing connections.\n");
1146 static int cmd_connect(int argc, char *argv[]) {
1148 fprintf(stderr, "Invalid number of arguments.\n");
1152 if(!check_id(argv[1])) {
1153 fprintf(stderr, "Invalid name for node.\n");
1157 if(!connect_tincd(true))
1160 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1161 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1162 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1169 static int cmd_disconnect(int argc, char *argv[]) {
1171 fprintf(stderr, "Invalid number of arguments.\n");
1175 if(!check_id(argv[1])) {
1176 fprintf(stderr, "Invalid name for node.\n");
1180 if(!connect_tincd(true))
1183 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1184 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1185 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1192 static int cmd_top(int argc, char *argv[]) {
1194 fprintf(stderr, "Too many arguments!\n");
1199 if(!connect_tincd(true))
1205 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1210 static int cmd_pcap(int argc, char *argv[]) {
1212 fprintf(stderr, "Too many arguments!\n");
1216 if(!connect_tincd(true))
1219 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1224 static void sigint_handler(int sig) {
1225 fprintf(stderr, "\n");
1226 shutdown(fd, SHUT_RDWR);
1230 static int cmd_log(int argc, char *argv[]) {
1232 fprintf(stderr, "Too many arguments!\n");
1236 if(!connect_tincd(true))
1240 signal(SIGINT, sigint_handler);
1243 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1246 signal(SIGINT, SIG_DFL);
1254 static int cmd_pid(int argc, char *argv[]) {
1256 fprintf(stderr, "Too many arguments!\n");
1260 if(!connect_tincd(true) && !pid)
1263 printf("%d\n", pid);
1267 int rstrip(char *value) {
1268 int len = strlen(value);
1269 while(len && strchr("\t\r\n ", value[len - 1]))
1274 char *get_my_name(bool verbose) {
1275 FILE *f = fopen(tinc_conf, "r");
1278 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1284 while(fgets(buf, sizeof buf, f)) {
1285 int len = strcspn(buf, "\t =");
1287 value += strspn(value, "\t ");
1290 value += strspn(value, "\t ");
1295 if(strcasecmp(buf, "Name"))
1299 return strdup(value);
1305 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1309 const var_t variables[] = {
1310 /* Server configuration */
1311 {"AddressFamily", VAR_SERVER},
1312 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1313 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1314 {"BindToInterface", VAR_SERVER},
1315 {"Broadcast", VAR_SERVER | VAR_SAFE},
1316 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1317 {"DecrementTTL", VAR_SERVER},
1318 {"Device", VAR_SERVER},
1319 {"DeviceType", VAR_SERVER},
1320 {"DirectOnly", VAR_SERVER},
1321 {"Ed25519PrivateKeyFile", VAR_SERVER},
1322 {"ExperimentalProtocol", VAR_SERVER},
1323 {"Forwarding", VAR_SERVER},
1324 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1325 {"Hostnames", VAR_SERVER},
1326 {"IffOneQueue", VAR_SERVER},
1327 {"Interface", VAR_SERVER},
1328 {"KeyExpire", VAR_SERVER},
1329 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1330 {"LocalDiscovery", VAR_SERVER},
1331 {"MACExpire", VAR_SERVER},
1332 {"MaxConnectionBurst", VAR_SERVER},
1333 {"MaxOutputBufferSize", VAR_SERVER},
1334 {"MaxTimeout", VAR_SERVER},
1335 {"Mode", VAR_SERVER | VAR_SAFE},
1336 {"Name", VAR_SERVER},
1337 {"PingInterval", VAR_SERVER},
1338 {"PingTimeout", VAR_SERVER},
1339 {"PriorityInheritance", VAR_SERVER},
1340 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1341 {"PrivateKeyFile", VAR_SERVER},
1342 {"ProcessPriority", VAR_SERVER},
1343 {"Proxy", VAR_SERVER},
1344 {"ReplayWindow", VAR_SERVER},
1345 {"ScriptsExtension", VAR_SERVER},
1346 {"ScriptsInterpreter", VAR_SERVER},
1347 {"StrictSubnets", VAR_SERVER},
1348 {"TunnelServer", VAR_SERVER},
1349 {"UDPRcvBuf", VAR_SERVER},
1350 {"UDPSndBuf", VAR_SERVER},
1351 {"VDEGroup", VAR_SERVER},
1352 {"VDEPort", VAR_SERVER},
1353 /* Host configuration */
1354 {"Address", VAR_HOST | VAR_MULTIPLE},
1355 {"Cipher", VAR_SERVER | VAR_HOST},
1356 {"ClampMSS", VAR_SERVER | VAR_HOST},
1357 {"Compression", VAR_SERVER | VAR_HOST},
1358 {"Digest", VAR_SERVER | VAR_HOST},
1359 {"Ed25519PublicKey", VAR_HOST},
1360 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1361 {"IndirectData", VAR_SERVER | VAR_HOST},
1362 {"MACLength", VAR_SERVER | VAR_HOST},
1363 {"PMTU", VAR_SERVER | VAR_HOST},
1364 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1366 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1367 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1368 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1369 {"TCPOnly", VAR_SERVER | VAR_HOST},
1370 {"Weight", VAR_HOST | VAR_SAFE},
1374 static int cmd_config(int argc, char *argv[]) {
1376 fprintf(stderr, "Invalid number of arguments.\n");
1380 if(strcasecmp(argv[0], "config"))
1384 if(!strcasecmp(argv[1], "get")) {
1386 } else if(!strcasecmp(argv[1], "add")) {
1387 argv++, argc--, action = 1;
1388 } else if(!strcasecmp(argv[1], "del")) {
1389 argv++, argc--, action = -1;
1390 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1391 argv++, argc--, action = 0;
1395 fprintf(stderr, "Invalid number of arguments.\n");
1399 // Concatenate the rest of the command line
1400 strncpy(line, argv[1], sizeof line - 1);
1401 for(int i = 2; i < argc; i++) {
1402 strncat(line, " ", sizeof line - 1 - strlen(line));
1403 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1406 // Liberal parsing into node name, variable name and value.
1412 len = strcspn(line, "\t =");
1414 value += strspn(value, "\t ");
1417 value += strspn(value, "\t ");
1420 variable = strchr(line, '.');
1429 fprintf(stderr, "No variable given.\n");
1433 if(action >= 0 && !*value) {
1434 fprintf(stderr, "No value for variable given.\n");
1438 if(action < -1 && *value)
1441 /* Some simple checks. */
1443 bool warnonremove = false;
1445 for(int i = 0; variables[i].name; i++) {
1446 if(strcasecmp(variables[i].name, variable))
1450 variable = (char *)variables[i].name;
1452 /* Discourage use of obsolete variables. */
1454 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1456 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1458 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1463 /* Don't put server variables in host config files */
1465 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1467 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1469 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1474 /* Should this go into our own host config file? */
1476 if(!node && !(variables[i].type & VAR_SERVER)) {
1477 node = get_my_name(true);
1482 /* Change "add" into "set" for variables that do not allow multiple occurences.
1483 Turn on warnings when it seems variables might be removed unintentionally. */
1485 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1486 warnonremove = true;
1488 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1489 warnonremove = true;
1495 if(node && !check_id(node)) {
1496 fprintf(stderr, "Invalid name for node.\n");
1501 if(force || action < 0) {
1502 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1504 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1509 // Open the right configuration file.
1512 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, node);
1514 filename = tinc_conf;
1516 FILE *f = fopen(filename, "r");
1518 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1522 char *tmpfile = NULL;
1526 xasprintf(&tmpfile, "%s.config.tmp", filename);
1527 tf = fopen(tmpfile, "w");
1529 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1535 // Copy the file, making modifications on the fly, unless we are just getting a value.
1539 bool removed = false;
1542 while(fgets(buf1, sizeof buf1, f)) {
1543 buf1[sizeof buf1 - 1] = 0;
1544 strncpy(buf2, buf1, sizeof buf2);
1546 // Parse line in a simple way
1550 len = strcspn(buf2, "\t =");
1551 bvalue = buf2 + len;
1552 bvalue += strspn(bvalue, "\t ");
1553 if(*bvalue == '=') {
1555 bvalue += strspn(bvalue, "\t ");
1561 if(!strcasecmp(buf2, variable)) {
1565 printf("%s\n", bvalue);
1567 } else if(action == -1) {
1568 if(!*value || !strcasecmp(bvalue, value)) {
1573 } else if(action == 0) {
1574 // Warn if "set" was used for variables that can occur multiple times
1575 if(warnonremove && strcasecmp(bvalue, value))
1576 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1578 // Already set? Delete the rest...
1582 // Otherwise, replace.
1583 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1584 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1593 // Copy original line...
1594 if(fputs(buf1, tf) < 0) {
1595 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1599 // Add newline if it is missing...
1600 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1601 if(fputc('\n', tf) < 0) {
1602 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1609 // Make sure we read everything...
1610 if(ferror(f) || !feof(f)) {
1611 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1616 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1620 // Add new variable if necessary.
1621 if(action > 0 || (action == 0 && !set)) {
1622 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1623 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1630 fprintf(stderr, "No matching configuration variables found.\n");
1634 // Make sure we wrote everything...
1636 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1640 // Could we find what we had to remove?
1641 if(action < 0 && !removed) {
1643 fprintf(stderr, "No configuration variables deleted.\n");
1647 // Replace the configuration file with the new one
1649 if(remove(filename)) {
1650 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1654 if(rename(tmpfile, filename)) {
1655 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1659 // Silently try notifying a running tincd of changes.
1660 if(connect_tincd(false))
1661 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1666 bool check_id(const char *name) {
1670 for(int i = 0; i < strlen(name); i++) {
1671 if(!isalnum(name[i]) && name[i] != '_')
1678 static bool try_bind(int port) {
1679 struct addrinfo *ai = NULL;
1680 struct addrinfo hint = {
1681 .ai_flags = AI_PASSIVE,
1682 .ai_family = AF_UNSPEC,
1683 .ai_socktype = SOCK_STREAM,
1684 .ai_protocol = IPPROTO_TCP,
1688 snprintf(portstr, sizeof portstr, "%d", port);
1690 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1694 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1697 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1707 int check_port(char *name) {
1711 fprintf(stderr, "Warning: could not bind to port 655. ");
1713 for(int i = 0; i < 100; i++) {
1714 int port = 0x1000 + (rand() & 0x7fff);
1715 if(try_bind(port)) {
1717 xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1718 FILE *f = fopen(filename, "a");
1721 fprintf(stderr, "Please change tinc's Port manually.\n");
1725 fprintf(f, "Port = %d\n", port);
1727 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1732 fprintf(stderr, "Please change tinc's Port manually.\n");
1736 static int cmd_init(int argc, char *argv[]) {
1737 if(!access(tinc_conf, F_OK)) {
1738 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1743 fprintf(stderr, "Too many arguments!\n");
1745 } else if(argc < 2) {
1748 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1749 if(!fgets(buf, sizeof buf, stdin)) {
1750 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1753 int len = rstrip(buf);
1755 fprintf(stderr, "No name given!\n");
1760 fprintf(stderr, "No Name given!\n");
1764 name = strdup(argv[1]);
1766 fprintf(stderr, "No Name given!\n");
1771 if(!check_id(name)) {
1772 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1776 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1777 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1781 if(mkdir(confbase, 0777) && errno != EEXIST) {
1782 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1786 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1787 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1791 FILE *f = fopen(tinc_conf, "w");
1793 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1797 fprintf(f, "Name = %s\n", name);
1800 if(!rsa_keygen(2048, false) || !ed25519_keygen(false))
1807 xasprintf(&filename, "%s" SLASH "tinc-up", confbase);
1808 if(access(filename, F_OK)) {
1809 FILE *f = fopenmask(filename, "w", 0777);
1811 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1814 fprintf(f, "#!/bin/sh\n\necho 'Unconfigured tinc-up script, please edit '$0'!'\n\n#ifconfig $INTERFACE <your vpn IP address> netmask <netmask of whole VPN>\n");
1823 static int cmd_generate_keys(int argc, char *argv[]) {
1825 fprintf(stderr, "Too many arguments!\n");
1830 name = get_my_name(false);
1832 return !(rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true) && ed25519_keygen(true));
1835 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1837 fprintf(stderr, "Too many arguments!\n");
1842 name = get_my_name(false);
1844 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1847 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1849 fprintf(stderr, "Too many arguments!\n");
1854 name = get_my_name(false);
1856 return !ed25519_keygen(true);
1859 static int cmd_help(int argc, char *argv[]) {
1864 static int cmd_version(int argc, char *argv[]) {
1866 fprintf(stderr, "Too many arguments!\n");
1874 static int cmd_info(int argc, char *argv[]) {
1876 fprintf(stderr, "Invalid number of arguments.\n");
1880 if(!connect_tincd(true))
1883 return info(fd, argv[1]);
1886 static const char *conffiles[] = {
1897 static int cmd_edit(int argc, char *argv[]) {
1899 fprintf(stderr, "Invalid number of arguments.\n");
1903 char *filename = NULL;
1905 if(strncmp(argv[1], "hosts" SLASH, 6)) {
1906 for(int i = 0; conffiles[i]; i++) {
1907 if(!strcmp(argv[1], conffiles[i])) {
1908 xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]);
1917 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]);
1918 char *dash = strchr(argv[1], '-');
1921 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
1922 fprintf(stderr, "Invalid configuration filename.\n");
1930 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
1932 xasprintf(&command, "edit \"%s\"", filename);
1934 int result = system(command);
1938 // Silently try notifying a running tincd of changes.
1939 if(connect_tincd(false))
1940 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1945 static int export(const char *name, FILE *out) {
1947 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
1948 FILE *in = fopen(filename, "r");
1950 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1954 fprintf(out, "Name = %s\n", name);
1956 while(fgets(buf, sizeof buf, in)) {
1957 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
1962 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
1971 static int cmd_export(int argc, char *argv[]) {
1973 fprintf(stderr, "Too many arguments!\n");
1977 char *name = get_my_name(true);
1981 int result = export(name, stdout);
1989 static int cmd_export_all(int argc, char *argv[]) {
1991 fprintf(stderr, "Too many arguments!\n");
1995 DIR *dir = opendir(hosts_dir);
1997 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2005 while((ent = readdir(dir))) {
2006 if(!check_id(ent->d_name))
2012 printf("#---------------------------------------------------------------#\n");
2014 result |= export(ent->d_name, stdout);
2023 static int cmd_import(int argc, char *argv[]) {
2025 fprintf(stderr, "Too many arguments!\n");
2034 char *filename = NULL;
2036 bool firstline = true;
2038 while(fgets(buf, sizeof buf, in)) {
2039 if(sscanf(buf, "Name = %s", name) == 1) {
2042 if(!check_id(name)) {
2043 fprintf(stderr, "Invalid Name in input!\n");
2051 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
2053 if(!force && !access(filename, F_OK)) {
2054 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2059 out = fopen(filename, "w");
2061 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2067 } else if(firstline) {
2068 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2073 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2077 if(fputs(buf, out) < 0) {
2078 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2088 fprintf(stderr, "Imported %d host configuration files.\n", count);
2091 fprintf(stderr, "No host configuration files imported.\n");
2096 static int cmd_exchange(int argc, char *argv[]) {
2097 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2100 static int cmd_exchange_all(int argc, char *argv[]) {
2101 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2104 static int switch_network(char *name) {
2116 free(unixsocketname);
2117 unixsocketname = NULL;
2123 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2126 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2127 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2128 xasprintf(&prompt, "%s> ", identname);
2133 static int cmd_network(int argc, char *argv[]) {
2135 fprintf(stderr, "Too many arguments!\n");
2140 return switch_network(argv[1]);
2142 DIR *dir = opendir(confdir);
2144 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2149 while((ent = readdir(dir))) {
2150 if(*ent->d_name == '.')
2153 if(!strcmp(ent->d_name, "tinc.conf")) {
2159 xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2160 if(!access(fname, R_OK))
2161 printf("%s\n", ent->d_name);
2170 static const struct {
2171 const char *command;
2172 int (*function)(int argc, char *argv[]);
2175 {"start", cmd_start},
2177 {"restart", cmd_restart},
2178 {"reload", cmd_reload},
2180 {"purge", cmd_purge},
2181 {"debug", cmd_debug},
2182 {"retry", cmd_retry},
2183 {"connect", cmd_connect},
2184 {"disconnect", cmd_disconnect},
2189 {"config", cmd_config, true},
2190 {"add", cmd_config},
2191 {"del", cmd_config},
2192 {"get", cmd_config},
2193 {"set", cmd_config},
2195 {"generate-keys", cmd_generate_keys},
2196 {"generate-rsa-keys", cmd_generate_rsa_keys},
2197 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2199 {"version", cmd_version},
2202 {"export", cmd_export},
2203 {"export-all", cmd_export_all},
2204 {"import", cmd_import},
2205 {"exchange", cmd_exchange},
2206 {"exchange-all", cmd_exchange_all},
2207 {"invite", cmd_invite},
2209 {"network", cmd_network},
2213 #ifdef HAVE_READLINE
2214 static char *complete_command(const char *text, int state) {
2222 while(commands[i].command) {
2223 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2224 return xstrdup(commands[i].command);
2231 static char *complete_dump(const char *text, int state) {
2232 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2241 if(!strncasecmp(matches[i], text, strlen(text)))
2242 return xstrdup(matches[i]);
2249 static char *complete_config(const char *text, int state) {
2257 while(variables[i].name) {
2258 char *dot = strchr(text, '.');
2260 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2262 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2266 if(!strncasecmp(variables[i].name, text, strlen(text)))
2267 return xstrdup(variables[i].name);
2275 static char *complete_info(const char *text, int state) {
2279 if(!connect_tincd(false))
2281 // Check the list of nodes
2282 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2283 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2286 while(recvline(fd, line, sizeof line)) {
2288 int n = sscanf(line, "%d %d %s", &code, &req, item);
2298 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2302 if(!strncmp(item, text, strlen(text)))
2303 return xstrdup(strip_weight(item));
2309 static char *complete_nothing(const char *text, int state) {
2313 static char **completion (const char *text, int start, int end) {
2314 char **matches = NULL;
2317 matches = rl_completion_matches(text, complete_command);
2318 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2319 matches = rl_completion_matches(text, complete_dump);
2320 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2321 matches = rl_completion_matches(text, complete_config);
2322 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2323 matches = rl_completion_matches(text, complete_config);
2324 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2325 matches = rl_completion_matches(text, complete_config);
2326 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2327 matches = rl_completion_matches(text, complete_config);
2328 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2329 matches = rl_completion_matches(text, complete_info);
2335 static int cmd_shell(int argc, char *argv[]) {
2336 xasprintf(&prompt, "%s> ", identname);
2340 int maxargs = argc + 16;
2341 char **nargv = xmalloc(maxargs * sizeof *nargv);
2343 for(int i = 0; i < argc; i++)
2346 #ifdef HAVE_READLINE
2347 rl_readline_name = "tinc";
2348 rl_completion_entry_function = complete_nothing;
2349 rl_attempted_completion_function = completion;
2350 rl_filename_completion_desired = 0;
2355 #ifdef HAVE_READLINE
2359 rl_basic_word_break_characters = "\t\n ";
2360 line = readline(prompt);
2362 copy = xstrdup(line);
2364 line = fgets(buf, sizeof buf, stdin);
2368 fputs(prompt, stdout);
2370 line = fgets(buf, sizeof buf, stdin);
2376 /* Ignore comments */
2384 char *p = line + strspn(line, " \t\n");
2385 char *next = strtok(p, " \t\n");
2388 if(nargc >= maxargs) {
2389 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2392 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2397 next = strtok(NULL, " \t\n");
2403 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2408 for(int i = 0; commands[i].command; i++) {
2409 if(!strcasecmp(nargv[argc], commands[i].command)) {
2410 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2416 #ifdef HAVE_READLINE
2422 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2435 int main(int argc, char *argv[]) {
2436 program_name = argv[0];
2440 if(!parse_options(argc, argv))
2444 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2445 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2458 static struct WSAData wsa_state;
2460 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2461 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2469 tty = isatty(0) && isatty(1);
2472 return cmd_shell(argc, argv);
2474 for(int i = 0; commands[i].command; i++) {
2475 if(!strcasecmp(argv[optind], commands[i].command))
2476 return commands[i].function(argc - optind, argv + optind);
2479 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);