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 {"config", required_argument, NULL, 'c'},
79 {"net", required_argument, NULL, 'n'},
80 {"help", no_argument, NULL, 1},
81 {"version", no_argument, NULL, 2},
82 {"pidfile", required_argument, NULL, 3},
83 {"force", no_argument, NULL, 4},
87 static void version(void) {
88 printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
89 VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
90 printf("Copyright (C) 1998-2012 Ivo Timmermans, Guus Sliepen and others.\n"
91 "See the AUTHORS file for a complete list.\n\n"
92 "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
93 "and you are welcome to redistribute it under certain conditions;\n"
94 "see the file COPYING for details.\n");
97 static void usage(bool status) {
99 fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
101 printf("Usage: %s [options] command\n\n", program_name);
102 printf("Valid options are:\n"
103 " -c, --config=DIR Read configuration options from DIR.\n"
104 " -n, --net=NETNAME Connect to net NETNAME.\n"
105 " --pidfile=FILENAME Read control cookie from FILENAME.\n"
106 " --help Display this help and exit.\n"
107 " --version Output version information and exit.\n"
109 "Valid commands are:\n"
110 " init [name] Create initial configuration files.\n"
111 " get VARIABLE Print current value of VARIABLE\n"
112 " set VARIABLE VALUE Set VARIABLE to VALUE\n"
113 " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
114 " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
115 " start [tincd options] Start tincd.\n"
116 " stop Stop tincd.\n"
117 " restart [tincd options] Restart tincd.\n"
118 " reload Partially reload configuration of running tincd.\n"
119 " pid Show PID of currently running tincd.\n"
120 " generate-keys [bits] Generate new RSA and Ed25519 public/private keypairs.\n"
121 " generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
122 " generate-ed25519-keys Generate a new Ed25519 public/private keypair.\n"
123 " dump Dump a list of one of the following things:\n"
124 " [reachable] nodes - all known nodes in the VPN\n"
125 " edges - all known connections in the VPN\n"
126 " subnets - all known subnets in the VPN\n"
127 " connections - all meta connections with ourself\n"
128 " [di]graph - graph of the VPN in dotty format\n"
129 " info NODE|SUBNET|ADDRESS Give information about a particular NODE, SUBNET or ADDRESS.\n"
130 " purge Purge unreachable nodes\n"
131 " debug N Set debug level\n"
132 " retry Retry all outgoing connections\n"
133 " disconnect NODE Close meta connection with NODE\n"
135 " top Show real-time statistics\n"
137 " pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
138 " log [level] Dump log output [up to the specified level]\n"
139 " export Export host configuration of local node to standard output\n"
140 " export-all Export all host configuration files to standard output\n"
141 " import [--force] Import host configuration file(s) from standard input\n"
142 " exchange [--force] Same as export followed by import\n"
143 " exchange-all [--force] Same as export-all followed by import\n"
144 " invite NODE [...] Generate an invitation for NODE\n"
145 " join INVITATION Join a VPN using an INVITIATION\n"
146 " network [NETNAME] List all known networks, or switch to the one named NETNAME.\n"
148 printf("Report bugs to tinc@tinc-vpn.org.\n");
152 static bool parse_options(int argc, char **argv) {
154 int option_index = 0;
156 while((r = getopt_long(argc, argv, "+c:n:", long_options, &option_index)) != EOF) {
158 case 0: /* long option */
161 case 'c': /* config file */
162 confbase = xstrdup(optarg);
163 confbasegiven = true;
166 case 'n': /* net name given */
167 netname = xstrdup(optarg);
170 case 1: /* show help */
174 case 2: /* show version */
178 case 3: /* open control socket here */
179 pidfilename = xstrdup(optarg);
186 case '?': /* wrong options */
195 if(!netname && (netname = getenv("NETNAME")))
196 netname = xstrdup(netname);
198 /* netname "." is special: a "top-level name" */
200 if(netname && (!*netname || !strcmp(netname, "."))) {
205 if(netname && (strpbrk(netname, "\\/") || *netname == '.')) {
206 fprintf(stderr, "Invalid character in netname!\n");
213 /* Open a file with the desired permissions, minus the umask.
214 Also, if we want to create an executable file, we call fchmod()
215 to set the executable bits. */
217 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
218 mode_t mask = umask(0);
221 FILE *f = fopen(filename, mode);
223 if((perms & 0444) && f)
224 fchmod(fileno(f), perms);
230 static void disable_old_keys(const char *filename, const char *what) {
231 char tmpfile[PATH_MAX] = "";
233 bool disabled = false;
238 r = fopen(filename, "r");
242 snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
244 struct stat st = {.st_mode = 0600};
245 fstat(fileno(r), &st);
246 w = fopenmask(tmpfile, "w", st.st_mode);
248 while(fgets(buf, sizeof buf, r)) {
249 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
250 if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
256 bool ed25519pubkey = !strncasecmp(buf, "Ed25519PublicKey", 16) && strchr(" \t=", buf[16]) && strstr(what, "Ed25519");
262 if(block || ed25519pubkey)
264 if(fputs(buf, w) < 0) {
270 if(block && !strncmp(buf, "-----END ", 9))
277 if(ferror(r) || fclose(r) < 0)
282 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
289 // We cannot atomically replace files on Windows.
290 char bakfile[PATH_MAX] = "";
291 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
292 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
293 rename(bakfile, filename);
295 if(rename(tmpfile, filename)) {
297 fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
302 fprintf(stderr, "Warning: old key(s) found and disabled.\n");
309 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
315 /* Check stdin and stdout */
317 /* Ask for a file and/or directory name. */
318 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
320 if(fgets(buf, sizeof buf, stdin) == NULL) {
321 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
325 size_t len = strlen(buf);
334 if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
336 if(filename[0] != '/') {
338 /* The directory is a relative path or a filename. */
339 directory = get_current_dir_name();
340 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
344 disable_old_keys(filename, what);
346 /* Open it first to keep the inode busy */
348 r = fopenmask(filename, mode, perms);
351 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
359 Generate a public/private Ed25519 keypair, and ask for a file to store
362 static bool ed25519_keygen(bool ask) {
365 char *pubname, *privname;
367 fprintf(stderr, "Generating Ed25519 keypair:\n");
369 if(!(key = ecdsa_generate())) {
370 fprintf(stderr, "Error during key generation!\n");
373 fprintf(stderr, "Done.\n");
375 xasprintf(&privname, "%s" SLASH "ed25519_key.priv", confbase);
376 f = ask_and_open(privname, "private Ed25519 key", "a", ask, 0600);
382 if(!ecdsa_write_pem_private_key(key, f)) {
383 fprintf(stderr, "Error writing private key!\n");
392 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
394 xasprintf(&pubname, "%s" SLASH "ed25519_key.pub", confbase);
396 f = ask_and_open(pubname, "public Ed25519 key", "a", ask, 0666);
402 char *pubkey = ecdsa_get_base64_public_key(key);
403 fprintf(f, "Ed25519PublicKey = %s\n", pubkey);
413 Generate a public/private RSA keypair, and ask for a file to store
416 static bool rsa_keygen(int bits, bool ask) {
419 char *pubname, *privname;
421 // Make sure the key size is a multiple of 8 bits.
424 // Force them to be between 1024 and 8192 bits long.
430 fprintf(stderr, "Generating %d bits keys:\n", bits);
432 if(!(key = rsa_generate(bits, 0x10001))) {
433 fprintf(stderr, "Error during key generation!\n");
436 fprintf(stderr, "Done.\n");
438 xasprintf(&privname, "%s" SLASH "rsa_key.priv", confbase);
439 f = ask_and_open(privname, "private RSA key", "a", ask, 0600);
445 if(!rsa_write_pem_private_key(key, f)) {
446 fprintf(stderr, "Error writing private key!\n");
455 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
457 xasprintf(&pubname, "%s" SLASH "rsa_key.pub", confbase);
459 f = ask_and_open(pubname, "public RSA key", "a", ask, 0666);
465 if(!rsa_write_pem_public_key(key, f)) {
466 fprintf(stderr, "Error writing public key!\n");
481 bool recvline(int fd, char *line, size_t len) {
482 char *newline = NULL;
487 while(!(newline = memchr(buffer, '\n', blen))) {
488 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
489 if(result == -1 && sockerrno == EINTR)
496 if(newline - buffer >= len)
499 len = newline - buffer;
501 memcpy(line, buffer, len);
503 memmove(buffer, newline + 1, blen - len - 1);
509 bool recvdata(int fd, char *data, size_t len) {
514 int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
515 if(result == -1 && sockerrno == EINTR)
522 memcpy(data, buffer, len);
523 memmove(buffer, buffer + len, blen - len);
529 bool sendline(int fd, char *format, ...) {
530 static char buffer[4096];
535 va_start(ap, format);
536 blen = vsnprintf(buffer, sizeof buffer, format, ap);
539 if(blen < 1 || blen >= sizeof buffer)
546 int result = send(fd, p, blen, MSG_NOSIGNAL);
547 if(result == -1 && sockerrno == EINTR)
558 static void pcap(int fd, FILE *out, int snaplen) {
559 sendline(fd, "%d %d %d", CONTROL, REQ_PCAP, snaplen);
567 uint32_t tz_accuracy;
574 snaplen ?: sizeof data,
587 fwrite(&header, sizeof header, 1, out);
591 while(recvline(fd, line, sizeof line)) {
593 int n = sscanf(line, "%d %d %d", &code, &req, &len);
594 gettimeofday(&tv, NULL);
595 if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
597 if(!recvdata(fd, data, len))
599 packet.tv_sec = tv.tv_sec;
600 packet.tv_usec = tv.tv_usec;
602 packet.origlen = len;
603 fwrite(&packet, sizeof packet, 1, out);
604 fwrite(data, len, 1, out);
609 static void logcontrol(int fd, FILE *out, int level) {
610 sendline(fd, "%d %d %d", CONTROL, REQ_LOG, level);
614 while(recvline(fd, line, sizeof line)) {
616 int n = sscanf(line, "%d %d %d", &code, &req, &len);
617 if(n != 3 || code != CONTROL || req != REQ_LOG || len < 0 || len > sizeof data)
619 if(!recvdata(fd, data, len))
621 fwrite(data, len, 1, out);
628 static bool remove_service(void) {
629 SC_HANDLE manager = NULL;
630 SC_HANDLE service = NULL;
631 SERVICE_STATUS status = {0};
633 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
635 fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
639 service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
642 fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
646 if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
647 fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
649 fprintf(stderr, "%s service stopped\n", identname);
651 if(!DeleteService(service)) {
652 fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
656 fprintf(stderr, "%s service removed\n", identname);
662 bool connect_tincd(bool verbose) {
667 struct timeval tv = {0, 0};
668 if(select(fd + 1, &r, NULL, NULL, &tv)) {
669 fprintf(stderr, "Previous connection to tincd lost, reconnecting.\n");
677 FILE *f = fopen(pidfilename, "r");
680 fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
687 if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
689 fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
697 struct sockaddr_un sa;
698 sa.sun_family = AF_UNIX;
699 strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path);
701 fd = socket(AF_UNIX, SOCK_STREAM, 0);
704 fprintf(stderr, "Cannot create UNIX socket: %s\n", sockstrerror(sockerrno));
708 if(connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
710 fprintf(stderr, "Cannot connect to UNIX socket %s: %s\n", unixsocketname, sockstrerror(sockerrno));
716 struct addrinfo hints = {
717 .ai_family = AF_UNSPEC,
718 .ai_socktype = SOCK_STREAM,
719 .ai_protocol = IPPROTO_TCP,
723 struct addrinfo *res = NULL;
725 if(getaddrinfo(host, port, &hints, &res) || !res) {
727 fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
731 fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
734 fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
739 unsigned long arg = 0;
741 if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
743 fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
747 if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
749 fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
759 static const int one = 1;
760 setsockopt(c, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
766 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
768 fprintf(stderr, "Cannot read greeting from control socket: %s\n", sockstrerror(sockerrno));
774 sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
776 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
778 fprintf(stderr, "Could not fully establish control socket connection\n");
788 static int cmd_start(int argc, char *argv[]) {
789 if(connect_tincd(false)) {
791 fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n", netname, pid);
793 fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
798 char *slash = strrchr(program_name, '/');
801 if ((c = strrchr(program_name, '\\')) > slash)
806 xasprintf(&c, "%.*stincd", (int)(slash - program_name), program_name);
811 char **nargv = xzalloc((optind + argc) * sizeof *nargv);
816 Windows has no real concept of an "argv array". A command line is just one string.
817 The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
818 it uses quotes to handle spaces in arguments.
819 Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
820 If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
821 into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
823 xasprintf(&arg0, "\"%s\"", arg0);
825 nargv[nargc++] = arg0;
826 for(int i = 1; i < optind; i++)
827 nargv[nargc++] = orig_argv[i];
828 for(int i = 1; i < argc; i++)
829 nargv[nargc++] = argv[i];
832 int status = spawnvp(_P_WAIT, c, nargv);
834 fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
841 fprintf(stderr, "Could not fork: %s\n", strerror(errno));
847 exit(execvp(c, nargv));
851 int status = -1, result;
853 signal(SIGINT, SIG_IGN);
855 result = waitpid(pid, &status, 0);
857 signal(SIGINT, SIG_DFL);
860 if(result != pid || !WIFEXITED(status) || WEXITSTATUS(status)) {
861 fprintf(stderr, "Error starting %s\n", c);
869 static int cmd_stop(int argc, char *argv[]) {
871 fprintf(stderr, "Too many arguments!\n");
876 if(!connect_tincd(true)) {
878 if(kill(pid, SIGTERM)) {
879 fprintf(stderr, "Could not send TERM signal to process with PID %u: %s\n", pid, strerror(errno));
883 fprintf(stderr, "Sent TERM signal to process with PID %u.\n", pid);
884 waitpid(pid, NULL, 0);
891 sendline(fd, "%d %d", CONTROL, REQ_STOP);
893 while(recvline(fd, line, sizeof line)) {
894 // Wait for tincd to close the connection...
897 if(!remove_service())
907 static int cmd_restart(int argc, char *argv[]) {
909 return cmd_start(argc, argv);
912 static int cmd_reload(int argc, char *argv[]) {
914 fprintf(stderr, "Too many arguments!\n");
918 if(!connect_tincd(true))
921 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
922 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
923 fprintf(stderr, "Could not reload configuration.\n");
931 static int cmd_dump(int argc, char *argv[]) {
932 bool only_reachable = false;
934 if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
935 if(strcasecmp(argv[2], "nodes")) {
936 fprintf(stderr, "`reachable' only supported for nodes.\n");
940 only_reachable = true;
946 fprintf(stderr, "Invalid number of arguments.\n");
951 if(!connect_tincd(true))
956 if(!strcasecmp(argv[1], "nodes"))
957 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
958 else if(!strcasecmp(argv[1], "edges"))
959 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
960 else if(!strcasecmp(argv[1], "subnets"))
961 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
962 else if(!strcasecmp(argv[1], "connections"))
963 sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
964 else if(!strcasecmp(argv[1], "graph")) {
965 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
966 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
968 } else if(!strcasecmp(argv[1], "digraph")) {
969 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
970 sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
973 fprintf(stderr, "Unknown dump type '%s'.\n", argv[1]);
980 else if(do_graph == 2)
981 printf("digraph {\n");
983 while(recvline(fd, line, sizeof line)) {
984 char node1[4096], node2[4096];
985 int n = sscanf(line, "%d %d %s %s", &code, &req, node1, node2);
987 if(do_graph && req == REQ_DUMP_NODES)
1004 char local_host[4096];
1005 char local_port[4096];
1008 int cipher, digest, maclength, compression, distance, socket, weight;
1009 short int pmtu, minmtu, maxmtu;
1010 unsigned int options, status_int;
1011 node_status_t status;
1012 long int last_state_change;
1015 case REQ_DUMP_NODES: {
1016 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);
1018 fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
1022 memcpy(&status, &status_int, sizeof status);
1025 const char *color = "black";
1026 if(!strcmp(host, "MYSELF"))
1028 else if(!status.reachable)
1030 else if(strcmp(via, node))
1032 else if(!status.validkey)
1036 printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
1038 if(only_reachable && !status.reachable)
1040 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",
1041 node, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
1045 case REQ_DUMP_EDGES: {
1046 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);
1048 fprintf(stderr, "Unable to parse edge dump from tincd.\n");
1053 float w = 1 + 65536.0 / weight;
1054 if(do_graph == 1 && strcmp(node1, node2) > 0)
1055 printf(" %s -- %s [w = %f, weight = %f];\n", node1, node2, w, w);
1056 else if(do_graph == 2)
1057 printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
1059 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);
1063 case REQ_DUMP_SUBNETS: {
1064 int n = sscanf(line, "%*d %*d %s %s", subnet, node);
1066 fprintf(stderr, "Unable to parse subnet dump from tincd.\n");
1069 printf("%s owner %s\n", strip_weight(subnet), node);
1072 case REQ_DUMP_CONNECTIONS: {
1073 int n = sscanf(line, "%*d %*d %s %s port %s %x %d %x", node, host, port, &options, &socket, &status_int);
1075 fprintf(stderr, "Unable to parse connection dump from tincd.\n");
1078 printf("%s at %s port %s options %x socket %d status %x\n", node, host, port, options, socket, status_int);
1082 fprintf(stderr, "Unable to parse dump from tincd.\n");
1087 fprintf(stderr, "Error receiving dump.\n");
1091 static int cmd_purge(int argc, char *argv[]) {
1093 fprintf(stderr, "Too many arguments!\n");
1097 if(!connect_tincd(true))
1100 sendline(fd, "%d %d", CONTROL, REQ_PURGE);
1101 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
1102 fprintf(stderr, "Could not purge old information.\n");
1109 static int cmd_debug(int argc, char *argv[]) {
1111 fprintf(stderr, "Invalid number of arguments.\n");
1115 if(!connect_tincd(true))
1118 int debuglevel = atoi(argv[1]);
1121 sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
1122 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
1123 fprintf(stderr, "Could not set debug level.\n");
1127 fprintf(stderr, "Old level %d, new level %d.\n", origlevel, debuglevel);
1131 static int cmd_retry(int argc, char *argv[]) {
1133 fprintf(stderr, "Too many arguments!\n");
1137 if(!connect_tincd(true))
1140 sendline(fd, "%d %d", CONTROL, REQ_RETRY);
1141 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
1142 fprintf(stderr, "Could not retry outgoing connections.\n");
1149 static int cmd_connect(int argc, char *argv[]) {
1151 fprintf(stderr, "Invalid number of arguments.\n");
1155 if(!check_id(argv[1])) {
1156 fprintf(stderr, "Invalid name for node.\n");
1160 if(!connect_tincd(true))
1163 sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, argv[1]);
1164 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
1165 fprintf(stderr, "Could not connect to %s.\n", argv[1]);
1172 static int cmd_disconnect(int argc, char *argv[]) {
1174 fprintf(stderr, "Invalid number of arguments.\n");
1178 if(!check_id(argv[1])) {
1179 fprintf(stderr, "Invalid name for node.\n");
1183 if(!connect_tincd(true))
1186 sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, argv[1]);
1187 if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
1188 fprintf(stderr, "Could not disconnect %s.\n", argv[1]);
1195 static int cmd_top(int argc, char *argv[]) {
1197 fprintf(stderr, "Too many arguments!\n");
1202 if(!connect_tincd(true))
1208 fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
1213 static int cmd_pcap(int argc, char *argv[]) {
1215 fprintf(stderr, "Too many arguments!\n");
1219 if(!connect_tincd(true))
1222 pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
1227 static void sigint_handler(int sig) {
1228 fprintf(stderr, "\n");
1229 shutdown(fd, SHUT_RDWR);
1233 static int cmd_log(int argc, char *argv[]) {
1235 fprintf(stderr, "Too many arguments!\n");
1239 if(!connect_tincd(true))
1243 signal(SIGINT, sigint_handler);
1246 logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
1249 signal(SIGINT, SIG_DFL);
1257 static int cmd_pid(int argc, char *argv[]) {
1259 fprintf(stderr, "Too many arguments!\n");
1263 if(!connect_tincd(true) && !pid)
1266 printf("%d\n", pid);
1270 int rstrip(char *value) {
1271 int len = strlen(value);
1272 while(len && strchr("\t\r\n ", value[len - 1]))
1277 char *get_my_name(bool verbose) {
1278 FILE *f = fopen(tinc_conf, "r");
1281 fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
1287 while(fgets(buf, sizeof buf, f)) {
1288 int len = strcspn(buf, "\t =");
1290 value += strspn(value, "\t ");
1293 value += strspn(value, "\t ");
1298 if(strcasecmp(buf, "Name"))
1302 return strdup(value);
1308 fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
1312 const var_t variables[] = {
1313 /* Server configuration */
1314 {"AddressFamily", VAR_SERVER},
1315 {"AutoConnect", VAR_SERVER | VAR_SAFE},
1316 {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
1317 {"BindToInterface", VAR_SERVER},
1318 {"Broadcast", VAR_SERVER | VAR_SAFE},
1319 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
1320 {"DecrementTTL", VAR_SERVER},
1321 {"Device", VAR_SERVER},
1322 {"DeviceType", VAR_SERVER},
1323 {"DirectOnly", VAR_SERVER},
1324 {"Ed25519PrivateKeyFile", VAR_SERVER},
1325 {"ExperimentalProtocol", VAR_SERVER},
1326 {"Forwarding", VAR_SERVER},
1327 {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
1328 {"Hostnames", VAR_SERVER},
1329 {"IffOneQueue", VAR_SERVER},
1330 {"Interface", VAR_SERVER},
1331 {"KeyExpire", VAR_SERVER},
1332 {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
1333 {"LocalDiscovery", VAR_SERVER},
1334 {"MACExpire", VAR_SERVER},
1335 {"MaxConnectionBurst", VAR_SERVER},
1336 {"MaxOutputBufferSize", VAR_SERVER},
1337 {"MaxTimeout", VAR_SERVER},
1338 {"Mode", VAR_SERVER | VAR_SAFE},
1339 {"Name", VAR_SERVER},
1340 {"PingInterval", VAR_SERVER},
1341 {"PingTimeout", VAR_SERVER},
1342 {"PriorityInheritance", VAR_SERVER},
1343 {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
1344 {"PrivateKeyFile", VAR_SERVER},
1345 {"ProcessPriority", VAR_SERVER},
1346 {"Proxy", VAR_SERVER},
1347 {"ReplayWindow", VAR_SERVER},
1348 {"ScriptsExtension", VAR_SERVER},
1349 {"ScriptsInterpreter", VAR_SERVER},
1350 {"StrictSubnets", VAR_SERVER},
1351 {"TunnelServer", VAR_SERVER},
1352 {"UDPRcvBuf", VAR_SERVER},
1353 {"UDPSndBuf", VAR_SERVER},
1354 {"VDEGroup", VAR_SERVER},
1355 {"VDEPort", VAR_SERVER},
1356 /* Host configuration */
1357 {"Address", VAR_HOST | VAR_MULTIPLE},
1358 {"Cipher", VAR_SERVER | VAR_HOST},
1359 {"ClampMSS", VAR_SERVER | VAR_HOST},
1360 {"Compression", VAR_SERVER | VAR_HOST},
1361 {"Digest", VAR_SERVER | VAR_HOST},
1362 {"Ed25519PublicKey", VAR_HOST},
1363 {"Ed25519PublicKeyFile", VAR_SERVER | VAR_HOST},
1364 {"IndirectData", VAR_SERVER | VAR_HOST},
1365 {"MACLength", VAR_SERVER | VAR_HOST},
1366 {"PMTU", VAR_SERVER | VAR_HOST},
1367 {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
1369 {"PublicKey", VAR_HOST | VAR_OBSOLETE},
1370 {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
1371 {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
1372 {"TCPOnly", VAR_SERVER | VAR_HOST},
1373 {"Weight", VAR_HOST | VAR_SAFE},
1377 static int cmd_config(int argc, char *argv[]) {
1379 fprintf(stderr, "Invalid number of arguments.\n");
1383 if(strcasecmp(argv[0], "config"))
1387 if(!strcasecmp(argv[1], "get")) {
1389 } else if(!strcasecmp(argv[1], "add")) {
1390 argv++, argc--, action = 1;
1391 } else if(!strcasecmp(argv[1], "del")) {
1392 argv++, argc--, action = -1;
1393 } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
1394 argv++, argc--, action = 0;
1398 fprintf(stderr, "Invalid number of arguments.\n");
1402 // Concatenate the rest of the command line
1403 strncpy(line, argv[1], sizeof line - 1);
1404 for(int i = 2; i < argc; i++) {
1405 strncat(line, " ", sizeof line - 1 - strlen(line));
1406 strncat(line, argv[i], sizeof line - 1 - strlen(line));
1409 // Liberal parsing into node name, variable name and value.
1415 len = strcspn(line, "\t =");
1417 value += strspn(value, "\t ");
1420 value += strspn(value, "\t ");
1423 variable = strchr(line, '.');
1432 fprintf(stderr, "No variable given.\n");
1436 if(action >= 0 && !*value) {
1437 fprintf(stderr, "No value for variable given.\n");
1441 if(action < -1 && *value)
1444 /* Some simple checks. */
1446 bool warnonremove = false;
1448 for(int i = 0; variables[i].name; i++) {
1449 if(strcasecmp(variables[i].name, variable))
1453 variable = (char *)variables[i].name;
1455 /* Discourage use of obsolete variables. */
1457 if(variables[i].type & VAR_OBSOLETE && action >= 0) {
1459 fprintf(stderr, "Warning: %s is an obsolete variable!\n", variable);
1461 fprintf(stderr, "%s is an obsolete variable! Use --force to use it anyway.\n", variable);
1466 /* Don't put server variables in host config files */
1468 if(node && !(variables[i].type & VAR_HOST) && action >= 0) {
1470 fprintf(stderr, "Warning: %s is not a host configuration variable!\n", variable);
1472 fprintf(stderr, "%s is not a host configuration variable! Use --force to use it anyway.\n", variable);
1477 /* Should this go into our own host config file? */
1479 if(!node && !(variables[i].type & VAR_SERVER)) {
1480 node = get_my_name(true);
1485 /* Change "add" into "set" for variables that do not allow multiple occurences.
1486 Turn on warnings when it seems variables might be removed unintentionally. */
1488 if(action == 1 && !(variables[i].type & VAR_MULTIPLE)) {
1489 warnonremove = true;
1491 } else if(action == 0 && (variables[i].type & VAR_MULTIPLE)) {
1492 warnonremove = true;
1498 if(node && !check_id(node)) {
1499 fprintf(stderr, "Invalid name for node.\n");
1504 if(force || action < 0) {
1505 fprintf(stderr, "Warning: %s is not a known configuration variable!\n", variable);
1507 fprintf(stderr, "%s: is not a known configuration variable! Use --force to use it anyway.\n", variable);
1512 // Open the right configuration file.
1515 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, node);
1517 filename = tinc_conf;
1519 FILE *f = fopen(filename, "r");
1521 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1525 char *tmpfile = NULL;
1529 xasprintf(&tmpfile, "%s.config.tmp", filename);
1530 tf = fopen(tmpfile, "w");
1532 fprintf(stderr, "Could not open temporary file %s: %s\n", tmpfile, strerror(errno));
1538 // Copy the file, making modifications on the fly, unless we are just getting a value.
1542 bool removed = false;
1545 while(fgets(buf1, sizeof buf1, f)) {
1546 buf1[sizeof buf1 - 1] = 0;
1547 strncpy(buf2, buf1, sizeof buf2);
1549 // Parse line in a simple way
1553 len = strcspn(buf2, "\t =");
1554 bvalue = buf2 + len;
1555 bvalue += strspn(bvalue, "\t ");
1556 if(*bvalue == '=') {
1558 bvalue += strspn(bvalue, "\t ");
1564 if(!strcasecmp(buf2, variable)) {
1568 printf("%s\n", bvalue);
1570 } else if(action == -1) {
1571 if(!*value || !strcasecmp(bvalue, value)) {
1576 } else if(action == 0) {
1577 // Warn if "set" was used for variables that can occur multiple times
1578 if(warnonremove && strcasecmp(bvalue, value))
1579 fprintf(stderr, "Warning: removing %s = %s\n", variable, bvalue);
1581 // Already set? Delete the rest...
1585 // Otherwise, replace.
1586 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1587 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1596 // Copy original line...
1597 if(fputs(buf1, tf) < 0) {
1598 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1602 // Add newline if it is missing...
1603 if(*buf1 && buf1[strlen(buf1) - 1] != '\n') {
1604 if(fputc('\n', tf) < 0) {
1605 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1612 // Make sure we read everything...
1613 if(ferror(f) || !feof(f)) {
1614 fprintf(stderr, "Error while reading from configuration file %s: %s\n", filename, strerror(errno));
1619 fprintf(stderr, "Error closing configuration file %s: %s\n", filename, strerror(errno));
1623 // Add new variable if necessary.
1624 if(action > 0 || (action == 0 && !set)) {
1625 if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
1626 fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
1633 fprintf(stderr, "No matching configuration variables found.\n");
1637 // Make sure we wrote everything...
1639 fprintf(stderr, "Error closing temporary file %s: %s\n", tmpfile, strerror(errno));
1643 // Could we find what we had to remove?
1644 if(action < 0 && !removed) {
1646 fprintf(stderr, "No configuration variables deleted.\n");
1650 // Replace the configuration file with the new one
1652 if(remove(filename)) {
1653 fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
1657 if(rename(tmpfile, filename)) {
1658 fprintf(stderr, "Error renaming temporary file %s to configuration file %s: %s\n", tmpfile, filename, strerror(errno));
1662 // Silently try notifying a running tincd of changes.
1663 if(connect_tincd(false))
1664 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1669 bool check_id(const char *name) {
1673 for(int i = 0; i < strlen(name); i++) {
1674 if(!isalnum(name[i]) && name[i] != '_')
1681 static bool try_bind(int port) {
1682 struct addrinfo *ai = NULL;
1683 struct addrinfo hint = {
1684 .ai_flags = AI_PASSIVE,
1685 .ai_family = AF_UNSPEC,
1686 .ai_socktype = SOCK_STREAM,
1687 .ai_protocol = IPPROTO_TCP,
1691 snprintf(portstr, sizeof portstr, "%d", port);
1693 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
1697 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
1700 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
1710 int check_port(char *name) {
1714 fprintf(stderr, "Warning: could not bind to port 655. ");
1716 for(int i = 0; i < 100; i++) {
1717 int port = 0x1000 + (rand() & 0x7fff);
1718 if(try_bind(port)) {
1720 xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
1721 FILE *f = fopen(filename, "a");
1724 fprintf(stderr, "Please change tinc's Port manually.\n");
1728 fprintf(f, "Port = %d\n", port);
1730 fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
1735 fprintf(stderr, "Please change tinc's Port manually.\n");
1739 static int cmd_init(int argc, char *argv[]) {
1740 if(!access(tinc_conf, F_OK)) {
1741 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1746 fprintf(stderr, "Too many arguments!\n");
1748 } else if(argc < 2) {
1751 fprintf(stderr, "Enter the Name you want your tinc node to have: ");
1752 if(!fgets(buf, sizeof buf, stdin)) {
1753 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1756 int len = rstrip(buf);
1758 fprintf(stderr, "No name given!\n");
1763 fprintf(stderr, "No Name given!\n");
1767 name = strdup(argv[1]);
1769 fprintf(stderr, "No Name given!\n");
1774 if(!check_id(name)) {
1775 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
1779 if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
1780 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
1784 if(mkdir(confbase, 0777) && errno != EEXIST) {
1785 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
1789 if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
1790 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
1794 FILE *f = fopen(tinc_conf, "w");
1796 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
1800 fprintf(f, "Name = %s\n", name);
1803 if(!rsa_keygen(2048, false) || !ed25519_keygen(false))
1810 xasprintf(&filename, "%s" SLASH "tinc-up", confbase);
1811 if(access(filename, F_OK)) {
1812 FILE *f = fopenmask(filename, "w", 0777);
1814 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
1817 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");
1826 static int cmd_generate_keys(int argc, char *argv[]) {
1828 fprintf(stderr, "Too many arguments!\n");
1833 name = get_my_name(false);
1835 return !(rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true) && ed25519_keygen(true));
1838 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
1840 fprintf(stderr, "Too many arguments!\n");
1845 name = get_my_name(false);
1847 return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
1850 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
1852 fprintf(stderr, "Too many arguments!\n");
1857 name = get_my_name(false);
1859 return !ed25519_keygen(true);
1862 static int cmd_help(int argc, char *argv[]) {
1867 static int cmd_version(int argc, char *argv[]) {
1869 fprintf(stderr, "Too many arguments!\n");
1877 static int cmd_info(int argc, char *argv[]) {
1879 fprintf(stderr, "Invalid number of arguments.\n");
1883 if(!connect_tincd(true))
1886 return info(fd, argv[1]);
1889 static const char *conffiles[] = {
1900 static int cmd_edit(int argc, char *argv[]) {
1902 fprintf(stderr, "Invalid number of arguments.\n");
1906 char *filename = NULL;
1908 if(strncmp(argv[1], "hosts" SLASH, 6)) {
1909 for(int i = 0; conffiles[i]; i++) {
1910 if(!strcmp(argv[1], conffiles[i])) {
1911 xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]);
1920 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]);
1921 char *dash = strchr(argv[1], '-');
1924 if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
1925 fprintf(stderr, "Invalid configuration filename.\n");
1933 xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
1935 xasprintf(&command, "edit \"%s\"", filename);
1937 int result = system(command);
1941 // Silently try notifying a running tincd of changes.
1942 if(connect_tincd(false))
1943 sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
1948 static int export(const char *name, FILE *out) {
1950 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
1951 FILE *in = fopen(filename, "r");
1953 fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
1957 fprintf(out, "Name = %s\n", name);
1959 while(fgets(buf, sizeof buf, in)) {
1960 if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
1965 fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
1974 static int cmd_export(int argc, char *argv[]) {
1976 fprintf(stderr, "Too many arguments!\n");
1980 char *name = get_my_name(true);
1984 int result = export(name, stdout);
1992 static int cmd_export_all(int argc, char *argv[]) {
1994 fprintf(stderr, "Too many arguments!\n");
1998 DIR *dir = opendir(hosts_dir);
2000 fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
2008 while((ent = readdir(dir))) {
2009 if(!check_id(ent->d_name))
2015 printf("#---------------------------------------------------------------#\n");
2017 result |= export(ent->d_name, stdout);
2026 static int cmd_import(int argc, char *argv[]) {
2028 fprintf(stderr, "Too many arguments!\n");
2037 char *filename = NULL;
2039 bool firstline = true;
2041 while(fgets(buf, sizeof buf, in)) {
2042 if(sscanf(buf, "Name = %s", name) == 1) {
2045 if(!check_id(name)) {
2046 fprintf(stderr, "Invalid Name in input!\n");
2054 xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
2056 if(!force && !access(filename, F_OK)) {
2057 fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
2062 out = fopen(filename, "w");
2064 fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
2070 } else if(firstline) {
2071 fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
2076 if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
2080 if(fputs(buf, out) < 0) {
2081 fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
2091 fprintf(stderr, "Imported %d host configuration files.\n", count);
2094 fprintf(stderr, "No host configuration files imported.\n");
2099 static int cmd_exchange(int argc, char *argv[]) {
2100 return cmd_export(argc, argv) ?: cmd_import(argc, argv);
2103 static int cmd_exchange_all(int argc, char *argv[]) {
2104 return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
2107 static int switch_network(char *name) {
2119 free(unixsocketname);
2120 unixsocketname = NULL;
2126 netname = strcmp(name, ".") ? xstrdup(name) : NULL;
2129 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2130 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2131 xasprintf(&prompt, "%s> ", identname);
2136 static int cmd_network(int argc, char *argv[]) {
2138 fprintf(stderr, "Too many arguments!\n");
2143 return switch_network(argv[1]);
2145 DIR *dir = opendir(confdir);
2147 fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
2152 while((ent = readdir(dir))) {
2153 if(*ent->d_name == '.')
2156 if(!strcmp(ent->d_name, "tinc.conf")) {
2162 xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
2163 if(!access(fname, R_OK))
2164 printf("%s\n", ent->d_name);
2173 static const struct {
2174 const char *command;
2175 int (*function)(int argc, char *argv[]);
2178 {"start", cmd_start},
2180 {"restart", cmd_restart},
2181 {"reload", cmd_reload},
2183 {"purge", cmd_purge},
2184 {"debug", cmd_debug},
2185 {"retry", cmd_retry},
2186 {"connect", cmd_connect},
2187 {"disconnect", cmd_disconnect},
2192 {"config", cmd_config, true},
2193 {"add", cmd_config},
2194 {"del", cmd_config},
2195 {"get", cmd_config},
2196 {"set", cmd_config},
2198 {"generate-keys", cmd_generate_keys},
2199 {"generate-rsa-keys", cmd_generate_rsa_keys},
2200 {"generate-ed25519-keys", cmd_generate_ed25519_keys},
2202 {"version", cmd_version},
2205 {"export", cmd_export},
2206 {"export-all", cmd_export_all},
2207 {"import", cmd_import},
2208 {"exchange", cmd_exchange},
2209 {"exchange-all", cmd_exchange_all},
2210 {"invite", cmd_invite},
2212 {"network", cmd_network},
2216 #ifdef HAVE_READLINE
2217 static char *complete_command(const char *text, int state) {
2225 while(commands[i].command) {
2226 if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
2227 return xstrdup(commands[i].command);
2234 static char *complete_dump(const char *text, int state) {
2235 const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
2244 if(!strncasecmp(matches[i], text, strlen(text)))
2245 return xstrdup(matches[i]);
2252 static char *complete_config(const char *text, int state) {
2260 while(variables[i].name) {
2261 char *dot = strchr(text, '.');
2263 if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
2265 xasprintf(&match, "%.*s.%s", (int)(dot - text), text, variables[i].name);
2269 if(!strncasecmp(variables[i].name, text, strlen(text)))
2270 return xstrdup(variables[i].name);
2278 static char *complete_info(const char *text, int state) {
2282 if(!connect_tincd(false))
2284 // Check the list of nodes
2285 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
2286 sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
2289 while(recvline(fd, line, sizeof line)) {
2291 int n = sscanf(line, "%d %d %s", &code, &req, item);
2301 fprintf(stderr, "Unable to parse dump from tincd, n = %d, i = %d.\n", n, i);
2305 if(!strncmp(item, text, strlen(text)))
2306 return xstrdup(strip_weight(item));
2312 static char *complete_nothing(const char *text, int state) {
2316 static char **completion (const char *text, int start, int end) {
2317 char **matches = NULL;
2320 matches = rl_completion_matches(text, complete_command);
2321 else if(!strncasecmp(rl_line_buffer, "dump ", 5))
2322 matches = rl_completion_matches(text, complete_dump);
2323 else if(!strncasecmp(rl_line_buffer, "add ", 4))
2324 matches = rl_completion_matches(text, complete_config);
2325 else if(!strncasecmp(rl_line_buffer, "del ", 4))
2326 matches = rl_completion_matches(text, complete_config);
2327 else if(!strncasecmp(rl_line_buffer, "get ", 4))
2328 matches = rl_completion_matches(text, complete_config);
2329 else if(!strncasecmp(rl_line_buffer, "set ", 4))
2330 matches = rl_completion_matches(text, complete_config);
2331 else if(!strncasecmp(rl_line_buffer, "info ", 5))
2332 matches = rl_completion_matches(text, complete_info);
2338 static int cmd_shell(int argc, char *argv[]) {
2339 xasprintf(&prompt, "%s> ", identname);
2343 int maxargs = argc + 16;
2344 char **nargv = xmalloc(maxargs * sizeof *nargv);
2346 for(int i = 0; i < argc; i++)
2349 #ifdef HAVE_READLINE
2350 rl_readline_name = "tinc";
2351 rl_completion_entry_function = complete_nothing;
2352 rl_attempted_completion_function = completion;
2353 rl_filename_completion_desired = 0;
2358 #ifdef HAVE_READLINE
2362 rl_basic_word_break_characters = "\t\n ";
2363 line = readline(prompt);
2365 copy = xstrdup(line);
2367 line = fgets(buf, sizeof buf, stdin);
2371 fputs(prompt, stdout);
2373 line = fgets(buf, sizeof buf, stdin);
2379 /* Ignore comments */
2387 char *p = line + strspn(line, " \t\n");
2388 char *next = strtok(p, " \t\n");
2391 if(nargc >= maxargs) {
2392 fprintf(stderr, "next %p '%s', p %p '%s'\n", next, next, p, p);
2395 nargv = xrealloc(nargv, maxargs * sizeof *nargv);
2400 next = strtok(NULL, " \t\n");
2406 if(!strcasecmp(nargv[argc], "exit") || !strcasecmp(nargv[argc], "quit"))
2411 for(int i = 0; commands[i].command; i++) {
2412 if(!strcasecmp(nargv[argc], commands[i].command)) {
2413 result |= commands[i].function(nargc - argc - 1, nargv + argc + 1);
2419 #ifdef HAVE_READLINE
2425 fprintf(stderr, "Unknown command `%s'.\n", nargv[argc]);
2438 int main(int argc, char *argv[]) {
2439 program_name = argv[0];
2443 if(!parse_options(argc, argv))
2447 xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
2448 xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
2461 static struct WSAData wsa_state;
2463 if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
2464 fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
2472 tty = isatty(0) && isatty(1);
2475 return cmd_shell(argc, argv);
2477 for(int i = 0; commands[i].command; i++) {
2478 if(!strcasecmp(argv[optind], commands[i].command))
2479 return commands[i].function(argc - optind, argv + optind);
2482 fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);