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-2014 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 #ifdef DISABLE_LEGACY
123 " generate-keys Generate a new Ed25519 public/private keypair.\n"
125 " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
126 " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
128 " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
129 " dump Dump a list of one of the following things:\n"
130 " [reachable] nodes - all known nodes in the VPN\n"
131 " edges - all known connections in the VPN\n"
132 " subnets - all known subnets in the VPN\n"
133 " connections - all meta connections with ourself\n"
134 " [di]graph - graph of the VPN in dotty format\n"
135 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
136 " purge Purge unreachable nodes\n"
137 " debug N Set debug level\n"
138 " retry Retry all outgoing connections\n"
139 " disconnect NODE Close meta connection with NODE\n"
141 " top Show real-time statistics\n"
143 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
144 " log [level] Dump log output [up to the specified level]\n"
145 " export Export host configuration of local node to standard output\n"
146 " export-all Export all host configuration files to standard output\n"
147 " import [--force] Import host configuration file(s) from standard input\n"
148 " exchange [--force] Same as export followed by import\n"
149 " exchange-all [--force] Same as export-all followed by import\n"
150 " invite NODE [...] Generate an invitation for NODE\n"
151 " join INVITATION Join a VPN using an INVITIATION\n"
152 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
154 printf("Report bugs to tinc@tinc-vpn.org.\n");
158 static bool parse_options(int argc, char **argv) {
160 int option_index = 0;
162 while((r = getopt_long(argc, argv, "+c:n:", long_options, &option_index)) != EOF) {
164 case 0: /* long option */
171 case 'c': /* config file */
172 confbase = xstrdup(optarg);
173 confbasegiven = true;
176 case 'n': /* net name given */
177 netname = xstrdup(optarg);
180 case 1: /* show help */
184 case 2: /* show version */
188 case 3: /* open control socket here */
189 pidfilename = xstrdup(optarg);
196 case '?': /* wrong options */
205 if(!netname && (netname = getenv("NETNAME")))
206 netname = xstrdup(netname);
208 /* netname "." is special: a "top-level name" */
210 if(netname && (!*netname || !strcmp(netname, "."))) {
215 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
216 fprintf(stderr, "Invalid character in netname!\n");
223 /* Open a file with the desired permissions, minus the umask.
224 Also, if we want to create an executable file, we call fchmod()
225 to set the executable bits. */
227 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
228 mode_t mask = umask(0);
231 FILE *f = fopen(filename, mode);
233 if((perms & 0444) && f)
234 fchmod(fileno(f), perms);
240 static void disable_old_keys(const char *filename, const char *what) {
241 char tmpfile[PATH_MAX] = "";
243 bool disabled = false;
248 r = fopen(filename, "r");
252 snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
254 struct stat st = {.st_mode = 0600};
255 fstat(fileno(r), &st);
256 w = fopenmask(tmpfile, "w", st.st_mode);
258 while(fgets(buf, sizeof buf, r)) {
259 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
260 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
266 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
272 if(block || ed25519pubkey)
274 if(fputs(buf, w) < 0) {
280 if(block && !strncmp(buf, "-----END ", 9))
287 if(ferror(r) || fclose(r) < 0)
292 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
299 // We cannot atomically replace files on Windows.
300 char bakfile[PATH_MAX] = "";
301 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
302 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
303 rename(bakfile, filename);
305 if(rename(tmpfile, filename)) {
307 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
312 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
319 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
325 /* Check stdin and stdout */
327 /* Ask for a file and/or directory name. */
328 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
330 if(fgets(buf, sizeof buf, stdin) == NULL) {
331 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
335 size_t len = strlen(buf);
344 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
346 if(filename[0] != '/') {
348 /* The directory is a relative path or a filename. */
349 directory = get_current_dir_name();
350 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
354 disable_old_keys(filename, what);
356 /* Open it first to keep the inode busy */
358 r = fopenmask(filename, mode, perms);
361 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
369 Generate a public/private Ed25519 keypair, and ask for a file to store
372 static bool ed25519_keygen(bool ask) {
375 char *pubname, *privname;
377 fprintf(stderr, "Generating Ed25519 keypair:\n");
379 if(!(key = ecdsa_generate())) {
380 fprintf(stderr, "Error during key generation!\n");
383 fprintf(stderr, "Done.\n");
385 xasprintf(&privname, "%s" SLASH "ed25519_key.priv", confbase);
386 f = ask_and_open(privname, "private Ed25519 key", "a", ask, 0600);
392 if(!ecdsa_write_pem_private_key(key, f)) {
393 fprintf(stderr, "Error writing private key!\n");
402 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
404 xasprintf(&pubname, "%s" SLASH "ed25519_key.pub", confbase);
406 f = ask_and_open(pubname, "public Ed25519 key", "a", ask, 0666);
412 char *pubkey = ecdsa_get_base64_public_key(key);
413 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
422 #ifndef DISABLE_LEGACY
424 Generate a public/private RSA keypair, and ask for a file to store
427 static bool rsa_keygen(int bits, bool ask) {
430 char *pubname, *privname;
432 // Make sure the key size is a multiple of 8 bits.
435 // Force them to be between 1024 and 8192 bits long.
441 fprintf(stderr, "Generating %d bits keys:\n", bits);
443 if(!(key = rsa_generate(bits, 0x10001))) {
444 fprintf(stderr, "Error during key generation!\n");
447 fprintf(stderr, "Done.\n");
449 xasprintf(&privname, "%s" SLASH "rsa_key.priv", confbase);
450 f = ask_and_open(privname, "private RSA key", "a", ask, 0600);
456 if(!rsa_write_pem_private_key(key, f)) {
457 fprintf(stderr, "Error writing private key!\n");
466 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
468 xasprintf(&pubname, "%s" SLASH "rsa_key.pub", confbase);
470 f = ask_and_open(pubname, "public RSA key", "a", ask, 0666);
476 if(!rsa_write_pem_public_key(key, f)) {
477 fprintf(stderr, "Error writing public key!\n");
493 bool recvline(int fd, char *line, size_t len) {
494 char *newline = NULL;
499 while(!(newline = memchr(buffer, '\n', blen))) {
500 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
501 if(result == -1 && sockerrno == EINTR)
508 if(newline - buffer >= len)
511 len = newline - buffer;
513 memcpy(line, buffer, len);
515 memmove(buffer, newline + 1, blen - len - 1);
521 bool recvdata(int fd, char *data, size_t len) {
526 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
527 if(result == -1 && sockerrno == EINTR)
534 memcpy(data, buffer, len);
535 memmove(buffer, buffer + len, blen - len);
541 bool sendline(int fd, char *format, ...) {
542 static char buffer[4096];
547 va_start(ap, format);
548 blen = vsnprintf(buffer, sizeof buffer, format, ap);
551 if(blen < 1 || blen >= sizeof buffer)
558 int result = send(fd, p, blen, MSG_NOSIGNAL);
559 if(result == -1 && sockerrno == EINTR)
570 static void pcap(int fd, FILE *out, int snaplen) {
571 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
579 uint32_t tz_accuracy;
586 snaplen ?: sizeof data,
599 fwrite(&header, sizeof header, 1, out);
603 while(recvline(fd, line, sizeof line)) {
605 int n = sscanf(line, "%d %d %d", &code, &req, &len);
606 gettimeofday(&tv, NULL);
607 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
609 if(!recvdata(fd, data, len))
611 packet.tv_sec = tv.tv_sec;
612 packet.tv_usec = tv.tv_usec;
614 packet.origlen = len;
615 fwrite(&packet, sizeof packet, 1, out);
616 fwrite(data, len, 1, out);
621 static void logcontrol(int fd, FILE *out, int level) {
622 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
626 while(recvline(fd, line, sizeof line)) {
628 int n = sscanf(line, "%d %d %d", &code, &req, &len);
629 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
631 if(!recvdata(fd, data, len))
633 fwrite(data, len, 1, out);
640 static bool remove_service(void) {
641 SC_HANDLE manager = NULL;
642 SC_HANDLE service = NULL;
643 SERVICE_STATUS status = {0};
645 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
647 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
651 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
654 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
658 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
659 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
661 fprintf(stderr, "%s service stopped\n", identname);
663 if(!DeleteService(service)) {
664 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
668 fprintf(stderr, "%s service removed\n", identname);
674 bool connect_tincd(bool verbose) {
679 struct timeval tv = {0, 0};
680 if(select(fd + 1, &r, NULL, NULL, &tv)) {
681 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
689 FILE *f = fopen(pidfilename, "r");
692 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
699 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
701 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
709 struct sockaddr_un sa;
710 sa.sun_family = AF_UNIX;
711 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
713 fd = socket(AF_UNIX, SOCK_STREAM, 0);
716 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
720 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
722 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
728 struct addrinfo hints = {
729 .ai_family = AF_UNSPEC,
730 .ai_socktype = SOCK_STREAM,
731 .ai_protocol = IPPROTO_TCP,
735 struct addrinfo *res = NULL;
737 if(getaddrinfo(host, port, &hints, &res) || !res) {
739 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
743 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
746 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
751 unsigned long arg = 0;
753 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
755 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
759 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
761 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
771 static const int one = 1;
772 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
778 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
780 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
786 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
788 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
790 fprintf(stderr, "Could not fully establish control socket connection\n");
800 static int cmd_start(int argc, char *argv[]) {
801 if(connect_tincd(false)) {
803 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
805 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
810 char *slash = strrchr(program_name, '/');
813 if ((c = strrchr(program_name, '\\')) > slash)
818 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
823 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
828 Windows has no real concept of an "argv array". A command line is just one string.
829 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
830 it uses quotes to handle spaces in arguments.
831 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
832 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
833 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
835 xasprintf(&arg0, "\"%s\"", arg0);
837 nargv[nargc++] = arg0;
838 for(int i = 1; i < optind; i++)
839 nargv[nargc++] = orig_argv[i];
840 for(int i = 1; i < argc; i++)
841 nargv[nargc++] = argv[i];
844 int status = spawnvp(_P_WAIT, c, nargv);
846 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
853 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
859 exit(execvp(c, nargv));
863 int status = -1, result;
865 signal(SIGINT, SIG_IGN);
867 result = waitpid(pid, &status, 0);
869 signal(SIGINT, SIG_DFL);
872 if(result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
873 fprintf(stderr, "Error starting %s\n", c);
881 static int cmd_stop(int argc, char *argv[]) {
883 fprintf(stderr, "Too many arguments!\n");
888 if(!connect_tincd(true)) {
890 if(kill(pid, SIGTERM)) {
891 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
895 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
896 waitpid(pid, NULL, 0);
903 sendline(fd, "%d %d", CONTROL, REQ_STOP);
905 while(recvline(fd, line, sizeof line)) {
906 // Wait for tincd to close the connection...
909 if(!remove_service())
919 static int cmd_restart(int argc, char *argv[]) {
921 return cmd_start(argc, argv);
924 static int cmd_reload(int argc, char *argv[]) {
926 fprintf(stderr, "Too many arguments!\n");
930 if(!connect_tincd(true))
933 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
934 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
935 fprintf(stderr, "Could not reload configuration.\n");
943 static int cmd_dump(int argc, char *argv[]) {
944 bool only_reachable = false;
946 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
947 if(strcasecmp(argv[2], "nodes")) {
948 fprintf(stderr, "`reachable' only supported for nodes.\n");
952 only_reachable = true;
958 fprintf(stderr, "Invalid number of arguments.\n");
963 if(!connect_tincd(true))
968 if(!strcasecmp(argv[1], "nodes"))
969 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
970 else if(!strcasecmp(argv[1], "edges"))
971 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
972 else if(!strcasecmp(argv[1], "subnets"))
973 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
974 else if(!strcasecmp(argv[1], "connections"))
975 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
976 else if(!strcasecmp(argv[1], "graph")) {
977 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
978 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
980 } else if(!strcasecmp(argv[1], "digraph")) {
981 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
982 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
985 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
992 else if(do_graph == 2)
993 printf("digraph {\n");
995 while(recvline(fd, line, sizeof line)) {
996 char node1[4096], node2[4096];
997 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
999 if(do_graph && req == REQ_DUMP_NODES)
1017 char local_host[4096];
1018 char local_port[4096];
1021 int cipher, digest, maclength, compression, distance, socket, weight;
1022 short int pmtu, minmtu, maxmtu;
1023 unsigned int options, status_int;
1024 node_status_t status;
1025 long int last_state_change;
1028 case REQ_DUMP_NODES: {
1029 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);
1031 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1035 memcpy(&status, &status_int, sizeof status);
1038 const char *color = "black";
1039 if(!strcmp(host, "MYSELF"))
1041 else if(!status.reachable)
1043 else if(strcmp(via, node))
1045 else if(!status.validkey)
1049 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1051 if(only_reachable && !status.reachable)
1053 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",
1054 node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1058 case REQ_DUMP_EDGES: {
1059 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);
1061 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1066 float w = 1 + 65536.0 / weight;
1067 if(do_graph == 1 && strcmp(node1, node2) > 0)
1068 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1069 else if(do_graph == 2)
1070 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1072 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);
1076 case REQ_DUMP_SUBNETS: {
1077 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1079 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1082 printf("%s owner %s\n", strip_weight(subnet), node);
1085 case REQ_DUMP_CONNECTIONS: {
1086 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1088 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1091 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1095 fprintf(stderr, "Unable to parse dump from tincd.\n");
1100 fprintf(stderr, "Error receiving dump.\n");
1104 static int cmd_purge(int argc, char *argv[]) {
1106 fprintf(stderr, "Too many arguments!\n");
1110 if(!connect_tincd(true))
1113 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1114 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1115 fprintf(stderr, "Could not purge old information.\n");
1122 static int cmd_debug(int argc, char *argv[]) {
1124 fprintf(stderr, "Invalid number of arguments.\n");
1128 if(!connect_tincd(true))
1131 int debuglevel = atoi(argv[1]);
1134 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1135 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1136 fprintf(stderr, "Could not set debug level.\n");
1140 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1144 static int cmd_retry(int argc, char *argv[]) {
1146 fprintf(stderr, "Too many arguments!\n");
1150 if(!connect_tincd(true))
1153 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1154 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1155 fprintf(stderr, "Could not retry outgoing connections.\n");
1162 static int cmd_connect(int argc, char *argv[]) {
1164 fprintf(stderr, "Invalid number of arguments.\n");
1168 if(!check_id(argv[1])) {
1169 fprintf(stderr, "Invalid name for node.\n");
1173 if(!connect_tincd(true))
1176 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1177 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1178 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1185 static int cmd_disconnect(int argc, char *argv[]) {
1187 fprintf(stderr, "Invalid number of arguments.\n");
1191 if(!check_id(argv[1])) {
1192 fprintf(stderr, "Invalid name for node.\n");
1196 if(!connect_tincd(true))
1199 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1200 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1201 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1208 static int cmd_top(int argc, char *argv[]) {
1210 fprintf(stderr, "Too many arguments!\n");
1215 if(!connect_tincd(true))
1221 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1226 static int cmd_pcap(int argc, char *argv[]) {
1228 fprintf(stderr, "Too many arguments!\n");
1232 if(!connect_tincd(true))
1235 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1240 static void sigint_handler(int sig) {
1241 fprintf(stderr, "\n");
1242 shutdown(fd, SHUT_RDWR);
1246 static int cmd_log(int argc, char *argv[]) {
1248 fprintf(stderr, "Too many arguments!\n");
1252 if(!connect_tincd(true))
1256 signal(SIGINT, sigint_handler);
1259 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1262 signal(SIGINT, SIG_DFL);
1270 static int cmd_pid(int argc, char *argv[]) {
1272 fprintf(stderr, "Too many arguments!\n");
1276 if(!connect_tincd(true) && !pid)
1279 printf("%d\n", pid);
1283 int rstrip(char *value) {
1284 int len = strlen(value);
1285 while(len && strchr("\t\r\n ", value[len - 1]))
1290 char *get_my_name(bool verbose) {
1291 FILE *f = fopen(tinc_conf, "r");
1294 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1300 while(fgets(buf, sizeof buf, f)) {
1301 int len = strcspn(buf, "\t =");
1303 value += strspn(value, "\t ");
1306 value += strspn(value, "\t ");
1311 if(strcasecmp(buf, "Name"))
1315 return replace_name(value);
1321 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1325 const var_t variables[] = {
1326 /* Server configuration */
1327 {"AddressFamily", VAR_SERVER},
1328 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1329 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1330 {"BindToInterface", VAR_SERVER},
1331 {"Broadcast", VAR_SERVER | VAR_SAFE},
1332 {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1333 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1334 {"DecrementTTL", VAR_SERVER},
1335 {"Device", VAR_SERVER},
1336 {"DeviceStandby", VAR_SERVER},
1337 {"DeviceType", VAR_SERVER},
1338 {"DirectOnly", VAR_SERVER},
1339 {"Ed25519PrivateKeyFile", VAR_SERVER},
1340 {"ExperimentalProtocol", VAR_SERVER},
1341 {"Forwarding", VAR_SERVER},
1342 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1343 {"Hostnames", VAR_SERVER},
1344 {"IffOneQueue", VAR_SERVER},
1345 {"Interface", VAR_SERVER},
1346 {"KeyExpire", VAR_SERVER},
1347 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1348 {"LocalDiscovery", VAR_SERVER},
1349 {"MACExpire", VAR_SERVER},
1350 {"MaxConnectionBurst", VAR_SERVER},
1351 {"MaxOutputBufferSize", VAR_SERVER},
1352 {"MaxTimeout", VAR_SERVER},
1353 {"Mode", VAR_SERVER | VAR_SAFE},
1354 {"Name", VAR_SERVER},
1355 {"PingInterval", VAR_SERVER},
1356 {"PingTimeout", VAR_SERVER},
1357 {"PriorityInheritance", VAR_SERVER},
1358 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1359 {"PrivateKeyFile", VAR_SERVER},
1360 {"ProcessPriority", VAR_SERVER},
1361 {"Proxy", VAR_SERVER},
1362 {"ReplayWindow", VAR_SERVER},
1363 {"ScriptsExtension", VAR_SERVER},
1364 {"ScriptsInterpreter", VAR_SERVER},
1365 {"StrictSubnets", VAR_SERVER},
1366 {"TunnelServer", VAR_SERVER},
1367 {"UDPDiscovery", VAR_SERVER},
1368 {"UDPDiscoveryInterval", VAR_SERVER},
1369 {"UDPDiscoveryTimeout", VAR_SERVER},
1370 {"UDPRcvBuf", VAR_SERVER},
1371 {"UDPSndBuf", VAR_SERVER},
1372 {"VDEGroup", VAR_SERVER},
1373 {"VDEPort", VAR_SERVER},
1374 /* Host configuration */
1375 {"Address", VAR_HOST | VAR_MULTIPLE},
1376 {"Cipher", VAR_SERVER | VAR_HOST},
1377 {"ClampMSS", VAR_SERVER | VAR_HOST},
1378 {"Compression", VAR_SERVER | VAR_HOST},
1379 {"Digest", VAR_SERVER | VAR_HOST},
1380 {"Ed25519PublicKey", VAR_HOST},
1381 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1382 {"IndirectData", VAR_SERVER | VAR_HOST},
1383 {"MACLength", VAR_SERVER | VAR_HOST},
1384 {"PMTU", VAR_SERVER | VAR_HOST},
1385 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1387 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1388 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1389 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1390 {"TCPOnly", VAR_SERVER | VAR_HOST},
1391 {"Weight", VAR_HOST | VAR_SAFE},
1395 static int cmd_config(int argc, char *argv[]) {
1397 fprintf(stderr, "Invalid number of arguments.\n");
1401 if(strcasecmp(argv[0], "config"))
1405 if(!strcasecmp(argv[1], "get")) {
1407 } else if(!strcasecmp(argv[1], "add")) {
1408 argv++, argc--, action = 1;
1409 } else if(!strcasecmp(argv[1], "del")) {
1410 argv++, argc--, action = -1;
1411 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1412 argv++, argc--, action = 0;
1416 fprintf(stderr, "Invalid number of arguments.\n");
1420 // Concatenate the rest of the command line
1421 strncpy(line, argv[1], sizeof line - 1);
1422 for(int i = 2; i < argc; i++) {
1423 strncat(line, " ", sizeof line - 1 - strlen(line));
1424 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1427 // Liberal parsing into node name, variable name and value.
1433 len = strcspn(line, "\t =");
1435 value += strspn(value, "\t ");
1438 value += strspn(value, "\t ");
1441 variable = strchr(line, '.');
1450 fprintf(stderr, "No variable given.\n");
1454 if(action >= 0 && !*value) {
1455 fprintf(stderr, "No value for variable given.\n");
1459 if(action < -1 && *value)
1462 /* Some simple checks. */
1464 bool warnonremove = false;
1466 for(int i = 0; variables[i].name; i++) {
1467 if(strcasecmp(variables[i].name, variable))
1471 variable = (char *)variables[i].name;
1473 /* Discourage use of obsolete variables. */
1475 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1477 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1479 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1484 /* Don't put server variables in host config files */
1486 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1488 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1490 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1495 /* Should this go into our own host config file? */
1497 if(!node && !(variables[i].type & VAR_SERVER)) {
1498 node = get_my_name(true);
1503 /* Change "add" into "set" for variables that do not allow multiple occurences.
1504 Turn on warnings when it seems variables might be removed unintentionally. */
1506 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1507 warnonremove = true;
1509 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1510 warnonremove = true;
1516 if(node && !check_id(node)) {
1517 fprintf(stderr, "Invalid name for node.\n");
1522 if(force || action < 0) {
1523 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1525 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1530 // Open the right configuration file.
1533 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, node);
1535 filename = tinc_conf;
1537 FILE *f = fopen(filename, "r");
1539 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1543 char *tmpfile = NULL;
1547 xasprintf(&tmpfile, "%s.config.tmp", filename);
1548 tf = fopen(tmpfile, "w");
1550 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1556 // Copy the file, making modifications on the fly, unless we are just getting a value.
1560 bool removed = false;
1563 while(fgets(buf1, sizeof buf1, f)) {
1564 buf1[sizeof buf1 - 1] = 0;
1565 strncpy(buf2, buf1, sizeof buf2);
1567 // Parse line in a simple way
1571 len = strcspn(buf2, "\t =");
1572 bvalue = buf2 + len;
1573 bvalue += strspn(bvalue, "\t ");
1574 if(*bvalue == '=') {
1576 bvalue += strspn(bvalue, "\t ");
1582 if(!strcasecmp(buf2, variable)) {
1586 printf("%s\n", bvalue);
1588 } else if(action == -1) {
1589 if(!*value || !strcasecmp(bvalue, value)) {
1594 } else if(action == 0) {
1595 // Warn if "set" was used for variables that can occur multiple times
1596 if(warnonremove && strcasecmp(bvalue, value))
1597 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1599 // Already set? Delete the rest...
1603 // Otherwise, replace.
1604 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1605 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1614 // Copy original line...
1615 if(fputs(buf1, tf) < 0) {
1616 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1620 // Add newline if it is missing...
1621 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1622 if(fputc('\n', tf) < 0) {
1623 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1630 // Make sure we read everything...
1631 if(ferror(f) || !feof(f)) {
1632 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1637 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1641 // Add new variable if necessary.
1642 if(action > 0 || (action == 0 && !set)) {
1643 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1644 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1653 fprintf(stderr, "No matching configuration variables found.\n");
1658 // Make sure we wrote everything...
1660 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1664 // Could we find what we had to remove?
1665 if(action < 0 && !removed) {
1667 fprintf(stderr, "No configuration variables deleted.\n");
1671 // Replace the configuration file with the new one
1673 if(remove(filename)) {
1674 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1678 if(rename(tmpfile, filename)) {
1679 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1683 // Silently try notifying a running tincd of changes.
1684 if(connect_tincd(false))
1685 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1690 static bool try_bind(int port) {
1691 struct addrinfo *ai = NULL;
1692 struct addrinfo hint = {
1693 .ai_flags = AI_PASSIVE,
1694 .ai_family = AF_UNSPEC,
1695 .ai_socktype = SOCK_STREAM,
1696 .ai_protocol = IPPROTO_TCP,
1700 snprintf(portstr, sizeof portstr, "%d", port);
1702 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1706 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1709 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1719 int check_port(char *name) {
1723 fprintf(stderr, "Warning: could not bind to port 655. ");
1725 for(int i = 0; i < 100; i++) {
1726 int port = 0x1000 + (rand() & 0x7fff);
1727 if(try_bind(port)) {
1729 xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1730 FILE *f = fopen(filename, "a");
1733 fprintf(stderr, "Please change tinc's Port manually.\n");
1737 fprintf(f, "Port = %d\n", port);
1739 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1744 fprintf(stderr, "Please change tinc's Port manually.\n");
1748 static int cmd_init(int argc, char *argv[]) {
1749 if(!access(tinc_conf, F_OK)) {
1750 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1755 fprintf(stderr, "Too many arguments!\n");
1757 } else if(argc < 2) {
1760 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1761 if(!fgets(buf, sizeof buf, stdin)) {
1762 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1765 int len = rstrip(buf);
1767 fprintf(stderr, "No name given!\n");
1772 fprintf(stderr, "No Name given!\n");
1776 name = strdup(argv[1]);
1778 fprintf(stderr, "No Name given!\n");
1783 if(!check_id(name)) {
1784 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1788 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1789 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1793 if(mkdir(confbase, 0777) && errno != EEXIST) {
1794 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1798 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1799 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1803 FILE *f = fopen(tinc_conf, "w");
1805 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1809 fprintf(f, "Name = %s\n", name);
1812 #ifndef DISABLE_LEGACY
1813 if(!rsa_keygen(2048, false))
1817 if(!ed25519_keygen(false))
1824 xasprintf(&filename, "%s" SLASH "tinc-up", confbase);
1825 if(access(filename, F_OK)) {
1826 FILE *f = fopenmask(filename, "w", 0777);
1828 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1831 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");
1840 static int cmd_generate_keys(int argc, char *argv[]) {
1841 #ifdef DISABLE_LEGACY
1846 fprintf(stderr, "Too many arguments!\n");
1851 name = get_my_name(false);
1853 #ifndef DISABLE_LEGACY
1854 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
1858 if(!ed25519_keygen(true))
1864 #ifndef DISABLE_LEGACY
1865 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1867 fprintf(stderr, "Too many arguments!\n");
1872 name = get_my_name(false);
1874 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1878 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1880 fprintf(stderr, "Too many arguments!\n");
1885 name = get_my_name(false);
1887 return !ed25519_keygen(true);
1890 static int cmd_help(int argc, char *argv[]) {
1895 static int cmd_version(int argc, char *argv[]) {
1897 fprintf(stderr, "Too many arguments!\n");
1905 static int cmd_info(int argc, char *argv[]) {
1907 fprintf(stderr, "Invalid number of arguments.\n");
1911 if(!connect_tincd(true))
1914 return info(fd, argv[1]);
1917 static const char *conffiles[] = {
1928 static int cmd_edit(int argc, char *argv[]) {
1930 fprintf(stderr, "Invalid number of arguments.\n");
1934 char *filename = NULL;
1936 if(strncmp(argv[1], "hosts" SLASH, 6)) {
1937 for(int i = 0; conffiles[i]; i++) {
1938 if(!strcmp(argv[1], conffiles[i])) {
1939 xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]);
1948 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]);
1949 char *dash = strchr(argv[1], '-');
1952 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
1953 fprintf(stderr, "Invalid configuration filename.\n");
1961 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
1963 xasprintf(&command, "edit \"%s\"", filename);
1965 int result = system(command);
1969 // Silently try notifying a running tincd of changes.
1970 if(connect_tincd(false))
1971 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1976 static int export(const char *name, FILE *out) {
1978 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
1979 FILE *in = fopen(filename, "r");
1981 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1985 fprintf(out, "Name = %s\n", name);
1987 while(fgets(buf, sizeof buf, in)) {
1988 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
1993 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2002 static int cmd_export(int argc, char *argv[]) {
2004 fprintf(stderr, "Too many arguments!\n");
2008 char *name = get_my_name(true);
2012 int result = export(name, stdout);
2020 static int cmd_export_all(int argc, char *argv[]) {
2022 fprintf(stderr, "Too many arguments!\n");
2026 DIR *dir = opendir(hosts_dir);
2028 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2036 while((ent = readdir(dir))) {
2037 if(!check_id(ent->d_name))
2043 printf("#---------------------------------------------------------------#\n");
2045 result |= export(ent->d_name, stdout);
2054 static int cmd_import(int argc, char *argv[]) {
2056 fprintf(stderr, "Too many arguments!\n");
2065 char *filename = NULL;
2067 bool firstline = true;
2069 while(fgets(buf, sizeof buf, in)) {
2070 if(sscanf(buf, "Name = %s", name) == 1) {
2073 if(!check_id(name)) {
2074 fprintf(stderr, "Invalid Name in input!\n");
2082 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
2084 if(!force && !access(filename, F_OK)) {
2085 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2090 out = fopen(filename, "w");
2092 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2098 } else if(firstline) {
2099 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2104 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2108 if(fputs(buf, out) < 0) {
2109 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2119 fprintf(stderr, "Imported %d host configuration files.\n", count);
2122 fprintf(stderr, "No host configuration files imported.\n");
2127 static int cmd_exchange(int argc, char *argv[]) {
2128 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2131 static int cmd_exchange_all(int argc, char *argv[]) {
2132 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2135 static int switch_network(char *name) {
2147 free(unixsocketname);
2148 unixsocketname = NULL;
2154 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2157 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2158 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2159 xasprintf(&prompt, "%s> ", identname);
2164 static int cmd_network(int argc, char *argv[]) {
2166 fprintf(stderr, "Too many arguments!\n");
2171 return switch_network(argv[1]);
2173 DIR *dir = opendir(confdir);
2175 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2180 while((ent = readdir(dir))) {
2181 if(*ent->d_name == '.')
2184 if(!strcmp(ent->d_name, "tinc.conf")) {
2190 xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2191 if(!access(fname, R_OK))
2192 printf("%s\n", ent->d_name);
2201 static const struct {
2202 const char *command;
2203 int (*function)(int argc, char *argv[]);
2206 {"start", cmd_start},
2208 {"restart", cmd_restart},
2209 {"reload", cmd_reload},
2211 {"purge", cmd_purge},
2212 {"debug", cmd_debug},
2213 {"retry", cmd_retry},
2214 {"connect", cmd_connect},
2215 {"disconnect", cmd_disconnect},
2220 {"config", cmd_config, true},
2221 {"add", cmd_config},
2222 {"del", cmd_config},
2223 {"get", cmd_config},
2224 {"set", cmd_config},
2226 {"generate-keys", cmd_generate_keys},
2227 #ifndef DISABLE_LEGACY
2228 {"generate-rsa-keys", cmd_generate_rsa_keys},
2230 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2232 {"version", cmd_version},
2235 {"export", cmd_export},
2236 {"export-all", cmd_export_all},
2237 {"import", cmd_import},
2238 {"exchange", cmd_exchange},
2239 {"exchange-all", cmd_exchange_all},
2240 {"invite", cmd_invite},
2242 {"network", cmd_network},
2246 #ifdef HAVE_READLINE
2247 static char *complete_command(const char *text, int state) {
2255 while(commands[i].command) {
2256 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2257 return xstrdup(commands[i].command);
2264 static char *complete_dump(const char *text, int state) {
2265 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2274 if(!strncasecmp(matches[i], text, strlen(text)))
2275 return xstrdup(matches[i]);
2282 static char *complete_config(const char *text, int state) {
2290 while(variables[i].name) {
2291 char *dot = strchr(text, '.');
2293 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2295 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2299 if(!strncasecmp(variables[i].name, text, strlen(text)))
2300 return xstrdup(variables[i].name);
2308 static char *complete_info(const char *text, int state) {
2312 if(!connect_tincd(false))
2314 // Check the list of nodes
2315 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2316 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2319 while(recvline(fd, line, sizeof line)) {
2321 int n = sscanf(line, "%d %d %s", &code, &req, item);
2331 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2335 if(!strncmp(item, text, strlen(text)))
2336 return xstrdup(strip_weight(item));
2342 static char *complete_nothing(const char *text, int state) {
2346 static char **completion (const char *text, int start, int end) {
2347 char **matches = NULL;
2350 matches = rl_completion_matches(text, complete_command);
2351 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2352 matches = rl_completion_matches(text, complete_dump);
2353 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2354 matches = rl_completion_matches(text, complete_config);
2355 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2356 matches = rl_completion_matches(text, complete_config);
2357 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2358 matches = rl_completion_matches(text, complete_config);
2359 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2360 matches = rl_completion_matches(text, complete_config);
2361 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2362 matches = rl_completion_matches(text, complete_info);
2368 static int cmd_shell(int argc, char *argv[]) {
2369 xasprintf(&prompt, "%s> ", identname);
2373 int maxargs = argc + 16;
2374 char **nargv = xmalloc(maxargs * sizeof *nargv);
2376 for(int i = 0; i < argc; i++)
2379 #ifdef HAVE_READLINE
2380 rl_readline_name = "tinc";
2381 rl_completion_entry_function = complete_nothing;
2382 rl_attempted_completion_function = completion;
2383 rl_filename_completion_desired = 0;
2388 #ifdef HAVE_READLINE
2392 rl_basic_word_break_characters = "\t\n ";
2393 line = readline(prompt);
2395 copy = xstrdup(line);
2397 line = fgets(buf, sizeof buf, stdin);
2401 fputs(prompt, stdout);
2403 line = fgets(buf, sizeof buf, stdin);
2409 /* Ignore comments */
2417 char *p = line + strspn(line, " \t\n");
2418 char *next = strtok(p, " \t\n");
2421 if(nargc >= maxargs) {
2422 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2425 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2430 next = strtok(NULL, " \t\n");
2436 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2441 for(int i = 0; commands[i].command; i++) {
2442 if(!strcasecmp(nargv[argc], commands[i].command)) {
2443 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2449 #ifdef HAVE_READLINE
2455 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2468 int main(int argc, char *argv[]) {
2469 program_name = argv[0];
2472 tty = isatty(0) && isatty(1);
2474 if(!parse_options(argc, argv))
2478 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2479 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2492 static struct WSAData wsa_state;
2494 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2495 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2504 return cmd_shell(argc, argv);
2506 for(int i = 0; commands[i].command; i++) {
2507 if(!strcasecmp(argv[optind], commands[i].command))
2508 return commands[i].function(argc - optind, argv + optind);
2511 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);