+
+ return 0;
+}
+
+static int cmd_top(int argc, char *argv[]) {
+#ifdef HAVE_CURSES
+ if(!connect_tincd())
+ return 1;
+
+ top(fd);
+ return 0;
+#else
+ fprintf(stderr, "This version of tincctl was compiled without support for the curses library.\n");
+ return 1;
+#endif
+}
+
+static int cmd_pcap(int argc, char *argv[]) {
+ if(!connect_tincd())
+ return 1;
+
+ pcap(fd, stdout, argc > 1 ? atoi(argv[1]) : 0);
+ return 0;
+}
+
+static int cmd_log(int argc, char *argv[]) {
+ if(!connect_tincd())
+ return 1;
+
+ logcontrol(fd, stdout, argc > 1 ? atoi(argv[1]) : -1);
+ return 0;
+}
+
+static int cmd_pid(int argc, char *argv[]) {
+ if(!connect_tincd())
+ return 1;
+
+ printf("%d\n", pid);
+ return 0;
+}
+
+static int rstrip(char *value) {
+ int len = strlen(value);
+ while(len && strchr("\t\r\n ", value[len - 1]))
+ value[--len] = 0;
+ return len;
+}
+
+static char *get_my_name() {
+ FILE *f = fopen(tinc_conf, "r");
+ if(!f) {
+ fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
+ return NULL;
+ }
+
+ char buf[4096];
+ char *value;
+ while(fgets(buf, sizeof buf, f)) {
+ int len = strcspn(buf, "\t =");
+ value = buf + len;
+ value += strspn(value, "\t ");
+ if(*value == '=') {
+ value++;
+ value += strspn(value, "\t ");
+ }
+ if(!rstrip(value))
+ continue;
+ buf[len] = 0;
+ if(strcasecmp(buf, "Name"))
+ continue;
+ if(*value) {
+ fclose(f);
+ return strdup(value);
+ }
+ }
+
+ fclose(f);
+ fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
+ return NULL;
+}
+
+#define VAR_SERVER 1 /* Should be in tinc.conf */
+#define VAR_HOST 2 /* Can be in host config file */
+#define VAR_MULTIPLE 4 /* Multiple statements allowed */
+#define VAR_OBSOLETE 8 /* Should not be used anymore */
+
+static struct {
+ const char *name;
+ int type;
+} const variables[] = {
+ /* Server configuration */
+ {"AddressFamily", VAR_SERVER},
+ {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
+ {"BindToInterface", VAR_SERVER},
+ {"Broadcast", VAR_SERVER},
+ {"ConnectTo", VAR_SERVER | VAR_MULTIPLE},
+ {"DecrementTTL", VAR_SERVER},
+ {"Device", VAR_SERVER},
+ {"DeviceType", VAR_SERVER},
+ {"DirectOnly", VAR_SERVER},
+ {"ECDSAPrivateKeyFile", VAR_SERVER},
+ {"ExperimentalProtocol", VAR_SERVER},
+ {"Forwarding", VAR_SERVER},
+ {"GraphDumpFile", VAR_SERVER},
+ {"Hostnames", VAR_SERVER},
+ {"IffOneQueue", VAR_SERVER},
+ {"Interface", VAR_SERVER},
+ {"KeyExpire", VAR_SERVER},
+ {"LocalDiscovery", VAR_SERVER},
+ {"MACExpire", VAR_SERVER},
+ {"MaxTimeout", VAR_SERVER},
+ {"Mode", VAR_SERVER},
+ {"Name", VAR_SERVER},
+ {"PingInterval", VAR_SERVER},
+ {"PingTimeout", VAR_SERVER},
+ {"PriorityInheritance", VAR_SERVER},
+ {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
+ {"PrivateKeyFile", VAR_SERVER},
+ {"ProcessPriority", VAR_SERVER},
+ {"ReplayWindow", VAR_SERVER},
+ {"StrictSubnets", VAR_SERVER},
+ {"TunnelServer", VAR_SERVER},
+ {"UDPRcvBuf", VAR_SERVER},
+ {"UDPSndBuf", VAR_SERVER},
+ /* Host configuration */
+ {"Address", VAR_HOST | VAR_MULTIPLE},
+ {"Cipher", VAR_SERVER | VAR_HOST},
+ {"ClampMSS", VAR_SERVER | VAR_HOST},
+ {"Compression", VAR_SERVER | VAR_HOST},
+ {"Digest", VAR_SERVER | VAR_HOST},
+ {"IndirectData", VAR_SERVER | VAR_HOST},
+ {"MACLength", VAR_SERVER | VAR_HOST},
+ {"PMTU", VAR_SERVER | VAR_HOST},
+ {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
+ {"Port", VAR_HOST},
+ {"PublicKey", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
+ {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
+ {"Subnet", VAR_HOST | VAR_MULTIPLE},
+ {"TCPOnly", VAR_SERVER | VAR_HOST},
+ {NULL, 0}
+};
+
+static int cmd_config(int argc, char *argv[]) {
+ if(argc < 2) {
+ fprintf(stderr, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ int action = 0;
+ if(!strcasecmp(argv[1], "add")) {
+ argv++, argc--, action = 1;
+ } else if(!strcasecmp(argv[1], "del")) {
+ argv++, argc--, action = -1;
+ } else if(!strcasecmp(argv[1], "replace") || !strcasecmp(argv[1], "set") || !strcasecmp(argv[1], "change")) {
+ argv++, argc--, action = 0;
+ }
+
+ if(argc < 2) {
+ fprintf(stderr, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ // Concatenate the rest of the command line
+ strncpy(line, argv[1], sizeof line - 1);
+ for(int i = 2; i < argc; i++) {
+ strncat(line, " ", sizeof line - 1 - strlen(line));
+ strncat(line, argv[i], sizeof line - 1 - strlen(line));
+ }
+
+ // Liberal parsing into node name, variable name and value.
+ char *node = NULL;
+ char *variable;
+ char *value;
+ int len;
+
+ len = strcspn(line, "\t =");
+ value = line + len;
+ value += strspn(value, "\t ");
+ if(*value == '=') {
+ value++;
+ value += strspn(value, "\t ");
+ }
+ line[len] = '\0';
+ variable = strchr(line, '.');
+ if(variable) {
+ node = line;
+ *variable++ = 0;
+ } else {
+ variable = line;
+ }
+
+ if(!*variable) {
+ fprintf(stderr, "No variable given.\n");
+ return 1;