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 {"UDPRcvBuf", VAR_SERVER},
1368 {"UDPSndBuf", VAR_SERVER},
1369 {"VDEGroup", VAR_SERVER},
1370 {"VDEPort", VAR_SERVER},
1371 /* Host configuration */
1372 {"Address", VAR_HOST | VAR_MULTIPLE},
1373 {"Cipher", VAR_SERVER | VAR_HOST},
1374 {"ClampMSS", VAR_SERVER | VAR_HOST},
1375 {"Compression", VAR_SERVER | VAR_HOST},
1376 {"Digest", VAR_SERVER | VAR_HOST},
1377 {"Ed25519PublicKey", VAR_HOST},
1378 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1379 {"IndirectData", VAR_SERVER | VAR_HOST},
1380 {"MACLength", VAR_SERVER | VAR_HOST},
1381 {"PMTU", VAR_SERVER | VAR_HOST},
1382 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1384 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1385 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1386 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1387 {"TCPOnly", VAR_SERVER | VAR_HOST},
1388 {"Weight", VAR_HOST | VAR_SAFE},
1392 static int cmd_config(int argc, char *argv[]) {
1394 fprintf(stderr, "Invalid number of arguments.\n");
1398 if(strcasecmp(argv[0], "config"))
1402 if(!strcasecmp(argv[1], "get")) {
1404 } else if(!strcasecmp(argv[1], "add")) {
1405 argv++, argc--, action = 1;
1406 } else if(!strcasecmp(argv[1], "del")) {
1407 argv++, argc--, action = -1;
1408 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1409 argv++, argc--, action = 0;
1413 fprintf(stderr, "Invalid number of arguments.\n");
1417 // Concatenate the rest of the command line
1418 strncpy(line, argv[1], sizeof line - 1);
1419 for(int i = 2; i < argc; i++) {
1420 strncat(line, " ", sizeof line - 1 - strlen(line));
1421 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1424 // Liberal parsing into node name, variable name and value.
1430 len = strcspn(line, "\t =");
1432 value += strspn(value, "\t ");
1435 value += strspn(value, "\t ");
1438 variable = strchr(line, '.');
1447 fprintf(stderr, "No variable given.\n");
1451 if(action >= 0 && !*value) {
1452 fprintf(stderr, "No value for variable given.\n");
1456 if(action < -1 && *value)
1459 /* Some simple checks. */
1461 bool warnonremove = false;
1463 for(int i = 0; variables[i].name; i++) {
1464 if(strcasecmp(variables[i].name, variable))
1468 variable = (char *)variables[i].name;
1470 /* Discourage use of obsolete variables. */
1472 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1474 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1476 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1481 /* Don't put server variables in host config files */
1483 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1485 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1487 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1492 /* Should this go into our own host config file? */
1494 if(!node && !(variables[i].type & VAR_SERVER)) {
1495 node = get_my_name(true);
1500 /* Change "add" into "set" for variables that do not allow multiple occurences.
1501 Turn on warnings when it seems variables might be removed unintentionally. */
1503 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1504 warnonremove = true;
1506 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1507 warnonremove = true;
1513 if(node && !check_id(node)) {
1514 fprintf(stderr, "Invalid name for node.\n");
1519 if(force || action < 0) {
1520 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1522 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1527 // Open the right configuration file.
1530 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, node);
1532 filename = tinc_conf;
1534 FILE *f = fopen(filename, "r");
1536 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1540 char *tmpfile = NULL;
1544 xasprintf(&tmpfile, "%s.config.tmp", filename);
1545 tf = fopen(tmpfile, "w");
1547 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1553 // Copy the file, making modifications on the fly, unless we are just getting a value.
1557 bool removed = false;
1560 while(fgets(buf1, sizeof buf1, f)) {
1561 buf1[sizeof buf1 - 1] = 0;
1562 strncpy(buf2, buf1, sizeof buf2);
1564 // Parse line in a simple way
1568 len = strcspn(buf2, "\t =");
1569 bvalue = buf2 + len;
1570 bvalue += strspn(bvalue, "\t ");
1571 if(*bvalue == '=') {
1573 bvalue += strspn(bvalue, "\t ");
1579 if(!strcasecmp(buf2, variable)) {
1583 printf("%s\n", bvalue);
1585 } else if(action == -1) {
1586 if(!*value || !strcasecmp(bvalue, value)) {
1591 } else if(action == 0) {
1592 // Warn if "set" was used for variables that can occur multiple times
1593 if(warnonremove && strcasecmp(bvalue, value))
1594 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1596 // Already set? Delete the rest...
1600 // Otherwise, replace.
1601 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1602 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1611 // Copy original line...
1612 if(fputs(buf1, tf) < 0) {
1613 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1617 // Add newline if it is missing...
1618 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1619 if(fputc('\n', tf) < 0) {
1620 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1627 // Make sure we read everything...
1628 if(ferror(f) || !feof(f)) {
1629 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1634 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1638 // Add new variable if necessary.
1639 if(action > 0 || (action == 0 && !set)) {
1640 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1641 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1650 fprintf(stderr, "No matching configuration variables found.\n");
1655 // Make sure we wrote everything...
1657 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1661 // Could we find what we had to remove?
1662 if(action < 0 && !removed) {
1664 fprintf(stderr, "No configuration variables deleted.\n");
1668 // Replace the configuration file with the new one
1670 if(remove(filename)) {
1671 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1675 if(rename(tmpfile, filename)) {
1676 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1680 // Silently try notifying a running tincd of changes.
1681 if(connect_tincd(false))
1682 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
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 #ifndef DISABLE_LEGACY
1810 if(!rsa_keygen(2048, false))
1814 if(!ed25519_keygen(false))
1821 xasprintf(&filename, "%s" SLASH "tinc-up", confbase);
1822 if(access(filename, F_OK)) {
1823 FILE *f = fopenmask(filename, "w", 0777);
1825 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1828 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");
1837 static int cmd_generate_keys(int argc, char *argv[]) {
1838 #ifdef DISABLE_LEGACY
1843 fprintf(stderr, "Too many arguments!\n");
1848 name = get_my_name(false);
1850 #ifndef DISABLE_LEGACY
1851 if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true)
1855 if(!ed25519_keygen(true))
1861 #ifndef DISABLE_LEGACY
1862 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1864 fprintf(stderr, "Too many arguments!\n");
1869 name = get_my_name(false);
1871 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1875 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1877 fprintf(stderr, "Too many arguments!\n");
1882 name = get_my_name(false);
1884 return !ed25519_keygen(true);
1887 static int cmd_help(int argc, char *argv[]) {
1892 static int cmd_version(int argc, char *argv[]) {
1894 fprintf(stderr, "Too many arguments!\n");
1902 static int cmd_info(int argc, char *argv[]) {
1904 fprintf(stderr, "Invalid number of arguments.\n");
1908 if(!connect_tincd(true))
1911 return info(fd, argv[1]);
1914 static const char *conffiles[] = {
1925 static int cmd_edit(int argc, char *argv[]) {
1927 fprintf(stderr, "Invalid number of arguments.\n");
1931 char *filename = NULL;
1933 if(strncmp(argv[1], "hosts" SLASH, 6)) {
1934 for(int i = 0; conffiles[i]; i++) {
1935 if(!strcmp(argv[1], conffiles[i])) {
1936 xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]);
1945 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]);
1946 char *dash = strchr(argv[1], '-');
1949 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
1950 fprintf(stderr, "Invalid configuration filename.\n");
1958 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
1960 xasprintf(&command, "edit \"%s\"", filename);
1962 int result = system(command);
1966 // Silently try notifying a running tincd of changes.
1967 if(connect_tincd(false))
1968 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1973 static int export(const char *name, FILE *out) {
1975 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
1976 FILE *in = fopen(filename, "r");
1978 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1982 fprintf(out, "Name = %s\n", name);
1984 while(fgets(buf, sizeof buf, in)) {
1985 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
1990 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
1999 static int cmd_export(int argc, char *argv[]) {
2001 fprintf(stderr, "Too many arguments!\n");
2005 char *name = get_my_name(true);
2009 int result = export(name, stdout);
2017 static int cmd_export_all(int argc, char *argv[]) {
2019 fprintf(stderr, "Too many arguments!\n");
2023 DIR *dir = opendir(hosts_dir);
2025 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2033 while((ent = readdir(dir))) {
2034 if(!check_id(ent->d_name))
2040 printf("#---------------------------------------------------------------#\n");
2042 result |= export(ent->d_name, stdout);
2051 static int cmd_import(int argc, char *argv[]) {
2053 fprintf(stderr, "Too many arguments!\n");
2062 char *filename = NULL;
2064 bool firstline = true;
2066 while(fgets(buf, sizeof buf, in)) {
2067 if(sscanf(buf, "Name = %s", name) == 1) {
2070 if(!check_id(name)) {
2071 fprintf(stderr, "Invalid Name in input!\n");
2079 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
2081 if(!force && !access(filename, F_OK)) {
2082 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2087 out = fopen(filename, "w");
2089 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2095 } else if(firstline) {
2096 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2101 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2105 if(fputs(buf, out) < 0) {
2106 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2116 fprintf(stderr, "Imported %d host configuration files.\n", count);
2119 fprintf(stderr, "No host configuration files imported.\n");
2124 static int cmd_exchange(int argc, char *argv[]) {
2125 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2128 static int cmd_exchange_all(int argc, char *argv[]) {
2129 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2132 static int switch_network(char *name) {
2144 free(unixsocketname);
2145 unixsocketname = NULL;
2151 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2154 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2155 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2156 xasprintf(&prompt, "%s> ", identname);
2161 static int cmd_network(int argc, char *argv[]) {
2163 fprintf(stderr, "Too many arguments!\n");
2168 return switch_network(argv[1]);
2170 DIR *dir = opendir(confdir);
2172 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2177 while((ent = readdir(dir))) {
2178 if(*ent->d_name == '.')
2181 if(!strcmp(ent->d_name, "tinc.conf")) {
2187 xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2188 if(!access(fname, R_OK))
2189 printf("%s\n", ent->d_name);
2198 static const struct {
2199 const char *command;
2200 int (*function)(int argc, char *argv[]);
2203 {"start", cmd_start},
2205 {"restart", cmd_restart},
2206 {"reload", cmd_reload},
2208 {"purge", cmd_purge},
2209 {"debug", cmd_debug},
2210 {"retry", cmd_retry},
2211 {"connect", cmd_connect},
2212 {"disconnect", cmd_disconnect},
2217 {"config", cmd_config, true},
2218 {"add", cmd_config},
2219 {"del", cmd_config},
2220 {"get", cmd_config},
2221 {"set", cmd_config},
2223 {"generate-keys", cmd_generate_keys},
2224 #ifndef DISABLE_LEGACY
2225 {"generate-rsa-keys", cmd_generate_rsa_keys},
2227 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2229 {"version", cmd_version},
2232 {"export", cmd_export},
2233 {"export-all", cmd_export_all},
2234 {"import", cmd_import},
2235 {"exchange", cmd_exchange},
2236 {"exchange-all", cmd_exchange_all},
2237 {"invite", cmd_invite},
2239 {"network", cmd_network},
2243 #ifdef HAVE_READLINE
2244 static char *complete_command(const char *text, int state) {
2252 while(commands[i].command) {
2253 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2254 return xstrdup(commands[i].command);
2261 static char *complete_dump(const char *text, int state) {
2262 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2271 if(!strncasecmp(matches[i], text, strlen(text)))
2272 return xstrdup(matches[i]);
2279 static char *complete_config(const char *text, int state) {
2287 while(variables[i].name) {
2288 char *dot = strchr(text, '.');
2290 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2292 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2296 if(!strncasecmp(variables[i].name, text, strlen(text)))
2297 return xstrdup(variables[i].name);
2305 static char *complete_info(const char *text, int state) {
2309 if(!connect_tincd(false))
2311 // Check the list of nodes
2312 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2313 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2316 while(recvline(fd, line, sizeof line)) {
2318 int n = sscanf(line, "%d %d %s", &code, &req, item);
2328 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2332 if(!strncmp(item, text, strlen(text)))
2333 return xstrdup(strip_weight(item));
2339 static char *complete_nothing(const char *text, int state) {
2343 static char **completion (const char *text, int start, int end) {
2344 char **matches = NULL;
2347 matches = rl_completion_matches(text, complete_command);
2348 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2349 matches = rl_completion_matches(text, complete_dump);
2350 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2351 matches = rl_completion_matches(text, complete_config);
2352 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2353 matches = rl_completion_matches(text, complete_config);
2354 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2355 matches = rl_completion_matches(text, complete_config);
2356 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2357 matches = rl_completion_matches(text, complete_config);
2358 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2359 matches = rl_completion_matches(text, complete_info);
2365 static int cmd_shell(int argc, char *argv[]) {
2366 xasprintf(&prompt, "%s> ", identname);
2370 int maxargs = argc + 16;
2371 char **nargv = xmalloc(maxargs * sizeof *nargv);
2373 for(int i = 0; i < argc; i++)
2376 #ifdef HAVE_READLINE
2377 rl_readline_name = "tinc";
2378 rl_completion_entry_function = complete_nothing;
2379 rl_attempted_completion_function = completion;
2380 rl_filename_completion_desired = 0;
2385 #ifdef HAVE_READLINE
2389 rl_basic_word_break_characters = "\t\n ";
2390 line = readline(prompt);
2392 copy = xstrdup(line);
2394 line = fgets(buf, sizeof buf, stdin);
2398 fputs(prompt, stdout);
2400 line = fgets(buf, sizeof buf, stdin);
2406 /* Ignore comments */
2414 char *p = line + strspn(line, " \t\n");
2415 char *next = strtok(p, " \t\n");
2418 if(nargc >= maxargs) {
2419 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2422 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2427 next = strtok(NULL, " \t\n");
2433 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2438 for(int i = 0; commands[i].command; i++) {
2439 if(!strcasecmp(nargv[argc], commands[i].command)) {
2440 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2446 #ifdef HAVE_READLINE
2452 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2465 int main(int argc, char *argv[]) {
2466 program_name = argv[0];
2469 tty = isatty(0) && isatty(1);
2471 if(!parse_options(argc, argv))
2475 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2476 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2489 static struct WSAData wsa_state;
2491 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2492 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2501 return cmd_shell(argc, argv);
2503 for(int i = 0; commands[i].command; i++) {
2504 if(!strcasecmp(argv[optind], commands[i].command))
2505 return commands[i].function(argc - optind, argv + optind);
2508 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);