Allow dumping a list of outstanding invitations.
[tinc] / src / tincctl.c
index ba53619..e0a6086 100644 (file)
@@ -1,6 +1,6 @@
 /*
     tincctl.c -- Controlling a running tincd
-    Copyright (C) 2007-2014 Guus Sliepen <guus@tinc-vpn.org>
+    Copyright (C) 2007-2015 Guus Sliepen <guus@tinc-vpn.org>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -31,6 +31,7 @@
 #include "control_common.h"
 #include "crypto.h"
 #include "ecdsagen.h"
+#include "fsck.h"
 #include "info.h"
 #include "invitation.h"
 #include "names.h"
@@ -66,7 +67,7 @@ char line[4096];
 static int code;
 static int req;
 static int result;
-static bool force = false;
+bool force = false;
 bool tty = true;
 bool confbasegiven = false;
 bool netnamegiven = false;
@@ -87,7 +88,7 @@ static struct option const long_options[] = {
 
 static void version(void) {
        printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
-                  VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
+                  BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
        printf("Copyright (C) 1998-2014 Ivo Timmermans, Guus Sliepen and others.\n"
                        "See the AUTHORS file for a complete list.\n\n"
                        "tinc comes with ABSOLUTELY NO WARRANTY.  This is free software,\n"
@@ -105,6 +106,7 @@ static void usage(bool status) {
                                "  -c, --config=DIR        Read configuration options from DIR.\n"
                                "  -n, --net=NETNAME       Connect to net NETNAME.\n"
                                "      --pidfile=FILENAME  Read control cookie from FILENAME.\n"
+                               "      --force             Force some commands to work despite warnings.\n"
                                "      --help              Display this help and exit.\n"
                                "      --version           Output version information and exit.\n"
                                "\n"
@@ -132,6 +134,7 @@ static void usage(bool status) {
                                "    subnets                  - all known subnets in the VPN\n"
                                "    connections              - all meta connections with ourself\n"
                                "    [di]graph                - graph of the VPN in dotty format\n"
+                               "    invitations              - outstanding invitations\n"
                                "  info NODE|SUBNET|ADDRESS   Give information about a particular NODE, SUBNET or ADDRESS.\n"
                                "  purge                      Purge unreachable nodes\n"
                                "  debug N                    Set debug level\n"
@@ -144,12 +147,13 @@ static void usage(bool status) {
                                "  log [level]                Dump log output [up to the specified level]\n"
                                "  export                     Export host configuration of local node to standard output\n"
                                "  export-all                 Export all host configuration files to standard output\n"
-                               "  import [--force]           Import host configuration file(s) from standard input\n"
-                               "  exchange [--force]         Same as export followed by import\n"
-                               "  exchange-all [--force]     Same as export-all followed by import\n"
+                               "  import                     Import host configuration file(s) from standard input\n"
+                               "  exchange                   Same as export followed by import\n"
+                               "  exchange-all               Same as export-all followed by import\n"
                                "  invite NODE [...]          Generate an invitation for NODE\n"
-                               "  join INVITATION            Join a VPN using an INVITIATION\n"
+                               "  join INVITATION            Join a VPN using an INVITATION\n"
                                "  network [NETNAME]          List all known networks, or switch to the one named NETNAME.\n"
+                               "  fsck                       Check the configuration files for problems.\n"
                                "\n");
                printf("Report bugs to tinc@tinc-vpn.org.\n");
        }
@@ -940,6 +944,65 @@ static int cmd_reload(int argc, char *argv[]) {
 
 }
 
+static int dump_invitations(void) {
+       char dname[PATH_MAX];
+       snprintf(dname, sizeof dname, "%s" SLASH "invitations", confbase);
+       DIR *dir = opendir(dname);
+       if(!dir) {
+               if(errno == ENOENT) {
+                       fprintf(stderr, "No outstanding invitations.\n");
+                       return 0;
+               }
+
+               fprintf(stderr, "Cannot not read directory %s: %s\n", dname, strerror(errno));
+               return 1;
+       }
+
+       struct dirent *ent;
+       bool found = false;
+
+       while((ent = readdir(dir))) {
+               char buf[MAX_STRING_SIZE];
+               if(b64decode(ent->d_name, buf, 24) != 18)
+                       continue;
+
+               char fname[PATH_MAX];
+               snprintf(fname, sizeof fname, "%s" SLASH "%s", dname, ent->d_name);
+               FILE *f = fopen(fname, "r");
+               if(!f) {
+                       fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno));
+                       fclose(f);
+                       continue;
+               }
+
+               buf[0] = 0;
+               if(!fgets(buf, sizeof buf, f)) {
+                       fprintf(stderr, "Invalid invitation file %s", fname);
+                       fclose(f);
+                       continue;
+               }
+               fclose(f);
+
+               char *eol = buf + strlen(buf);
+               while(strchr("\t \r\n", *--eol))
+                       *eol = 0;
+               if(strncmp(buf, "Name = ", 7) || !check_id(buf + 7)) {
+                       fprintf(stderr, "Invalid invitation file %s", fname);
+                       continue;
+               }
+
+               found = true;
+               printf("%s %s\n", ent->d_name, buf + 7);
+       }
+
+       closedir(dir);
+
+       if(!found)
+               fprintf(stderr, "No outstanding invitations.\n");
+
+       return 0;
+}
+
 static int cmd_dump(int argc, char *argv[]) {
        bool only_reachable = false;
 
@@ -960,6 +1023,9 @@ static int cmd_dump(int argc, char *argv[]) {
                return 1;
        }
 
+       if(!strcasecmp(argv[1], "invitations"))
+               return dump_invitations();
+
        if(!connect_tincd(true))
                return 1;
 
@@ -1364,6 +1430,12 @@ const var_t variables[] = {
        {"ScriptsInterpreter", VAR_SERVER},
        {"StrictSubnets", VAR_SERVER},
        {"TunnelServer", VAR_SERVER},
+       {"UDPDiscovery", VAR_SERVER},
+       {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
+       {"UDPDiscoveryInterval", VAR_SERVER},
+       {"UDPDiscoveryTimeout", VAR_SERVER},
+       {"MTUInfoInterval", VAR_SERVER},
+       {"UDPInfoInterval", VAR_SERVER},
        {"UDPRcvBuf", VAR_SERVER},
        {"UDPSndBuf", VAR_SERVER},
        {"VDEGroup", VAR_SERVER},
@@ -1604,6 +1676,11 @@ static int cmd_config(int argc, char *argv[]) {
                                }
                                set = true;
                                continue;
+                       // Add
+                       } else if(action > 0) {
+                               // Check if we've already seen this variable with the same value
+                               if(!strcasecmp(bvalue, value))
+                                       found = true;
                        }
                }
 
@@ -1636,7 +1713,7 @@ static int cmd_config(int argc, char *argv[]) {
        }
 
        // Add new variable if necessary.
-       if(action > 0 || (action == 0 && !set)) {
+       if((action > 0 && !found)|| (action == 0 && !set)) {
                if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
                        fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
                        return 1;
@@ -2150,7 +2227,6 @@ static int switch_network(char *name) {
        free(netname);
        netname = strcmp(name, ".") ? xstrdup(name) : NULL;
 
-       make_names();
         xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
         xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
        xasprintf(&prompt, "%s> ", identname);
@@ -2195,6 +2271,15 @@ static int cmd_network(int argc, char *argv[]) {
        return 0;
 }
 
+static int cmd_fsck(int argc, char *argv[]) {
+       if(argc > 1) {
+               fprintf(stderr, "Too many arguments!\n");
+               return 1;
+       }
+
+       return fsck(orig_argv[0]);
+}
+
 static const struct {
        const char *command;
        int (*function)(int argc, char *argv[]);
@@ -2205,6 +2290,7 @@ static const struct {
        {"restart", cmd_restart},
        {"reload", cmd_reload},
        {"dump", cmd_dump},
+       {"list", cmd_dump},
        {"purge", cmd_purge},
        {"debug", cmd_debug},
        {"retry", cmd_retry},
@@ -2237,6 +2323,7 @@ static const struct {
        {"invite", cmd_invite},
        {"join", cmd_join},
        {"network", cmd_network},
+       {"fsck", cmd_fsck},
        {NULL, NULL},
 };