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 {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
1369 {"UDPDiscoveryInterval", VAR_SERVER},
1370 {"UDPDiscoveryTimeout", VAR_SERVER},
1371 {"UDPRcvBuf", VAR_SERVER},
1372 {"UDPSndBuf", VAR_SERVER},
1373 {"VDEGroup", VAR_SERVER},
1374 {"VDEPort", VAR_SERVER},
1375 /* Host configuration */
1376 {"Address", VAR_HOST | VAR_MULTIPLE},
1377 {"Cipher", VAR_SERVER | VAR_HOST},
1378 {"ClampMSS", VAR_SERVER | VAR_HOST},
1379 {"Compression", VAR_SERVER | VAR_HOST},
1380 {"Digest", VAR_SERVER | VAR_HOST},
1381 {"Ed25519PublicKey", VAR_HOST},
1382 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1383 {"IndirectData", VAR_SERVER | VAR_HOST},
1384 {"MACLength", VAR_SERVER | VAR_HOST},
1385 {"PMTU", VAR_SERVER | VAR_HOST},
1386 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1388 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1389 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1390 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1391 {"TCPOnly", VAR_SERVER | VAR_HOST},
1392 {"Weight", VAR_HOST | VAR_SAFE},
1396 static int cmd_config(int argc, char *argv[]) {
1398 fprintf(stderr, "Invalid number of arguments.\n");
1402 if(strcasecmp(argv[0], "config"))
1406 if(!strcasecmp(argv[1], "get")) {
1408 } else if(!strcasecmp(argv[1], "add")) {
1409 argv++, argc--, action = 1;
1410 } else if(!strcasecmp(argv[1], "del")) {
1411 argv++, argc--, action = -1;
1412 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1413 argv++, argc--, action = 0;
1417 fprintf(stderr, "Invalid number of arguments.\n");
1421 // Concatenate the rest of the command line
1422 strncpy(line, argv[1], sizeof line - 1);
1423 for(int i = 2; i < argc; i++) {
1424 strncat(line, " ", sizeof line - 1 - strlen(line));
1425 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1428 // Liberal parsing into node name, variable name and value.
1434 len = strcspn(line, "\t =");
1436 value += strspn(value, "\t ");
1439 value += strspn(value, "\t ");
1442 variable = strchr(line, '.');
1451 fprintf(stderr, "No variable given.\n");
1455 if(action >= 0 && !*value) {
1456 fprintf(stderr, "No value for variable given.\n");
1460 if(action < -1 && *value)
1463 /* Some simple checks. */
1465 bool warnonremove = false;
1467 for(int i = 0; variables[i].name; i++) {
1468 if(strcasecmp(variables[i].name, variable))
1472 variable = (char *)variables[i].name;
1474 /* Discourage use of obsolete variables. */
1476 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1478 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1480 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1485 /* Don't put server variables in host config files */
1487 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1489 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1491 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1496 /* Should this go into our own host config file? */
1498 if(!node && !(variables[i].type & VAR_SERVER)) {
1499 node = get_my_name(true);
1504 /* Change "add" into "set" for variables that do not allow multiple occurences.
1505 Turn on warnings when it seems variables might be removed unintentionally. */
1507 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1508 warnonremove = true;
1510 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1511 warnonremove = true;
1517 if(node && !check_id(node)) {
1518 fprintf(stderr, "Invalid name for node.\n");
1523 if(force || action < 0) {
1524 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1526 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1531 // Open the right configuration file.
1534 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, node);
1536 filename = tinc_conf;
1538 FILE *f = fopen(filename, "r");
1540 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1544 char *tmpfile = NULL;
1548 xasprintf(&tmpfile, "%s.config.tmp", filename);
1549 tf = fopen(tmpfile, "w");
1551 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1557 // Copy the file, making modifications on the fly, unless we are just getting a value.
1561 bool removed = false;
1564 while(fgets(buf1, sizeof buf1, f)) {
1565 buf1[sizeof buf1 - 1] = 0;
1566 strncpy(buf2, buf1, sizeof buf2);
1568 // Parse line in a simple way
1572 len = strcspn(buf2, "\t =");
1573 bvalue = buf2 + len;
1574 bvalue += strspn(bvalue, "\t ");
1575 if(*bvalue == '=') {
1577 bvalue += strspn(bvalue, "\t ");
1583 if(!strcasecmp(buf2, variable)) {
1587 printf("%s\n", bvalue);
1589 } else if(action == -1) {
1590 if(!*value || !strcasecmp(bvalue, value)) {
1595 } else if(action == 0) {
1596 // Warn if "set" was used for variables that can occur multiple times
1597 if(warnonremove && strcasecmp(bvalue, value))
1598 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1600 // Already set? Delete the rest...
1604 // Otherwise, replace.
1605 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1606 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1615 // Copy original line...
1616 if(fputs(buf1, tf) < 0) {
1617 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1621 // Add newline if it is missing...
1622 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1623 if(fputc('\n', tf) < 0) {
1624 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1631 // Make sure we read everything...
1632 if(ferror(f) || !feof(f)) {
1633 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1638 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1642 // Add new variable if necessary.
1643 if(action > 0 || (action == 0 && !set)) {
1644 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1645 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1654 fprintf(stderr, "No matching configuration variables found.\n");
1659 // Make sure we wrote everything...
1661 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1665 // Could we find what we had to remove?
1666 if(action < 0 && !removed) {
1668 fprintf(stderr, "No configuration variables deleted.\n");
1672 // Replace the configuration file with the new one
1674 if(remove(filename)) {
1675 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1679 if(rename(tmpfile, filename)) {
1680 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1684 // Silently try notifying a running tincd of changes.
1685 if(connect_tincd(false))
1686 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1691 static bool try_bind(int port) {
1692 struct addrinfo *ai = NULL;
1693 struct addrinfo hint = {
1694 .ai_flags = AI_PASSIVE,
1695 .ai_family = AF_UNSPEC,
1696 .ai_socktype = SOCK_STREAM,
1697 .ai_protocol = IPPROTO_TCP,
1701 snprintf(portstr, sizeof portstr, "%d", port);
1703 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1707 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1710 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1720 int check_port(char *name) {
1724 fprintf(stderr, "Warning: could not bind to port 655. ");
1726 for(int i = 0; i < 100; i++) {
1727 int port = 0x1000 + (rand() & 0x7fff);
1728 if(try_bind(port)) {
1730 xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1731 FILE *f = fopen(filename, "a");
1734 fprintf(stderr, "Please change tinc's Port manually.\n");
1738 fprintf(f, "Port = %d\n", port);
1740 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1745 fprintf(stderr, "Please change tinc's Port manually.\n");
1749 static int cmd_init(int argc, char *argv[]) {
1750 if(!access(tinc_conf, F_OK)) {
1751 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1756 fprintf(stderr, "Too many arguments!\n");
1758 } else if(argc < 2) {
1761 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1762 if(!fgets(buf, sizeof buf, stdin)) {
1763 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1766 int len = rstrip(buf);
1768 fprintf(stderr, "No name given!\n");
1773 fprintf(stderr, "No Name given!\n");
1777 name = strdup(argv[1]);
1779 fprintf(stderr, "No Name given!\n");
1784 if(!check_id(name)) {
1785 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1789 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1790 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1794 if(mkdir(confbase, 0777) && errno != EEXIST) {
1795 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1799 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1800 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1804 FILE *f = fopen(tinc_conf, "w");
1806 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1810 fprintf(f, "Name = %s\n", name);
1813 #ifndef DISABLE_LEGACY
1814 if(!rsa_keygen(2048, false))
1818 if(!ed25519_keygen(false))
1825 xasprintf(&filename, "%s" SLASH "tinc-up", confbase);
1826 if(access(filename, F_OK)) {
1827 FILE *f = fopenmask(filename, "w", 0777);
1829 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1832 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");
1841 static int cmd_generate_keys(int argc, char *argv[]) {
1842 #ifdef DISABLE_LEGACY
1847 fprintf(stderr, "Too many arguments!\n");
1852 name = get_my_name(false);
1854 #ifndef DISABLE_LEGACY
1855 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
1859 if(!ed25519_keygen(true))
1865 #ifndef DISABLE_LEGACY
1866 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1868 fprintf(stderr, "Too many arguments!\n");
1873 name = get_my_name(false);
1875 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1879 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1881 fprintf(stderr, "Too many arguments!\n");
1886 name = get_my_name(false);
1888 return !ed25519_keygen(true);
1891 static int cmd_help(int argc, char *argv[]) {
1896 static int cmd_version(int argc, char *argv[]) {
1898 fprintf(stderr, "Too many arguments!\n");
1906 static int cmd_info(int argc, char *argv[]) {
1908 fprintf(stderr, "Invalid number of arguments.\n");
1912 if(!connect_tincd(true))
1915 return info(fd, argv[1]);
1918 static const char *conffiles[] = {
1929 static int cmd_edit(int argc, char *argv[]) {
1931 fprintf(stderr, "Invalid number of arguments.\n");
1935 char *filename = NULL;
1937 if(strncmp(argv[1], "hosts" SLASH, 6)) {
1938 for(int i = 0; conffiles[i]; i++) {
1939 if(!strcmp(argv[1], conffiles[i])) {
1940 xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]);
1949 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]);
1950 char *dash = strchr(argv[1], '-');
1953 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
1954 fprintf(stderr, "Invalid configuration filename.\n");
1962 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
1964 xasprintf(&command, "edit \"%s\"", filename);
1966 int result = system(command);
1970 // Silently try notifying a running tincd of changes.
1971 if(connect_tincd(false))
1972 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1977 static int export(const char *name, FILE *out) {
1979 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
1980 FILE *in = fopen(filename, "r");
1982 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1986 fprintf(out, "Name = %s\n", name);
1988 while(fgets(buf, sizeof buf, in)) {
1989 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
1994 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
2003 static int cmd_export(int argc, char *argv[]) {
2005 fprintf(stderr, "Too many arguments!\n");
2009 char *name = get_my_name(true);
2013 int result = export(name, stdout);
2021 static int cmd_export_all(int argc, char *argv[]) {
2023 fprintf(stderr, "Too many arguments!\n");
2027 DIR *dir = opendir(hosts_dir);
2029 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2037 while((ent = readdir(dir))) {
2038 if(!check_id(ent->d_name))
2044 printf("#---------------------------------------------------------------#\n");
2046 result |= export(ent->d_name, stdout);
2055 static int cmd_import(int argc, char *argv[]) {
2057 fprintf(stderr, "Too many arguments!\n");
2066 char *filename = NULL;
2068 bool firstline = true;
2070 while(fgets(buf, sizeof buf, in)) {
2071 if(sscanf(buf, "Name = %s", name) == 1) {
2074 if(!check_id(name)) {
2075 fprintf(stderr, "Invalid Name in input!\n");
2083 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
2085 if(!force && !access(filename, F_OK)) {
2086 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2091 out = fopen(filename, "w");
2093 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2099 } else if(firstline) {
2100 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2105 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2109 if(fputs(buf, out) < 0) {
2110 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2120 fprintf(stderr, "Imported %d host configuration files.\n", count);
2123 fprintf(stderr, "No host configuration files imported.\n");
2128 static int cmd_exchange(int argc, char *argv[]) {
2129 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2132 static int cmd_exchange_all(int argc, char *argv[]) {
2133 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2136 static int switch_network(char *name) {
2148 free(unixsocketname);
2149 unixsocketname = NULL;
2155 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2158 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2159 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2160 xasprintf(&prompt, "%s> ", identname);
2165 static int cmd_network(int argc, char *argv[]) {
2167 fprintf(stderr, "Too many arguments!\n");
2172 return switch_network(argv[1]);
2174 DIR *dir = opendir(confdir);
2176 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2181 while((ent = readdir(dir))) {
2182 if(*ent->d_name == '.')
2185 if(!strcmp(ent->d_name, "tinc.conf")) {
2191 xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2192 if(!access(fname, R_OK))
2193 printf("%s\n", ent->d_name);
2202 static const struct {
2203 const char *command;
2204 int (*function)(int argc, char *argv[]);
2207 {"start", cmd_start},
2209 {"restart", cmd_restart},
2210 {"reload", cmd_reload},
2212 {"purge", cmd_purge},
2213 {"debug", cmd_debug},
2214 {"retry", cmd_retry},
2215 {"connect", cmd_connect},
2216 {"disconnect", cmd_disconnect},
2221 {"config", cmd_config, true},
2222 {"add", cmd_config},
2223 {"del", cmd_config},
2224 {"get", cmd_config},
2225 {"set", cmd_config},
2227 {"generate-keys", cmd_generate_keys},
2228 #ifndef DISABLE_LEGACY
2229 {"generate-rsa-keys", cmd_generate_rsa_keys},
2231 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2233 {"version", cmd_version},
2236 {"export", cmd_export},
2237 {"export-all", cmd_export_all},
2238 {"import", cmd_import},
2239 {"exchange", cmd_exchange},
2240 {"exchange-all", cmd_exchange_all},
2241 {"invite", cmd_invite},
2243 {"network", cmd_network},
2247 #ifdef HAVE_READLINE
2248 static char *complete_command(const char *text, int state) {
2256 while(commands[i].command) {
2257 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2258 return xstrdup(commands[i].command);
2265 static char *complete_dump(const char *text, int state) {
2266 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2275 if(!strncasecmp(matches[i], text, strlen(text)))
2276 return xstrdup(matches[i]);
2283 static char *complete_config(const char *text, int state) {
2291 while(variables[i].name) {
2292 char *dot = strchr(text, '.');
2294 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2296 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2300 if(!strncasecmp(variables[i].name, text, strlen(text)))
2301 return xstrdup(variables[i].name);
2309 static char *complete_info(const char *text, int state) {
2313 if(!connect_tincd(false))
2315 // Check the list of nodes
2316 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2317 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2320 while(recvline(fd, line, sizeof line)) {
2322 int n = sscanf(line, "%d %d %s", &code, &req, item);
2332 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2336 if(!strncmp(item, text, strlen(text)))
2337 return xstrdup(strip_weight(item));
2343 static char *complete_nothing(const char *text, int state) {
2347 static char **completion (const char *text, int start, int end) {
2348 char **matches = NULL;
2351 matches = rl_completion_matches(text, complete_command);
2352 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2353 matches = rl_completion_matches(text, complete_dump);
2354 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2355 matches = rl_completion_matches(text, complete_config);
2356 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2357 matches = rl_completion_matches(text, complete_config);
2358 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2359 matches = rl_completion_matches(text, complete_config);
2360 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2361 matches = rl_completion_matches(text, complete_config);
2362 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2363 matches = rl_completion_matches(text, complete_info);
2369 static int cmd_shell(int argc, char *argv[]) {
2370 xasprintf(&prompt, "%s> ", identname);
2374 int maxargs = argc + 16;
2375 char **nargv = xmalloc(maxargs * sizeof *nargv);
2377 for(int i = 0; i < argc; i++)
2380 #ifdef HAVE_READLINE
2381 rl_readline_name = "tinc";
2382 rl_completion_entry_function = complete_nothing;
2383 rl_attempted_completion_function = completion;
2384 rl_filename_completion_desired = 0;
2389 #ifdef HAVE_READLINE
2393 rl_basic_word_break_characters = "\t\n ";
2394 line = readline(prompt);
2396 copy = xstrdup(line);
2398 line = fgets(buf, sizeof buf, stdin);
2402 fputs(prompt, stdout);
2404 line = fgets(buf, sizeof buf, stdin);
2410 /* Ignore comments */
2418 char *p = line + strspn(line, " \t\n");
2419 char *next = strtok(p, " \t\n");
2422 if(nargc >= maxargs) {
2423 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2426 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2431 next = strtok(NULL, " \t\n");
2437 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2442 for(int i = 0; commands[i].command; i++) {
2443 if(!strcasecmp(nargv[argc], commands[i].command)) {
2444 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2450 #ifdef HAVE_READLINE
2456 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2469 int main(int argc, char *argv[]) {
2470 program_name = argv[0];
2473 tty = isatty(0) && isatty(1);
2475 if(!parse_options(argc, argv))
2479 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2480 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2493 static struct WSAData wsa_state;
2495 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2496 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2505 return cmd_shell(argc, argv);
2507 for(int i = 0; commands[i].command; i++) {
2508 if(!strcasecmp(argv[optind], commands[i].command))
2509 return commands[i].function(argc - optind, argv + optind);
2512 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);