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 {"batch", no_argument, NULL, 'b'},
79 {"config", required_argument, NULL, 'c'},
80 {"net", required_argument, NULL, 'n'},
81 {"help", no_argument, NULL, 1},
82 {"version", no_argument, NULL, 2},
83 {"pidfile", required_argument, NULL, 3},
84 {"force", no_argument, NULL, 4},
88 static void version(void) {
89 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
90 VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
91 printf("Copyright (C) 1998-2012 Ivo Timmermans, Guus Sliepen and others.\n"
92 "See the AUTHORS file for a complete list.\n\n"
93 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
94 "and you are welcome to redistribute it under certain conditions;\n"
95 "see the file COPYING for details.\n");
98 static void usage(bool status) {
100 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
102 printf("Usage: %s [options] command\n\n", program_name);
103 printf("Valid options are:\n"
104 " -b, --batch Don't ask for anything (non-interactive mode).\n"
105 " -c, --config=DIR Read configuration options from DIR.\n"
106 " -n, --net=NETNAME Connect to net NETNAME.\n"
107 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
108 " --help Display this help and exit.\n"
109 " --version Output version information and exit.\n"
111 "Valid commands are:\n"
112 " init [name] Create initial configuration files.\n"
113 " get VARIABLE Print current value of VARIABLE\n"
114 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
115 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
116 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
117 " start [tincd options] Start tincd.\n"
118 " stop Stop tincd.\n"
119 " restart [tincd options] Restart tincd.\n"
120 " reload Partially reload configuration of running tincd.\n"
121 " pid Show PID of currently running tincd.\n"
122 " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
123 " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
124 " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
125 " dump Dump a list of one of the following things:\n"
126 " [reachable] nodes - all known nodes in the VPN\n"
127 " edges - all known connections in the VPN\n"
128 " subnets - all known subnets in the VPN\n"
129 " connections - all meta connections with ourself\n"
130 " [di]graph - graph of the VPN in dotty format\n"
131 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
132 " purge Purge unreachable nodes\n"
133 " debug N Set debug level\n"
134 " retry Retry all outgoing connections\n"
135 " disconnect NODE Close meta connection with NODE\n"
137 " top Show real-time statistics\n"
139 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
140 " log [level] Dump log output [up to the specified level]\n"
141 " export Export host configuration of local node to standard output\n"
142 " export-all Export all host configuration files to standard output\n"
143 " import [--force] Import host configuration file(s) from standard input\n"
144 " exchange [--force] Same as export followed by import\n"
145 " exchange-all [--force] Same as export-all followed by import\n"
146 " invite NODE [...] Generate an invitation for NODE\n"
147 " join INVITATION Join a VPN using an INVITIATION\n"
148 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
150 printf("Report bugs to tinc@tinc-vpn.org.\n");
154 static bool parse_options(int argc, char **argv) {
156 int option_index = 0;
158 while((r = getopt_long(argc, argv, "+c:n:", long_options, &option_index)) != EOF) {
160 case 0: /* long option */
167 case 'c': /* config file */
168 confbase = xstrdup(optarg);
169 confbasegiven = true;
172 case 'n': /* net name given */
173 netname = xstrdup(optarg);
176 case 1: /* show help */
180 case 2: /* show version */
184 case 3: /* open control socket here */
185 pidfilename = xstrdup(optarg);
192 case '?': /* wrong options */
201 if(!netname && (netname = getenv("NETNAME")))
202 netname = xstrdup(netname);
204 /* netname "." is special: a "top-level name" */
206 if(netname && (!*netname || !strcmp(netname, "."))) {
211 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
212 fprintf(stderr, "Invalid character in netname!\n");
219 /* Open a file with the desired permissions, minus the umask.
220 Also, if we want to create an executable file, we call fchmod()
221 to set the executable bits. */
223 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
224 mode_t mask = umask(0);
227 FILE *f = fopen(filename, mode);
229 if((perms & 0444) && f)
230 fchmod(fileno(f), perms);
236 static void disable_old_keys(const char *filename, const char *what) {
237 char tmpfile[PATH_MAX] = "";
239 bool disabled = false;
244 r = fopen(filename, "r");
248 snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
250 struct stat st = {.st_mode = 0600};
251 fstat(fileno(r), &st);
252 w = fopenmask(tmpfile, "w", st.st_mode);
254 while(fgets(buf, sizeof buf, r)) {
255 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
256 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
262 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
268 if(block || ed25519pubkey)
270 if(fputs(buf, w) < 0) {
276 if(block && !strncmp(buf, "-----END ", 9))
283 if(ferror(r) || fclose(r) < 0)
288 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
295 // We cannot atomically replace files on Windows.
296 char bakfile[PATH_MAX] = "";
297 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
298 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
299 rename(bakfile, filename);
301 if(rename(tmpfile, filename)) {
303 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
308 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
315 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
321 /* Check stdin and stdout */
323 /* Ask for a file and/or directory name. */
324 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
326 if(fgets(buf, sizeof buf, stdin) == NULL) {
327 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
331 size_t len = strlen(buf);
340 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
342 if(filename[0] != '/') {
344 /* The directory is a relative path or a filename. */
345 directory = get_current_dir_name();
346 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
350 disable_old_keys(filename, what);
352 /* Open it first to keep the inode busy */
354 r = fopenmask(filename, mode, perms);
357 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
365 Generate a public/private Ed25519 keypair, and ask for a file to store
368 static bool ed25519_keygen(bool ask) {
371 char *pubname, *privname;
373 fprintf(stderr, "Generating Ed25519 keypair:\n");
375 if(!(key = ecdsa_generate())) {
376 fprintf(stderr, "Error during key generation!\n");
379 fprintf(stderr, "Done.\n");
381 xasprintf(&privname, "%s" SLASH "ed25519_key.priv", confbase);
382 f = ask_and_open(privname, "private Ed25519 key", "a", ask, 0600);
388 if(!ecdsa_write_pem_private_key(key, f)) {
389 fprintf(stderr, "Error writing private key!\n");
398 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
400 xasprintf(&pubname, "%s" SLASH "ed25519_key.pub", confbase);
402 f = ask_and_open(pubname, "public Ed25519 key", "a", ask, 0666);
408 char *pubkey = ecdsa_get_base64_public_key(key);
409 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
419 Generate a public/private RSA keypair, and ask for a file to store
422 static bool rsa_keygen(int bits, bool ask) {
425 char *pubname, *privname;
427 // Make sure the key size is a multiple of 8 bits.
430 // Force them to be between 1024 and 8192 bits long.
436 fprintf(stderr, "Generating %d bits keys:\n", bits);
438 if(!(key = rsa_generate(bits, 0x10001))) {
439 fprintf(stderr, "Error during key generation!\n");
442 fprintf(stderr, "Done.\n");
444 xasprintf(&privname, "%s" SLASH "rsa_key.priv", confbase);
445 f = ask_and_open(privname, "private RSA key", "a", ask, 0600);
451 if(!rsa_write_pem_private_key(key, f)) {
452 fprintf(stderr, "Error writing private key!\n");
461 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
463 xasprintf(&pubname, "%s" SLASH "rsa_key.pub", confbase);
465 f = ask_and_open(pubname, "public RSA key", "a", ask, 0666);
471 if(!rsa_write_pem_public_key(key, f)) {
472 fprintf(stderr, "Error writing public key!\n");
487 bool recvline(int fd, char *line, size_t len) {
488 char *newline = NULL;
493 while(!(newline = memchr(buffer, '\n', blen))) {
494 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
495 if(result == -1 && sockerrno == EINTR)
502 if(newline - buffer >= len)
505 len = newline - buffer;
507 memcpy(line, buffer, len);
509 memmove(buffer, newline + 1, blen - len - 1);
515 bool recvdata(int fd, char *data, size_t len) {
520 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
521 if(result == -1 && sockerrno == EINTR)
528 memcpy(data, buffer, len);
529 memmove(buffer, buffer + len, blen - len);
535 bool sendline(int fd, char *format, ...) {
536 static char buffer[4096];
541 va_start(ap, format);
542 blen = vsnprintf(buffer, sizeof buffer, format, ap);
545 if(blen < 1 || blen >= sizeof buffer)
552 int result = send(fd, p, blen, MSG_NOSIGNAL);
553 if(result == -1 && sockerrno == EINTR)
564 static void pcap(int fd, FILE *out, int snaplen) {
565 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
573 uint32_t tz_accuracy;
580 snaplen ?: sizeof data,
593 fwrite(&header, sizeof header, 1, out);
597 while(recvline(fd, line, sizeof line)) {
599 int n = sscanf(line, "%d %d %d", &code, &req, &len);
600 gettimeofday(&tv, NULL);
601 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
603 if(!recvdata(fd, data, len))
605 packet.tv_sec = tv.tv_sec;
606 packet.tv_usec = tv.tv_usec;
608 packet.origlen = len;
609 fwrite(&packet, sizeof packet, 1, out);
610 fwrite(data, len, 1, out);
615 static void logcontrol(int fd, FILE *out, int level) {
616 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
620 while(recvline(fd, line, sizeof line)) {
622 int n = sscanf(line, "%d %d %d", &code, &req, &len);
623 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
625 if(!recvdata(fd, data, len))
627 fwrite(data, len, 1, out);
634 static bool remove_service(void) {
635 SC_HANDLE manager = NULL;
636 SC_HANDLE service = NULL;
637 SERVICE_STATUS status = {0};
639 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
641 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
645 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
648 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
652 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
653 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
655 fprintf(stderr, "%s service stopped\n", identname);
657 if(!DeleteService(service)) {
658 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
662 fprintf(stderr, "%s service removed\n", identname);
668 bool connect_tincd(bool verbose) {
673 struct timeval tv = {0, 0};
674 if(select(fd + 1, &r, NULL, NULL, &tv)) {
675 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
683 FILE *f = fopen(pidfilename, "r");
686 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
693 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
695 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
703 struct sockaddr_un sa;
704 sa.sun_family = AF_UNIX;
705 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
707 fd = socket(AF_UNIX, SOCK_STREAM, 0);
710 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
714 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
716 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
722 struct addrinfo hints = {
723 .ai_family = AF_UNSPEC,
724 .ai_socktype = SOCK_STREAM,
725 .ai_protocol = IPPROTO_TCP,
729 struct addrinfo *res = NULL;
731 if(getaddrinfo(host, port, &hints, &res) || !res) {
733 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
737 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
740 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
745 unsigned long arg = 0;
747 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
749 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
753 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
755 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
765 static const int one = 1;
766 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
772 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
774 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
780 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
782 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
784 fprintf(stderr, "Could not fully establish control socket connection\n");
794 static int cmd_start(int argc, char *argv[]) {
795 if(connect_tincd(false)) {
797 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
799 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
804 char *slash = strrchr(program_name, '/');
807 if ((c = strrchr(program_name, '\\')) > slash)
812 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
817 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
822 Windows has no real concept of an "argv array". A command line is just one string.
823 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
824 it uses quotes to handle spaces in arguments.
825 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
826 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
827 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
829 xasprintf(&arg0, "\"%s\"", arg0);
831 nargv[nargc++] = arg0;
832 for(int i = 1; i < optind; i++)
833 nargv[nargc++] = orig_argv[i];
834 for(int i = 1; i < argc; i++)
835 nargv[nargc++] = argv[i];
838 int status = spawnvp(_P_WAIT, c, nargv);
840 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
847 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
853 exit(execvp(c, nargv));
857 int status = -1, result;
859 signal(SIGINT, SIG_IGN);
861 result = waitpid(pid, &status, 0);
863 signal(SIGINT, SIG_DFL);
866 if(result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
867 fprintf(stderr, "Error starting %s\n", c);
875 static int cmd_stop(int argc, char *argv[]) {
877 fprintf(stderr, "Too many arguments!\n");
882 if(!connect_tincd(true)) {
884 if(kill(pid, SIGTERM)) {
885 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
889 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
890 waitpid(pid, NULL, 0);
897 sendline(fd, "%d %d", CONTROL, REQ_STOP);
899 while(recvline(fd, line, sizeof line)) {
900 // Wait for tincd to close the connection...
903 if(!remove_service())
913 static int cmd_restart(int argc, char *argv[]) {
915 return cmd_start(argc, argv);
918 static int cmd_reload(int argc, char *argv[]) {
920 fprintf(stderr, "Too many arguments!\n");
924 if(!connect_tincd(true))
927 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
928 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
929 fprintf(stderr, "Could not reload configuration.\n");
937 static int cmd_dump(int argc, char *argv[]) {
938 bool only_reachable = false;
940 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
941 if(strcasecmp(argv[2], "nodes")) {
942 fprintf(stderr, "`reachable' only supported for nodes.\n");
946 only_reachable = true;
952 fprintf(stderr, "Invalid number of arguments.\n");
957 if(!connect_tincd(true))
962 if(!strcasecmp(argv[1], "nodes"))
963 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
964 else if(!strcasecmp(argv[1], "edges"))
965 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
966 else if(!strcasecmp(argv[1], "subnets"))
967 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
968 else if(!strcasecmp(argv[1], "connections"))
969 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
970 else if(!strcasecmp(argv[1], "graph")) {
971 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
972 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
974 } else if(!strcasecmp(argv[1], "digraph")) {
975 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
976 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
979 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
986 else if(do_graph == 2)
987 printf("digraph {\n");
989 while(recvline(fd, line, sizeof line)) {
990 char node1[4096], node2[4096];
991 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
993 if(do_graph && req == REQ_DUMP_NODES)
1011 char local_host[4096];
1012 char local_port[4096];
1015 int cipher, digest, maclength, compression, distance, socket, weight;
1016 short int pmtu, minmtu, maxmtu;
1017 unsigned int options, status_int;
1018 node_status_t status;
1019 long int last_state_change;
1022 case REQ_DUMP_NODES: {
1023 int n = sscanf(line, "%*d %*d %s %s %s port %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
1025 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1029 memcpy(&status, &status_int, sizeof status);
1032 const char *color = "black";
1033 if(!strcmp(host, "MYSELF"))
1035 else if(!status.reachable)
1037 else if(strcmp(via, node))
1039 else if(!status.validkey)
1043 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1045 if(only_reachable && !status.reachable)
1047 printf("%s id %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",
1048 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1052 case REQ_DUMP_EDGES: {
1053 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);
1055 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1060 float w = 1 + 65536.0 / weight;
1061 if(do_graph == 1 && strcmp(node1, node2) > 0)
1062 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1063 else if(do_graph == 2)
1064 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1066 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);
1070 case REQ_DUMP_SUBNETS: {
1071 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1073 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1076 printf("%s owner %s\n", strip_weight(subnet), node);
1079 case REQ_DUMP_CONNECTIONS: {
1080 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1082 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1085 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1089 fprintf(stderr, "Unable to parse dump from tincd.\n");
1094 fprintf(stderr, "Error receiving dump.\n");
1098 static int cmd_purge(int argc, char *argv[]) {
1100 fprintf(stderr, "Too many arguments!\n");
1104 if(!connect_tincd(true))
1107 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1108 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1109 fprintf(stderr, "Could not purge old information.\n");
1116 static int cmd_debug(int argc, char *argv[]) {
1118 fprintf(stderr, "Invalid number of arguments.\n");
1122 if(!connect_tincd(true))
1125 int debuglevel = atoi(argv[1]);
1128 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1129 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1130 fprintf(stderr, "Could not set debug level.\n");
1134 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1138 static int cmd_retry(int argc, char *argv[]) {
1140 fprintf(stderr, "Too many arguments!\n");
1144 if(!connect_tincd(true))
1147 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1148 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1149 fprintf(stderr, "Could not retry outgoing connections.\n");
1156 static int cmd_connect(int argc, char *argv[]) {
1158 fprintf(stderr, "Invalid number of arguments.\n");
1162 if(!check_id(argv[1])) {
1163 fprintf(stderr, "Invalid name for node.\n");
1167 if(!connect_tincd(true))
1170 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1171 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1172 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1179 static int cmd_disconnect(int argc, char *argv[]) {
1181 fprintf(stderr, "Invalid number of arguments.\n");
1185 if(!check_id(argv[1])) {
1186 fprintf(stderr, "Invalid name for node.\n");
1190 if(!connect_tincd(true))
1193 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1194 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1195 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1202 static int cmd_top(int argc, char *argv[]) {
1204 fprintf(stderr, "Too many arguments!\n");
1209 if(!connect_tincd(true))
1215 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1220 static int cmd_pcap(int argc, char *argv[]) {
1222 fprintf(stderr, "Too many arguments!\n");
1226 if(!connect_tincd(true))
1229 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1234 static void sigint_handler(int sig) {
1235 fprintf(stderr, "\n");
1236 shutdown(fd, SHUT_RDWR);
1240 static int cmd_log(int argc, char *argv[]) {
1242 fprintf(stderr, "Too many arguments!\n");
1246 if(!connect_tincd(true))
1250 signal(SIGINT, sigint_handler);
1253 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1256 signal(SIGINT, SIG_DFL);
1264 static int cmd_pid(int argc, char *argv[]) {
1266 fprintf(stderr, "Too many arguments!\n");
1270 if(!connect_tincd(true) && !pid)
1273 printf("%d\n", pid);
1277 int rstrip(char *value) {
1278 int len = strlen(value);
1279 while(len && strchr("\t\r\n ", value[len - 1]))
1284 char *get_my_name(bool verbose) {
1285 FILE *f = fopen(tinc_conf, "r");
1288 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1294 while(fgets(buf, sizeof buf, f)) {
1295 int len = strcspn(buf, "\t =");
1297 value += strspn(value, "\t ");
1300 value += strspn(value, "\t ");
1305 if(strcasecmp(buf, "Name"))
1309 return replace_name(value);
1315 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1319 const var_t variables[] = {
1320 /* Server configuration */
1321 {"AddressFamily", VAR_SERVER},
1322 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1323 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1324 {"BindToInterface", VAR_SERVER},
1325 {"Broadcast", VAR_SERVER | VAR_SAFE},
1326 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1327 {"DecrementTTL", VAR_SERVER},
1328 {"Device", VAR_SERVER},
1329 {"DeviceType", VAR_SERVER},
1330 {"DirectOnly", VAR_SERVER},
1331 {"Ed25519PrivateKeyFile", VAR_SERVER},
1332 {"ExperimentalProtocol", VAR_SERVER},
1333 {"Forwarding", VAR_SERVER},
1334 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1335 {"Hostnames", VAR_SERVER},
1336 {"IffOneQueue", VAR_SERVER},
1337 {"Interface", VAR_SERVER},
1338 {"KeyExpire", VAR_SERVER},
1339 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1340 {"LocalDiscovery", VAR_SERVER},
1341 {"MACExpire", VAR_SERVER},
1342 {"MaxConnectionBurst", VAR_SERVER},
1343 {"MaxOutputBufferSize", VAR_SERVER},
1344 {"MaxTimeout", VAR_SERVER},
1345 {"Mode", VAR_SERVER | VAR_SAFE},
1346 {"Name", VAR_SERVER},
1347 {"PingInterval", VAR_SERVER},
1348 {"PingTimeout", VAR_SERVER},
1349 {"PriorityInheritance", VAR_SERVER},
1350 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1351 {"PrivateKeyFile", VAR_SERVER},
1352 {"ProcessPriority", VAR_SERVER},
1353 {"Proxy", VAR_SERVER},
1354 {"ReplayWindow", VAR_SERVER},
1355 {"ScriptsExtension", VAR_SERVER},
1356 {"ScriptsInterpreter", VAR_SERVER},
1357 {"StrictSubnets", VAR_SERVER},
1358 {"TunnelServer", VAR_SERVER},
1359 {"UDPRcvBuf", VAR_SERVER},
1360 {"UDPSndBuf", VAR_SERVER},
1361 {"VDEGroup", VAR_SERVER},
1362 {"VDEPort", VAR_SERVER},
1363 /* Host configuration */
1364 {"Address", VAR_HOST | VAR_MULTIPLE},
1365 {"Cipher", VAR_SERVER | VAR_HOST},
1366 {"ClampMSS", VAR_SERVER | VAR_HOST},
1367 {"Compression", VAR_SERVER | VAR_HOST},
1368 {"Digest", VAR_SERVER | VAR_HOST},
1369 {"Ed25519PublicKey", VAR_HOST},
1370 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1371 {"IndirectData", VAR_SERVER | VAR_HOST},
1372 {"MACLength", VAR_SERVER | VAR_HOST},
1373 {"PMTU", VAR_SERVER | VAR_HOST},
1374 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1376 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1377 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1378 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1379 {"TCPOnly", VAR_SERVER | VAR_HOST},
1380 {"Weight", VAR_HOST | VAR_SAFE},
1384 static int cmd_config(int argc, char *argv[]) {
1386 fprintf(stderr, "Invalid number of arguments.\n");
1390 if(strcasecmp(argv[0], "config"))
1394 if(!strcasecmp(argv[1], "get")) {
1396 } else if(!strcasecmp(argv[1], "add")) {
1397 argv++, argc--, action = 1;
1398 } else if(!strcasecmp(argv[1], "del")) {
1399 argv++, argc--, action = -1;
1400 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1401 argv++, argc--, action = 0;
1405 fprintf(stderr, "Invalid number of arguments.\n");
1409 // Concatenate the rest of the command line
1410 strncpy(line, argv[1], sizeof line - 1);
1411 for(int i = 2; i < argc; i++) {
1412 strncat(line, " ", sizeof line - 1 - strlen(line));
1413 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1416 // Liberal parsing into node name, variable name and value.
1422 len = strcspn(line, "\t =");
1424 value += strspn(value, "\t ");
1427 value += strspn(value, "\t ");
1430 variable = strchr(line, '.');
1439 fprintf(stderr, "No variable given.\n");
1443 if(action >= 0 && !*value) {
1444 fprintf(stderr, "No value for variable given.\n");
1448 if(action < -1 && *value)
1451 /* Some simple checks. */
1453 bool warnonremove = false;
1455 for(int i = 0; variables[i].name; i++) {
1456 if(strcasecmp(variables[i].name, variable))
1460 variable = (char *)variables[i].name;
1462 /* Discourage use of obsolete variables. */
1464 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1466 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1468 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1473 /* Don't put server variables in host config files */
1475 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1477 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1479 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1484 /* Should this go into our own host config file? */
1486 if(!node && !(variables[i].type & VAR_SERVER)) {
1487 node = get_my_name(true);
1492 /* Change "add" into "set" for variables that do not allow multiple occurences.
1493 Turn on warnings when it seems variables might be removed unintentionally. */
1495 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1496 warnonremove = true;
1498 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1499 warnonremove = true;
1505 if(node && !check_id(node)) {
1506 fprintf(stderr, "Invalid name for node.\n");
1511 if(force || action < 0) {
1512 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1514 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1519 // Open the right configuration file.
1522 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, node);
1524 filename = tinc_conf;
1526 FILE *f = fopen(filename, "r");
1528 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1532 char *tmpfile = NULL;
1536 xasprintf(&tmpfile, "%s.config.tmp", filename);
1537 tf = fopen(tmpfile, "w");
1539 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1545 // Copy the file, making modifications on the fly, unless we are just getting a value.
1549 bool removed = false;
1552 while(fgets(buf1, sizeof buf1, f)) {
1553 buf1[sizeof buf1 - 1] = 0;
1554 strncpy(buf2, buf1, sizeof buf2);
1556 // Parse line in a simple way
1560 len = strcspn(buf2, "\t =");
1561 bvalue = buf2 + len;
1562 bvalue += strspn(bvalue, "\t ");
1563 if(*bvalue == '=') {
1565 bvalue += strspn(bvalue, "\t ");
1571 if(!strcasecmp(buf2, variable)) {
1575 printf("%s\n", bvalue);
1577 } else if(action == -1) {
1578 if(!*value || !strcasecmp(bvalue, value)) {
1583 } else if(action == 0) {
1584 // Warn if "set" was used for variables that can occur multiple times
1585 if(warnonremove && strcasecmp(bvalue, value))
1586 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1588 // Already set? Delete the rest...
1592 // Otherwise, replace.
1593 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1594 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1603 // Copy original line...
1604 if(fputs(buf1, tf) < 0) {
1605 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1609 // Add newline if it is missing...
1610 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1611 if(fputc('\n', tf) < 0) {
1612 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1619 // Make sure we read everything...
1620 if(ferror(f) || !feof(f)) {
1621 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1626 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1630 // Add new variable if necessary.
1631 if(action > 0 || (action == 0 && !set)) {
1632 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1633 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1642 fprintf(stderr, "No matching configuration variables found.\n");
1647 // Make sure we wrote everything...
1649 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1653 // Could we find what we had to remove?
1654 if(action < 0 && !removed) {
1656 fprintf(stderr, "No configuration variables deleted.\n");
1660 // Replace the configuration file with the new one
1662 if(remove(filename)) {
1663 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1667 if(rename(tmpfile, filename)) {
1668 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1672 // Silently try notifying a running tincd of changes.
1673 if(connect_tincd(false))
1674 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1679 static bool try_bind(int port) {
1680 struct addrinfo *ai = NULL;
1681 struct addrinfo hint = {
1682 .ai_flags = AI_PASSIVE,
1683 .ai_family = AF_UNSPEC,
1684 .ai_socktype = SOCK_STREAM,
1685 .ai_protocol = IPPROTO_TCP,
1689 snprintf(portstr, sizeof portstr, "%d", port);
1691 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1695 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1698 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1708 int check_port(char *name) {
1712 fprintf(stderr, "Warning: could not bind to port 655. ");
1714 for(int i = 0; i < 100; i++) {
1715 int port = 0x1000 + (rand() & 0x7fff);
1716 if(try_bind(port)) {
1718 xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1719 FILE *f = fopen(filename, "a");
1722 fprintf(stderr, "Please change tinc's Port manually.\n");
1726 fprintf(f, "Port = %d\n", port);
1728 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1733 fprintf(stderr, "Please change tinc's Port manually.\n");
1737 static int cmd_init(int argc, char *argv[]) {
1738 if(!access(tinc_conf, F_OK)) {
1739 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1744 fprintf(stderr, "Too many arguments!\n");
1746 } else if(argc < 2) {
1749 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1750 if(!fgets(buf, sizeof buf, stdin)) {
1751 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1754 int len = rstrip(buf);
1756 fprintf(stderr, "No name given!\n");
1761 fprintf(stderr, "No Name given!\n");
1765 name = strdup(argv[1]);
1767 fprintf(stderr, "No Name given!\n");
1772 if(!check_id(name)) {
1773 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1777 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1778 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1782 if(mkdir(confbase, 0777) && errno != EEXIST) {
1783 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1787 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1788 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1792 FILE *f = fopen(tinc_conf, "w");
1794 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1798 fprintf(f, "Name = %s\n", name);
1801 if(!rsa_keygen(2048, false) || !ed25519_keygen(false))
1808 xasprintf(&filename, "%s" SLASH "tinc-up", confbase);
1809 if(access(filename, F_OK)) {
1810 FILE *f = fopenmask(filename, "w", 0777);
1812 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1815 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");
1824 static int cmd_generate_keys(int argc, char *argv[]) {
1826 fprintf(stderr, "Too many arguments!\n");
1831 name = get_my_name(false);
1833 return !(rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true) && ed25519_keygen(true));
1836 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1838 fprintf(stderr, "Too many arguments!\n");
1843 name = get_my_name(false);
1845 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1848 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1850 fprintf(stderr, "Too many arguments!\n");
1855 name = get_my_name(false);
1857 return !ed25519_keygen(true);
1860 static int cmd_help(int argc, char *argv[]) {
1865 static int cmd_version(int argc, char *argv[]) {
1867 fprintf(stderr, "Too many arguments!\n");
1875 static int cmd_info(int argc, char *argv[]) {
1877 fprintf(stderr, "Invalid number of arguments.\n");
1881 if(!connect_tincd(true))
1884 return info(fd, argv[1]);
1887 static const char *conffiles[] = {
1898 static int cmd_edit(int argc, char *argv[]) {
1900 fprintf(stderr, "Invalid number of arguments.\n");
1904 char *filename = NULL;
1906 if(strncmp(argv[1], "hosts" SLASH, 6)) {
1907 for(int i = 0; conffiles[i]; i++) {
1908 if(!strcmp(argv[1], conffiles[i])) {
1909 xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]);
1918 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]);
1919 char *dash = strchr(argv[1], '-');
1922 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
1923 fprintf(stderr, "Invalid configuration filename.\n");
1931 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
1933 xasprintf(&command, "edit \"%s\"", filename);
1935 int result = system(command);
1939 // Silently try notifying a running tincd of changes.
1940 if(connect_tincd(false))
1941 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1946 static int export(const char *name, FILE *out) {
1948 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
1949 FILE *in = fopen(filename, "r");
1951 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1955 fprintf(out, "Name = %s\n", name);
1957 while(fgets(buf, sizeof buf, in)) {
1958 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
1963 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
1972 static int cmd_export(int argc, char *argv[]) {
1974 fprintf(stderr, "Too many arguments!\n");
1978 char *name = get_my_name(true);
1982 int result = export(name, stdout);
1990 static int cmd_export_all(int argc, char *argv[]) {
1992 fprintf(stderr, "Too many arguments!\n");
1996 DIR *dir = opendir(hosts_dir);
1998 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2006 while((ent = readdir(dir))) {
2007 if(!check_id(ent->d_name))
2013 printf("#---------------------------------------------------------------#\n");
2015 result |= export(ent->d_name, stdout);
2024 static int cmd_import(int argc, char *argv[]) {
2026 fprintf(stderr, "Too many arguments!\n");
2035 char *filename = NULL;
2037 bool firstline = true;
2039 while(fgets(buf, sizeof buf, in)) {
2040 if(sscanf(buf, "Name = %s", name) == 1) {
2043 if(!check_id(name)) {
2044 fprintf(stderr, "Invalid Name in input!\n");
2052 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
2054 if(!force && !access(filename, F_OK)) {
2055 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2060 out = fopen(filename, "w");
2062 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2068 } else if(firstline) {
2069 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2074 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2078 if(fputs(buf, out) < 0) {
2079 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2089 fprintf(stderr, "Imported %d host configuration files.\n", count);
2092 fprintf(stderr, "No host configuration files imported.\n");
2097 static int cmd_exchange(int argc, char *argv[]) {
2098 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2101 static int cmd_exchange_all(int argc, char *argv[]) {
2102 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2105 static int switch_network(char *name) {
2117 free(unixsocketname);
2118 unixsocketname = NULL;
2124 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2127 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2128 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2129 xasprintf(&prompt, "%s> ", identname);
2134 static int cmd_network(int argc, char *argv[]) {
2136 fprintf(stderr, "Too many arguments!\n");
2141 return switch_network(argv[1]);
2143 DIR *dir = opendir(confdir);
2145 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2150 while((ent = readdir(dir))) {
2151 if(*ent->d_name == '.')
2154 if(!strcmp(ent->d_name, "tinc.conf")) {
2160 xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2161 if(!access(fname, R_OK))
2162 printf("%s\n", ent->d_name);
2171 static const struct {
2172 const char *command;
2173 int (*function)(int argc, char *argv[]);
2176 {"start", cmd_start},
2178 {"restart", cmd_restart},
2179 {"reload", cmd_reload},
2181 {"purge", cmd_purge},
2182 {"debug", cmd_debug},
2183 {"retry", cmd_retry},
2184 {"connect", cmd_connect},
2185 {"disconnect", cmd_disconnect},
2190 {"config", cmd_config, true},
2191 {"add", cmd_config},
2192 {"del", cmd_config},
2193 {"get", cmd_config},
2194 {"set", cmd_config},
2196 {"generate-keys", cmd_generate_keys},
2197 {"generate-rsa-keys", cmd_generate_rsa_keys},
2198 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2200 {"version", cmd_version},
2203 {"export", cmd_export},
2204 {"export-all", cmd_export_all},
2205 {"import", cmd_import},
2206 {"exchange", cmd_exchange},
2207 {"exchange-all", cmd_exchange_all},
2208 {"invite", cmd_invite},
2210 {"network", cmd_network},
2214 #ifdef HAVE_READLINE
2215 static char *complete_command(const char *text, int state) {
2223 while(commands[i].command) {
2224 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2225 return xstrdup(commands[i].command);
2232 static char *complete_dump(const char *text, int state) {
2233 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2242 if(!strncasecmp(matches[i], text, strlen(text)))
2243 return xstrdup(matches[i]);
2250 static char *complete_config(const char *text, int state) {
2258 while(variables[i].name) {
2259 char *dot = strchr(text, '.');
2261 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2263 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2267 if(!strncasecmp(variables[i].name, text, strlen(text)))
2268 return xstrdup(variables[i].name);
2276 static char *complete_info(const char *text, int state) {
2280 if(!connect_tincd(false))
2282 // Check the list of nodes
2283 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2284 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2287 while(recvline(fd, line, sizeof line)) {
2289 int n = sscanf(line, "%d %d %s", &code, &req, item);
2299 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2303 if(!strncmp(item, text, strlen(text)))
2304 return xstrdup(strip_weight(item));
2310 static char *complete_nothing(const char *text, int state) {
2314 static char **completion (const char *text, int start, int end) {
2315 char **matches = NULL;
2318 matches = rl_completion_matches(text, complete_command);
2319 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2320 matches = rl_completion_matches(text, complete_dump);
2321 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2322 matches = rl_completion_matches(text, complete_config);
2323 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2324 matches = rl_completion_matches(text, complete_config);
2325 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2326 matches = rl_completion_matches(text, complete_config);
2327 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2328 matches = rl_completion_matches(text, complete_config);
2329 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2330 matches = rl_completion_matches(text, complete_info);
2336 static int cmd_shell(int argc, char *argv[]) {
2337 xasprintf(&prompt, "%s> ", identname);
2341 int maxargs = argc + 16;
2342 char **nargv = xmalloc(maxargs * sizeof *nargv);
2344 for(int i = 0; i < argc; i++)
2347 #ifdef HAVE_READLINE
2348 rl_readline_name = "tinc";
2349 rl_completion_entry_function = complete_nothing;
2350 rl_attempted_completion_function = completion;
2351 rl_filename_completion_desired = 0;
2356 #ifdef HAVE_READLINE
2360 rl_basic_word_break_characters = "\t\n ";
2361 line = readline(prompt);
2363 copy = xstrdup(line);
2365 line = fgets(buf, sizeof buf, stdin);
2369 fputs(prompt, stdout);
2371 line = fgets(buf, sizeof buf, stdin);
2377 /* Ignore comments */
2385 char *p = line + strspn(line, " \t\n");
2386 char *next = strtok(p, " \t\n");
2389 if(nargc >= maxargs) {
2390 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2393 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2398 next = strtok(NULL, " \t\n");
2404 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2409 for(int i = 0; commands[i].command; i++) {
2410 if(!strcasecmp(nargv[argc], commands[i].command)) {
2411 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2417 #ifdef HAVE_READLINE
2423 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2436 int main(int argc, char *argv[]) {
2437 program_name = argv[0];
2440 tty = isatty(0) && isatty(1);
2442 if(!parse_options(argc, argv))
2446 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2447 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2460 static struct WSAData wsa_state;
2462 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2463 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
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]);