2 tincctl.c -- Controlling a running tincd
3 Copyright (C) 2007-2014 Guus Sliepen <guus@tinc-vpn.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "readline/readline.h"
26 #include "readline/history.h"
31 #include "control_common.h"
35 #include "invitation.h"
44 #define MSG_NOSIGNAL 0
47 static char **orig_argv;
50 /* If nonzero, display usage information and exit. */
51 static bool show_help = false;
53 /* If nonzero, print the version on standard output and exit. */
54 static bool show_version = false;
56 static char *name = NULL;
57 static char controlcookie[1025];
58 char *tinc_conf = NULL;
59 char *hosts_dir = NULL;
62 // Horrible global variables...
69 static bool force = false;
71 bool confbasegiven = false;
72 bool netnamegiven = false;
73 char *scriptinterpreter = NULL;
74 char *scriptextension = "";
77 static struct option const long_options[] = {
78 {"batch", no_argument, NULL, 'b'},
79 {"config", required_argument, NULL, 'c'},
80 {"net", required_argument, NULL, 'n'},
81 {"help", no_argument, NULL, 1},
82 {"version", no_argument, NULL, 2},
83 {"pidfile", required_argument, NULL, 3},
84 {"force", no_argument, NULL, 4},
88 static void version(void) {
89 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
90 VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
91 printf("Copyright (C) 1998-2012 Ivo Timmermans, Guus Sliepen and others.\n"
92 "See the AUTHORS file for a complete list.\n\n"
93 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
94 "and you are welcome to redistribute it under certain conditions;\n"
95 "see the file COPYING for details.\n");
98 static void usage(bool status) {
100 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
102 printf("Usage: %s [options] command\n\n", program_name);
103 printf("Valid options are:\n"
104 " -b, --batch Don't ask for anything (non-interactive mode).\n"
105 " -c, --config=DIR Read configuration options from DIR.\n"
106 " -n, --net=NETNAME Connect to net NETNAME.\n"
107 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
108 " --help Display this help and exit.\n"
109 " --version Output version information and exit.\n"
111 "Valid commands are:\n"
112 " init [name] Create initial configuration files.\n"
113 " get VARIABLE Print current value of VARIABLE\n"
114 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
115 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
116 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
117 " start [tincd options] Start tincd.\n"
118 " stop Stop tincd.\n"
119 " restart [tincd options] Restart tincd.\n"
120 " reload Partially reload configuration of running tincd.\n"
121 " pid Show PID of currently running tincd.\n"
122 " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
123 " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
124 " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
125 " dump Dump a list of one of the following things:\n"
126 " [reachable] nodes - all known nodes in the VPN\n"
127 " edges - all known connections in the VPN\n"
128 " subnets - all known subnets in the VPN\n"
129 " connections - all meta connections with ourself\n"
130 " [di]graph - graph of the VPN in dotty format\n"
131 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
132 " purge Purge unreachable nodes\n"
133 " debug N Set debug level\n"
134 " retry Retry all outgoing connections\n"
135 " disconnect NODE Close meta connection with NODE\n"
137 " top Show real-time statistics\n"
139 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
140 " log [level] Dump log output [up to the specified level]\n"
141 " export Export host configuration of local node to standard output\n"
142 " export-all Export all host configuration files to standard output\n"
143 " import [--force] Import host configuration file(s) from standard input\n"
144 " exchange [--force] Same as export followed by import\n"
145 " exchange-all [--force] Same as export-all followed by import\n"
146 " invite NODE [...] Generate an invitation for NODE\n"
147 " join INVITATION Join a VPN using an INVITIATION\n"
148 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
150 printf("Report bugs to tinc@tinc-vpn.org.\n");
154 static bool parse_options(int argc, char **argv) {
156 int option_index = 0;
158 while((r = getopt_long(argc, argv, "+c:n:", long_options, &option_index)) != EOF) {
160 case 0: /* long option */
167 case 'c': /* config file */
168 confbase = xstrdup(optarg);
169 confbasegiven = true;
172 case 'n': /* net name given */
173 netname = xstrdup(optarg);
176 case 1: /* show help */
180 case 2: /* show version */
184 case 3: /* open control socket here */
185 pidfilename = xstrdup(optarg);
192 case '?': /* wrong options */
201 if(!netname && (netname = getenv("NETNAME")))
202 netname = xstrdup(netname);
204 /* netname "." is special: a "top-level name" */
206 if(netname && (!*netname || !strcmp(netname, "."))) {
211 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
212 fprintf(stderr, "Invalid character in netname!\n");
219 /* Open a file with the desired permissions, minus the umask.
220 Also, if we want to create an executable file, we call fchmod()
221 to set the executable bits. */
223 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
224 mode_t mask = umask(0);
227 FILE *f = fopen(filename, mode);
229 if((perms & 0444) && f)
230 fchmod(fileno(f), perms);
236 static void disable_old_keys(const char *filename, const char *what) {
237 char tmpfile[PATH_MAX] = "";
239 bool disabled = false;
244 r = fopen(filename, "r");
248 snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
250 struct stat st = {.st_mode = 0600};
251 fstat(fileno(r), &st);
252 w = fopenmask(tmpfile, "w", st.st_mode);
254 while(fgets(buf, sizeof buf, r)) {
255 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
256 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
262 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
268 if(block || ed25519pubkey)
270 if(fputs(buf, w) < 0) {
276 if(block && !strncmp(buf, "-----END ", 9))
283 if(ferror(r) || fclose(r) < 0)
288 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
295 // We cannot atomically replace files on Windows.
296 char bakfile[PATH_MAX] = "";
297 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
298 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
299 rename(bakfile, filename);
301 if(rename(tmpfile, filename)) {
303 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
308 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
315 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
321 /* Check stdin and stdout */
323 /* Ask for a file and/or directory name. */
324 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
326 if(fgets(buf, sizeof buf, stdin) == NULL) {
327 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
331 size_t len = strlen(buf);
340 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
342 if(filename[0] != '/') {
344 /* The directory is a relative path or a filename. */
345 directory = get_current_dir_name();
346 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
350 disable_old_keys(filename, what);
352 /* Open it first to keep the inode busy */
354 r = fopenmask(filename, mode, perms);
357 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
365 Generate a public/private Ed25519 keypair, and ask for a file to store
368 static bool ed25519_keygen(bool ask) {
371 char *pubname, *privname;
373 fprintf(stderr, "Generating Ed25519 keypair:\n");
375 if(!(key = ecdsa_generate())) {
376 fprintf(stderr, "Error during key generation!\n");
379 fprintf(stderr, "Done.\n");
381 xasprintf(&privname, "%s" SLASH "ed25519_key.priv", confbase);
382 f = ask_and_open(privname, "private Ed25519 key", "a", ask, 0600);
388 if(!ecdsa_write_pem_private_key(key, f)) {
389 fprintf(stderr, "Error writing private key!\n");
398 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
400 xasprintf(&pubname, "%s" SLASH "ed25519_key.pub", confbase);
402 f = ask_and_open(pubname, "public Ed25519 key", "a", ask, 0666);
408 char *pubkey = ecdsa_get_base64_public_key(key);
409 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
419 Generate a public/private RSA keypair, and ask for a file to store
422 static bool rsa_keygen(int bits, bool ask) {
425 char *pubname, *privname;
427 // Make sure the key size is a multiple of 8 bits.
430 // Force them to be between 1024 and 8192 bits long.
436 fprintf(stderr, "Generating %d bits keys:\n", bits);
438 if(!(key = rsa_generate(bits, 0x10001))) {
439 fprintf(stderr, "Error during key generation!\n");
442 fprintf(stderr, "Done.\n");
444 xasprintf(&privname, "%s" SLASH "rsa_key.priv", confbase);
445 f = ask_and_open(privname, "private RSA key", "a", ask, 0600);
451 if(!rsa_write_pem_private_key(key, f)) {
452 fprintf(stderr, "Error writing private key!\n");
461 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
463 xasprintf(&pubname, "%s" SLASH "rsa_key.pub", confbase);
465 f = ask_and_open(pubname, "public RSA key", "a", ask, 0666);
471 if(!rsa_write_pem_public_key(key, f)) {
472 fprintf(stderr, "Error writing public key!\n");
487 bool recvline(int fd, char *line, size_t len) {
488 char *newline = NULL;
493 while(!(newline = memchr(buffer, '\n', blen))) {
494 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
495 if(result == -1 && sockerrno == EINTR)
502 if(newline - buffer >= len)
505 len = newline - buffer;
507 memcpy(line, buffer, len);
509 memmove(buffer, newline + 1, blen - len - 1);
515 bool recvdata(int fd, char *data, size_t len) {
520 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
521 if(result == -1 && sockerrno == EINTR)
528 memcpy(data, buffer, len);
529 memmove(buffer, buffer + len, blen - len);
535 bool sendline(int fd, char *format, ...) {
536 static char buffer[4096];
541 va_start(ap, format);
542 blen = vsnprintf(buffer, sizeof buffer, format, ap);
545 if(blen < 1 || blen >= sizeof buffer)
552 int result = send(fd, p, blen, MSG_NOSIGNAL);
553 if(result == -1 && sockerrno == EINTR)
564 static void pcap(int fd, FILE *out, int snaplen) {
565 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
573 uint32_t tz_accuracy;
580 snaplen ?: sizeof data,
593 fwrite(&header, sizeof header, 1, out);
597 while(recvline(fd, line, sizeof line)) {
599 int n = sscanf(line, "%d %d %d", &code, &req, &len);
600 gettimeofday(&tv, NULL);
601 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
603 if(!recvdata(fd, data, len))
605 packet.tv_sec = tv.tv_sec;
606 packet.tv_usec = tv.tv_usec;
608 packet.origlen = len;
609 fwrite(&packet, sizeof packet, 1, out);
610 fwrite(data, len, 1, out);
615 static void logcontrol(int fd, FILE *out, int level) {
616 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
620 while(recvline(fd, line, sizeof line)) {
622 int n = sscanf(line, "%d %d %d", &code, &req, &len);
623 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
625 if(!recvdata(fd, data, len))
627 fwrite(data, len, 1, out);
634 static bool remove_service(void) {
635 SC_HANDLE manager = NULL;
636 SC_HANDLE service = NULL;
637 SERVICE_STATUS status = {0};
639 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
641 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
645 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
648 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
652 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
653 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
655 fprintf(stderr, "%s service stopped\n", identname);
657 if(!DeleteService(service)) {
658 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
662 fprintf(stderr, "%s service removed\n", identname);
668 bool connect_tincd(bool verbose) {
673 struct timeval tv = {0, 0};
674 if(select(fd + 1, &r, NULL, NULL, &tv)) {
675 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
683 FILE *f = fopen(pidfilename, "r");
686 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
693 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
695 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
703 struct sockaddr_un sa;
704 sa.sun_family = AF_UNIX;
705 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
707 fd = socket(AF_UNIX, SOCK_STREAM, 0);
710 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
714 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
716 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
722 struct addrinfo hints = {
723 .ai_family = AF_UNSPEC,
724 .ai_socktype = SOCK_STREAM,
725 .ai_protocol = IPPROTO_TCP,
729 struct addrinfo *res = NULL;
731 if(getaddrinfo(host, port, &hints, &res) || !res) {
733 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
737 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
740 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
745 unsigned long arg = 0;
747 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
749 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
753 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
755 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
765 static const int one = 1;
766 setsockopt(c, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
772 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
774 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
780 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
782 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
784 fprintf(stderr, "Could not fully establish control socket connection\n");
794 static int cmd_start(int argc, char *argv[]) {
795 if(connect_tincd(false)) {
797 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
799 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
804 char *slash = strrchr(program_name, '/');
807 if ((c = strrchr(program_name, '\\')) > slash)
812 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
817 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
822 Windows has no real concept of an "argv array". A command line is just one string.
823 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
824 it uses quotes to handle spaces in arguments.
825 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
826 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
827 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
829 xasprintf(&arg0, "\"%s\"", arg0);
831 nargv[nargc++] = arg0;
832 for(int i = 1; i < optind; i++)
833 nargv[nargc++] = orig_argv[i];
834 for(int i = 1; i < argc; i++)
835 nargv[nargc++] = argv[i];
838 int status = spawnvp(_P_WAIT, c, nargv);
840 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
847 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
853 exit(execvp(c, nargv));
857 int status = -1, result;
859 signal(SIGINT, SIG_IGN);
861 result = waitpid(pid, &status, 0);
863 signal(SIGINT, SIG_DFL);
866 if(result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
867 fprintf(stderr, "Error starting %s\n", c);
875 static int cmd_stop(int argc, char *argv[]) {
877 fprintf(stderr, "Too many arguments!\n");
882 if(!connect_tincd(true)) {
884 if(kill(pid, SIGTERM)) {
885 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
889 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
890 waitpid(pid, NULL, 0);
897 sendline(fd, "%d %d", CONTROL, REQ_STOP);
899 while(recvline(fd, line, sizeof line)) {
900 // Wait for tincd to close the connection...
903 if(!remove_service())
913 static int cmd_restart(int argc, char *argv[]) {
915 return cmd_start(argc, argv);
918 static int cmd_reload(int argc, char *argv[]) {
920 fprintf(stderr, "Too many arguments!\n");
924 if(!connect_tincd(true))
927 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
928 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
929 fprintf(stderr, "Could not reload configuration.\n");
937 static int cmd_dump(int argc, char *argv[]) {
938 bool only_reachable = false;
940 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
941 if(strcasecmp(argv[2], "nodes")) {
942 fprintf(stderr, "`reachable' only supported for nodes.\n");
946 only_reachable = true;
952 fprintf(stderr, "Invalid number of arguments.\n");
957 if(!connect_tincd(true))
962 if(!strcasecmp(argv[1], "nodes"))
963 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
964 else if(!strcasecmp(argv[1], "edges"))
965 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
966 else if(!strcasecmp(argv[1], "subnets"))
967 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
968 else if(!strcasecmp(argv[1], "connections"))
969 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
970 else if(!strcasecmp(argv[1], "graph")) {
971 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
972 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
974 } else if(!strcasecmp(argv[1], "digraph")) {
975 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
976 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
979 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
986 else if(do_graph == 2)
987 printf("digraph {\n");
989 while(recvline(fd, line, sizeof line)) {
990 char node1[4096], node2[4096];
991 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
993 if(do_graph && req == REQ_DUMP_NODES)
1010 char local_host[4096];
1011 char local_port[4096];
1014 int cipher, digest, maclength, compression, distance, socket, weight;
1015 short int pmtu, minmtu, maxmtu;
1016 unsigned int options, status_int;
1017 node_status_t status;
1018 long int last_state_change;
1021 case REQ_DUMP_NODES: {
1022 int n = sscanf(line, "%*d %*d %s %s port %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", node, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
1024 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1028 memcpy(&status, &status_int, sizeof status);
1031 const char *color = "black";
1032 if(!strcmp(host, "MYSELF"))
1034 else if(!status.reachable)
1036 else if(strcmp(via, node))
1038 else if(!status.validkey)
1042 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1044 if(only_reachable && !status.reachable)
1046 printf("%s at %s port %s cipher %d digest %d maclength %d compression %d options %x status %04x nexthop %s via %s distance %d pmtu %hd (min %hd max %hd)\n",
1047 node, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1051 case REQ_DUMP_EDGES: {
1052 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);
1054 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1059 float w = 1 + 65536.0 / weight;
1060 if(do_graph == 1 && strcmp(node1, node2) > 0)
1061 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1062 else if(do_graph == 2)
1063 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1065 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);
1069 case REQ_DUMP_SUBNETS: {
1070 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1072 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1075 printf("%s owner %s\n", strip_weight(subnet), node);
1078 case REQ_DUMP_CONNECTIONS: {
1079 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1081 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1084 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1088 fprintf(stderr, "Unable to parse dump from tincd.\n");
1093 fprintf(stderr, "Error receiving dump.\n");
1097 static int cmd_purge(int argc, char *argv[]) {
1099 fprintf(stderr, "Too many arguments!\n");
1103 if(!connect_tincd(true))
1106 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1107 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1108 fprintf(stderr, "Could not purge old information.\n");
1115 static int cmd_debug(int argc, char *argv[]) {
1117 fprintf(stderr, "Invalid number of arguments.\n");
1121 if(!connect_tincd(true))
1124 int debuglevel = atoi(argv[1]);
1127 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1128 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1129 fprintf(stderr, "Could not set debug level.\n");
1133 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1137 static int cmd_retry(int argc, char *argv[]) {
1139 fprintf(stderr, "Too many arguments!\n");
1143 if(!connect_tincd(true))
1146 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1147 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1148 fprintf(stderr, "Could not retry outgoing connections.\n");
1155 static int cmd_connect(int argc, char *argv[]) {
1157 fprintf(stderr, "Invalid number of arguments.\n");
1161 if(!check_id(argv[1])) {
1162 fprintf(stderr, "Invalid name for node.\n");
1166 if(!connect_tincd(true))
1169 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1170 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1171 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1178 static int cmd_disconnect(int argc, char *argv[]) {
1180 fprintf(stderr, "Invalid number of arguments.\n");
1184 if(!check_id(argv[1])) {
1185 fprintf(stderr, "Invalid name for node.\n");
1189 if(!connect_tincd(true))
1192 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1193 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1194 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1201 static int cmd_top(int argc, char *argv[]) {
1203 fprintf(stderr, "Too many arguments!\n");
1208 if(!connect_tincd(true))
1214 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1219 static int cmd_pcap(int argc, char *argv[]) {
1221 fprintf(stderr, "Too many arguments!\n");
1225 if(!connect_tincd(true))
1228 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1233 static void sigint_handler(int sig) {
1234 fprintf(stderr, "\n");
1235 shutdown(fd, SHUT_RDWR);
1239 static int cmd_log(int argc, char *argv[]) {
1241 fprintf(stderr, "Too many arguments!\n");
1245 if(!connect_tincd(true))
1249 signal(SIGINT, sigint_handler);
1252 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1255 signal(SIGINT, SIG_DFL);
1263 static int cmd_pid(int argc, char *argv[]) {
1265 fprintf(stderr, "Too many arguments!\n");
1269 if(!connect_tincd(true) && !pid)
1272 printf("%d\n", pid);
1276 int rstrip(char *value) {
1277 int len = strlen(value);
1278 while(len && strchr("\t\r\n ", value[len - 1]))
1283 char *get_my_name(bool verbose) {
1284 FILE *f = fopen(tinc_conf, "r");
1287 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1293 while(fgets(buf, sizeof buf, f)) {
1294 int len = strcspn(buf, "\t =");
1296 value += strspn(value, "\t ");
1299 value += strspn(value, "\t ");
1304 if(strcasecmp(buf, "Name"))
1308 return strdup(value);
1314 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1318 const var_t variables[] = {
1319 /* Server configuration */
1320 {"AddressFamily", VAR_SERVER},
1321 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1322 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1323 {"BindToInterface", VAR_SERVER},
1324 {"Broadcast", VAR_SERVER | VAR_SAFE},
1325 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1326 {"DecrementTTL", VAR_SERVER},
1327 {"Device", VAR_SERVER},
1328 {"DeviceType", VAR_SERVER},
1329 {"DirectOnly", VAR_SERVER},
1330 {"Ed25519PrivateKeyFile", VAR_SERVER},
1331 {"ExperimentalProtocol", VAR_SERVER},
1332 {"Forwarding", VAR_SERVER},
1333 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1334 {"Hostnames", VAR_SERVER},
1335 {"IffOneQueue", VAR_SERVER},
1336 {"Interface", VAR_SERVER},
1337 {"KeyExpire", VAR_SERVER},
1338 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1339 {"LocalDiscovery", VAR_SERVER},
1340 {"MACExpire", VAR_SERVER},
1341 {"MaxConnectionBurst", VAR_SERVER},
1342 {"MaxOutputBufferSize", VAR_SERVER},
1343 {"MaxTimeout", VAR_SERVER},
1344 {"Mode", VAR_SERVER | VAR_SAFE},
1345 {"Name", VAR_SERVER},
1346 {"PingInterval", VAR_SERVER},
1347 {"PingTimeout", VAR_SERVER},
1348 {"PriorityInheritance", VAR_SERVER},
1349 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1350 {"PrivateKeyFile", VAR_SERVER},
1351 {"ProcessPriority", VAR_SERVER},
1352 {"Proxy", VAR_SERVER},
1353 {"ReplayWindow", VAR_SERVER},
1354 {"ScriptsExtension", VAR_SERVER},
1355 {"ScriptsInterpreter", VAR_SERVER},
1356 {"StrictSubnets", VAR_SERVER},
1357 {"TunnelServer", VAR_SERVER},
1358 {"UDPRcvBuf", VAR_SERVER},
1359 {"UDPSndBuf", VAR_SERVER},
1360 {"VDEGroup", VAR_SERVER},
1361 {"VDEPort", VAR_SERVER},
1362 /* Host configuration */
1363 {"Address", VAR_HOST | VAR_MULTIPLE},
1364 {"Cipher", VAR_SERVER | VAR_HOST},
1365 {"ClampMSS", VAR_SERVER | VAR_HOST},
1366 {"Compression", VAR_SERVER | VAR_HOST},
1367 {"Digest", VAR_SERVER | VAR_HOST},
1368 {"Ed25519PublicKey", VAR_HOST},
1369 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1370 {"IndirectData", VAR_SERVER | VAR_HOST},
1371 {"MACLength", VAR_SERVER | VAR_HOST},
1372 {"PMTU", VAR_SERVER | VAR_HOST},
1373 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1375 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1376 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1377 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1378 {"TCPOnly", VAR_SERVER | VAR_HOST},
1379 {"Weight", VAR_HOST | VAR_SAFE},
1383 static int cmd_config(int argc, char *argv[]) {
1385 fprintf(stderr, "Invalid number of arguments.\n");
1389 if(strcasecmp(argv[0], "config"))
1393 if(!strcasecmp(argv[1], "get")) {
1395 } else if(!strcasecmp(argv[1], "add")) {
1396 argv++, argc--, action = 1;
1397 } else if(!strcasecmp(argv[1], "del")) {
1398 argv++, argc--, action = -1;
1399 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1400 argv++, argc--, action = 0;
1404 fprintf(stderr, "Invalid number of arguments.\n");
1408 // Concatenate the rest of the command line
1409 strncpy(line, argv[1], sizeof line - 1);
1410 for(int i = 2; i < argc; i++) {
1411 strncat(line, " ", sizeof line - 1 - strlen(line));
1412 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1415 // Liberal parsing into node name, variable name and value.
1421 len = strcspn(line, "\t =");
1423 value += strspn(value, "\t ");
1426 value += strspn(value, "\t ");
1429 variable = strchr(line, '.');
1438 fprintf(stderr, "No variable given.\n");
1442 if(action >= 0 && !*value) {
1443 fprintf(stderr, "No value for variable given.\n");
1447 if(action < -1 && *value)
1450 /* Some simple checks. */
1452 bool warnonremove = false;
1454 for(int i = 0; variables[i].name; i++) {
1455 if(strcasecmp(variables[i].name, variable))
1459 variable = (char *)variables[i].name;
1461 /* Discourage use of obsolete variables. */
1463 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1465 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1467 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1472 /* Don't put server variables in host config files */
1474 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1476 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1478 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1483 /* Should this go into our own host config file? */
1485 if(!node && !(variables[i].type & VAR_SERVER)) {
1486 node = get_my_name(true);
1491 /* Change "add" into "set" for variables that do not allow multiple occurences.
1492 Turn on warnings when it seems variables might be removed unintentionally. */
1494 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1495 warnonremove = true;
1497 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1498 warnonremove = true;
1504 if(node && !check_id(node)) {
1505 fprintf(stderr, "Invalid name for node.\n");
1510 if(force || action < 0) {
1511 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1513 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1518 // Open the right configuration file.
1521 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, node);
1523 filename = tinc_conf;
1525 FILE *f = fopen(filename, "r");
1527 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1531 char *tmpfile = NULL;
1535 xasprintf(&tmpfile, "%s.config.tmp", filename);
1536 tf = fopen(tmpfile, "w");
1538 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1544 // Copy the file, making modifications on the fly, unless we are just getting a value.
1548 bool removed = false;
1551 while(fgets(buf1, sizeof buf1, f)) {
1552 buf1[sizeof buf1 - 1] = 0;
1553 strncpy(buf2, buf1, sizeof buf2);
1555 // Parse line in a simple way
1559 len = strcspn(buf2, "\t =");
1560 bvalue = buf2 + len;
1561 bvalue += strspn(bvalue, "\t ");
1562 if(*bvalue == '=') {
1564 bvalue += strspn(bvalue, "\t ");
1570 if(!strcasecmp(buf2, variable)) {
1574 printf("%s\n", bvalue);
1576 } else if(action == -1) {
1577 if(!*value || !strcasecmp(bvalue, value)) {
1582 } else if(action == 0) {
1583 // Warn if "set" was used for variables that can occur multiple times
1584 if(warnonremove && strcasecmp(bvalue, value))
1585 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1587 // Already set? Delete the rest...
1591 // Otherwise, replace.
1592 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1593 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1602 // Copy original line...
1603 if(fputs(buf1, tf) < 0) {
1604 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1608 // Add newline if it is missing...
1609 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1610 if(fputc('\n', tf) < 0) {
1611 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1618 // Make sure we read everything...
1619 if(ferror(f) || !feof(f)) {
1620 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1625 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1629 // Add new variable if necessary.
1630 if(action > 0 || (action == 0 && !set)) {
1631 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1632 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1639 fprintf(stderr, "No matching configuration variables found.\n");
1643 // Make sure we wrote everything...
1645 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1649 // Could we find what we had to remove?
1650 if(action < 0 && !removed) {
1652 fprintf(stderr, "No configuration variables deleted.\n");
1656 // Replace the configuration file with the new one
1658 if(remove(filename)) {
1659 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1663 if(rename(tmpfile, filename)) {
1664 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1668 // Silently try notifying a running tincd of changes.
1669 if(connect_tincd(false))
1670 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1675 bool check_id(const char *name) {
1679 for(int i = 0; i < strlen(name); i++) {
1680 if(!isalnum(name[i]) && name[i] != '_')
1687 static bool try_bind(int port) {
1688 struct addrinfo *ai = NULL;
1689 struct addrinfo hint = {
1690 .ai_flags = AI_PASSIVE,
1691 .ai_family = AF_UNSPEC,
1692 .ai_socktype = SOCK_STREAM,
1693 .ai_protocol = IPPROTO_TCP,
1697 snprintf(portstr, sizeof portstr, "%d", port);
1699 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1703 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1706 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1716 int check_port(char *name) {
1720 fprintf(stderr, "Warning: could not bind to port 655. ");
1722 for(int i = 0; i < 100; i++) {
1723 int port = 0x1000 + (rand() & 0x7fff);
1724 if(try_bind(port)) {
1726 xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1727 FILE *f = fopen(filename, "a");
1730 fprintf(stderr, "Please change tinc's Port manually.\n");
1734 fprintf(f, "Port = %d\n", port);
1736 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1741 fprintf(stderr, "Please change tinc's Port manually.\n");
1745 static int cmd_init(int argc, char *argv[]) {
1746 if(!access(tinc_conf, F_OK)) {
1747 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1752 fprintf(stderr, "Too many arguments!\n");
1754 } else if(argc < 2) {
1757 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1758 if(!fgets(buf, sizeof buf, stdin)) {
1759 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1762 int len = rstrip(buf);
1764 fprintf(stderr, "No name given!\n");
1769 fprintf(stderr, "No Name given!\n");
1773 name = strdup(argv[1]);
1775 fprintf(stderr, "No Name given!\n");
1780 if(!check_id(name)) {
1781 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1785 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1786 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1790 if(mkdir(confbase, 0777) && errno != EEXIST) {
1791 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1795 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1796 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1800 FILE *f = fopen(tinc_conf, "w");
1802 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1806 fprintf(f, "Name = %s\n", name);
1809 if(!rsa_keygen(2048, false) || !ed25519_keygen(false))
1816 xasprintf(&filename, "%s" SLASH "tinc-up", confbase);
1817 if(access(filename, F_OK)) {
1818 FILE *f = fopenmask(filename, "w", 0777);
1820 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1823 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");
1832 static int cmd_generate_keys(int argc, char *argv[]) {
1834 fprintf(stderr, "Too many arguments!\n");
1839 name = get_my_name(false);
1841 return !(rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true) && ed25519_keygen(true));
1844 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1846 fprintf(stderr, "Too many arguments!\n");
1851 name = get_my_name(false);
1853 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1856 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1858 fprintf(stderr, "Too many arguments!\n");
1863 name = get_my_name(false);
1865 return !ed25519_keygen(true);
1868 static int cmd_help(int argc, char *argv[]) {
1873 static int cmd_version(int argc, char *argv[]) {
1875 fprintf(stderr, "Too many arguments!\n");
1883 static int cmd_info(int argc, char *argv[]) {
1885 fprintf(stderr, "Invalid number of arguments.\n");
1889 if(!connect_tincd(true))
1892 return info(fd, argv[1]);
1895 static const char *conffiles[] = {
1906 static int cmd_edit(int argc, char *argv[]) {
1908 fprintf(stderr, "Invalid number of arguments.\n");
1912 char *filename = NULL;
1914 if(strncmp(argv[1], "hosts" SLASH, 6)) {
1915 for(int i = 0; conffiles[i]; i++) {
1916 if(!strcmp(argv[1], conffiles[i])) {
1917 xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]);
1926 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]);
1927 char *dash = strchr(argv[1], '-');
1930 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
1931 fprintf(stderr, "Invalid configuration filename.\n");
1939 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
1941 xasprintf(&command, "edit \"%s\"", filename);
1943 int result = system(command);
1947 // Silently try notifying a running tincd of changes.
1948 if(connect_tincd(false))
1949 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1954 static int export(const char *name, FILE *out) {
1956 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
1957 FILE *in = fopen(filename, "r");
1959 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1963 fprintf(out, "Name = %s\n", name);
1965 while(fgets(buf, sizeof buf, in)) {
1966 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
1971 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
1980 static int cmd_export(int argc, char *argv[]) {
1982 fprintf(stderr, "Too many arguments!\n");
1986 char *name = get_my_name(true);
1990 int result = export(name, stdout);
1998 static int cmd_export_all(int argc, char *argv[]) {
2000 fprintf(stderr, "Too many arguments!\n");
2004 DIR *dir = opendir(hosts_dir);
2006 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2014 while((ent = readdir(dir))) {
2015 if(!check_id(ent->d_name))
2021 printf("#---------------------------------------------------------------#\n");
2023 result |= export(ent->d_name, stdout);
2032 static int cmd_import(int argc, char *argv[]) {
2034 fprintf(stderr, "Too many arguments!\n");
2043 char *filename = NULL;
2045 bool firstline = true;
2047 while(fgets(buf, sizeof buf, in)) {
2048 if(sscanf(buf, "Name = %s", name) == 1) {
2051 if(!check_id(name)) {
2052 fprintf(stderr, "Invalid Name in input!\n");
2060 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
2062 if(!force && !access(filename, F_OK)) {
2063 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2068 out = fopen(filename, "w");
2070 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2076 } else if(firstline) {
2077 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2082 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2086 if(fputs(buf, out) < 0) {
2087 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2097 fprintf(stderr, "Imported %d host configuration files.\n", count);
2100 fprintf(stderr, "No host configuration files imported.\n");
2105 static int cmd_exchange(int argc, char *argv[]) {
2106 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2109 static int cmd_exchange_all(int argc, char *argv[]) {
2110 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2113 static int switch_network(char *name) {
2125 free(unixsocketname);
2126 unixsocketname = NULL;
2132 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2135 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2136 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2137 xasprintf(&prompt, "%s> ", identname);
2142 static int cmd_network(int argc, char *argv[]) {
2144 fprintf(stderr, "Too many arguments!\n");
2149 return switch_network(argv[1]);
2151 DIR *dir = opendir(confdir);
2153 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2158 while((ent = readdir(dir))) {
2159 if(*ent->d_name == '.')
2162 if(!strcmp(ent->d_name, "tinc.conf")) {
2168 xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2169 if(!access(fname, R_OK))
2170 printf("%s\n", ent->d_name);
2179 static const struct {
2180 const char *command;
2181 int (*function)(int argc, char *argv[]);
2184 {"start", cmd_start},
2186 {"restart", cmd_restart},
2187 {"reload", cmd_reload},
2189 {"purge", cmd_purge},
2190 {"debug", cmd_debug},
2191 {"retry", cmd_retry},
2192 {"connect", cmd_connect},
2193 {"disconnect", cmd_disconnect},
2198 {"config", cmd_config, true},
2199 {"add", cmd_config},
2200 {"del", cmd_config},
2201 {"get", cmd_config},
2202 {"set", cmd_config},
2204 {"generate-keys", cmd_generate_keys},
2205 {"generate-rsa-keys", cmd_generate_rsa_keys},
2206 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2208 {"version", cmd_version},
2211 {"export", cmd_export},
2212 {"export-all", cmd_export_all},
2213 {"import", cmd_import},
2214 {"exchange", cmd_exchange},
2215 {"exchange-all", cmd_exchange_all},
2216 {"invite", cmd_invite},
2218 {"network", cmd_network},
2222 #ifdef HAVE_READLINE
2223 static char *complete_command(const char *text, int state) {
2231 while(commands[i].command) {
2232 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2233 return xstrdup(commands[i].command);
2240 static char *complete_dump(const char *text, int state) {
2241 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2250 if(!strncasecmp(matches[i], text, strlen(text)))
2251 return xstrdup(matches[i]);
2258 static char *complete_config(const char *text, int state) {
2266 while(variables[i].name) {
2267 char *dot = strchr(text, '.');
2269 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2271 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2275 if(!strncasecmp(variables[i].name, text, strlen(text)))
2276 return xstrdup(variables[i].name);
2284 static char *complete_info(const char *text, int state) {
2288 if(!connect_tincd(false))
2290 // Check the list of nodes
2291 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2292 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2295 while(recvline(fd, line, sizeof line)) {
2297 int n = sscanf(line, "%d %d %s", &code, &req, item);
2307 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2311 if(!strncmp(item, text, strlen(text)))
2312 return xstrdup(strip_weight(item));
2318 static char *complete_nothing(const char *text, int state) {
2322 static char **completion (const char *text, int start, int end) {
2323 char **matches = NULL;
2326 matches = rl_completion_matches(text, complete_command);
2327 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2328 matches = rl_completion_matches(text, complete_dump);
2329 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2330 matches = rl_completion_matches(text, complete_config);
2331 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2332 matches = rl_completion_matches(text, complete_config);
2333 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2334 matches = rl_completion_matches(text, complete_config);
2335 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2336 matches = rl_completion_matches(text, complete_config);
2337 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2338 matches = rl_completion_matches(text, complete_info);
2344 static int cmd_shell(int argc, char *argv[]) {
2345 xasprintf(&prompt, "%s> ", identname);
2349 int maxargs = argc + 16;
2350 char **nargv = xmalloc(maxargs * sizeof *nargv);
2352 for(int i = 0; i < argc; i++)
2355 #ifdef HAVE_READLINE
2356 rl_readline_name = "tinc";
2357 rl_completion_entry_function = complete_nothing;
2358 rl_attempted_completion_function = completion;
2359 rl_filename_completion_desired = 0;
2364 #ifdef HAVE_READLINE
2368 rl_basic_word_break_characters = "\t\n ";
2369 line = readline(prompt);
2371 copy = xstrdup(line);
2373 line = fgets(buf, sizeof buf, stdin);
2377 fputs(prompt, stdout);
2379 line = fgets(buf, sizeof buf, stdin);
2385 /* Ignore comments */
2393 char *p = line + strspn(line, " \t\n");
2394 char *next = strtok(p, " \t\n");
2397 if(nargc >= maxargs) {
2398 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2401 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2406 next = strtok(NULL, " \t\n");
2412 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2417 for(int i = 0; commands[i].command; i++) {
2418 if(!strcasecmp(nargv[argc], commands[i].command)) {
2419 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2425 #ifdef HAVE_READLINE
2431 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2444 int main(int argc, char *argv[]) {
2445 program_name = argv[0];
2448 tty = isatty(0) && isatty(1);
2450 if(!parse_options(argc, argv))
2454 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2455 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2468 static struct WSAData wsa_state;
2470 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2471 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2480 return cmd_shell(argc, argv);
2482 for(int i = 0; commands[i].command; i++) {
2483 if(!strcasecmp(argv[optind], commands[i].command))
2484 return commands[i].function(argc - optind, argv + optind);
2487 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);