+static int cmd_info(int argc, char *argv[]) {
+ if(argc != 2) {
+ fprintf(stderr, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ if(!connect_tincd(true))
+ return 1;
+
+ return info(fd, argv[1]);
+}
+
+static const char *conffiles[] = {
+ "tinc.conf",
+ "tinc-up",
+ "tinc-down",
+ "subnet-up",
+ "subnet-down",
+ "host-up",
+ "host-down",
+ NULL,
+};
+
+static int cmd_edit(int argc, char *argv[]) {
+ if(argc != 2) {
+ fprintf(stderr, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ char *filename = NULL;
+
+ if(strncmp(argv[1], "hosts" SLASH, 6)) {
+ for(int i = 0; conffiles[i]; i++) {
+ if(!strcmp(argv[1], conffiles[i])) {
+ xasprintf(&filename, "%s" SLASH "%s", confbase, argv[1]);
+ break;
+ }
+ }
+ } else {
+ argv[1] += 6;
+ }
+
+ if(!filename) {
+ xasprintf(&filename, "%s" SLASH "%s", hosts_dir, argv[1]);
+ char *dash = strchr(argv[1], '-');
+ if(dash) {
+ *dash++ = 0;
+ if((strcmp(dash, "up") && strcmp(dash, "down")) || !check_id(argv[1])) {
+ fprintf(stderr, "Invalid configuration filename.\n");
+ return 1;
+ }
+ }
+ }
+
+ char *command;
+#ifndef HAVE_MINGW
+ xasprintf(&command, "\"%s\" \"%s\"", getenv("VISUAL") ?: getenv("EDITOR") ?: "vi", filename);
+#else
+ xasprintf(&command, "edit \"%s\"", filename);
+#endif
+ int result = system(command);
+ if(result)
+ return result;
+
+ // Silently try notifying a running tincd of changes.
+ if(connect_tincd(false))
+ sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
+
+ return 0;
+}
+
+static int export(const char *name, FILE *out) {
+ char *filename;
+ xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
+ FILE *in = fopen(filename, "r");
+ if(!in) {
+ fprintf(stderr, "Could not open configuration file %s: %s\n", filename, strerror(errno));
+ return 1;
+ }
+
+ fprintf(out, "Name = %s\n", name);
+ char buf[4096];
+ while(fgets(buf, sizeof buf, in)) {
+ if(strcspn(buf, "\t =") != 4 || strncasecmp(buf, "Name", 4))
+ fputs(buf, out);
+ }
+
+ if(ferror(in)) {
+ fprintf(stderr, "Error while reading configuration file %s: %s\n", filename, strerror(errno));
+ fclose(in);
+ return 1;
+ }
+
+ fclose(in);
+ return 0;
+}
+
+static int cmd_export(int argc, char *argv[]) {
+ if(argc > 1) {
+ fprintf(stderr, "Too many arguments!\n");
+ return 1;
+ }
+
+ char *name = get_my_name(true);
+ if(!name)
+ return 1;
+
+ int result = export(name, stdout);
+ if(!tty)
+ fclose(stdout);
+
+ free(name);
+ return result;
+}
+
+static int cmd_export_all(int argc, char *argv[]) {
+ if(argc > 1) {
+ fprintf(stderr, "Too many arguments!\n");
+ return 1;
+ }
+
+ DIR *dir = opendir(hosts_dir);
+ if(!dir) {
+ fprintf(stderr, "Could not open host configuration directory %s: %s\n", hosts_dir, strerror(errno));
+ return 1;
+ }
+
+ bool first = true;
+ int result = 0;
+ struct dirent *ent;
+
+ while((ent = readdir(dir))) {
+ if(!check_id(ent->d_name))
+ continue;
+
+ if(first)
+ first = false;
+ else
+ printf("#---------------------------------------------------------------#\n");
+
+ result |= export(ent->d_name, stdout);
+ }
+
+ closedir(dir);
+ if(!tty)
+ fclose(stdout);
+ return result;
+}
+
+static int cmd_import(int argc, char *argv[]) {
+ if(argc > 1) {
+ fprintf(stderr, "Too many arguments!\n");
+ return 1;
+ }
+
+ FILE *in = stdin;
+ FILE *out = NULL;
+
+ char buf[4096];
+ char name[4096];
+ char *filename = NULL;
+ int count = 0;
+ bool firstline = true;
+
+ while(fgets(buf, sizeof buf, in)) {
+ if(sscanf(buf, "Name = %s", name) == 1) {
+ firstline = false;
+
+ if(!check_id(name)) {
+ fprintf(stderr, "Invalid Name in input!\n");
+ return 1;
+ }
+
+ if(out)
+ fclose(out);
+
+ free(filename);
+ xasprintf(&filename, "%s" SLASH "%s", hosts_dir, name);
+
+ if(!force && !access(filename, F_OK)) {
+ fprintf(stderr, "Host configuration file %s already exists, skipping.\n", filename);
+ out = NULL;
+ continue;
+ }
+
+ out = fopen(filename, "w");
+ if(!out) {
+ fprintf(stderr, "Error creating configuration file %s: %s\n", filename, strerror(errno));
+ return 1;
+ }
+
+ count++;
+ continue;
+ } else if(firstline) {
+ fprintf(stderr, "Junk at the beginning of the input, ignoring.\n");
+ firstline = false;
+ }
+
+
+ if(!strcmp(buf, "#---------------------------------------------------------------#\n"))
+ continue;
+
+ if(out) {
+ if(fputs(buf, out) < 0) {
+ fprintf(stderr, "Error writing to host configuration file %s: %s\n", filename, strerror(errno));
+ return 1;
+ }
+ }
+ }
+
+ if(out)
+ fclose(out);
+
+ if(count) {
+ fprintf(stderr, "Imported %d host configuration files.\n", count);
+ return 0;
+ } else {
+ fprintf(stderr, "No host configuration files imported.\n");
+ return 1;
+ }
+}
+
+static int cmd_exchange(int argc, char *argv[]) {
+ return cmd_export(argc, argv) ?: cmd_import(argc, argv);
+}
+
+static int cmd_exchange_all(int argc, char *argv[]) {
+ return cmd_export_all(argc, argv) ?: cmd_import(argc, argv);
+}
+
+static int switch_network(char *name) {
+ if(fd >= 0) {
+ close(fd);
+ fd = -1;
+ }
+
+ free(confbase);
+ confbase = NULL;
+ free(pidfilename);
+ pidfilename = NULL;
+ free(logfilename);
+ logfilename = NULL;
+ free(unixsocketname);
+ unixsocketname = NULL;
+ free(tinc_conf);
+ free(hosts_dir);
+ free(prompt);
+
+ 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);
+
+ return 0;
+}
+
+static int cmd_network(int argc, char *argv[]) {
+ if(argc > 2) {
+ fprintf(stderr, "Too many arguments!\n");
+ return 1;
+ }
+
+ if(argc == 2)
+ return switch_network(argv[1]);
+
+ DIR *dir = opendir(confdir);
+ if(!dir) {
+ fprintf(stderr, "Could not read directory %s: %s\n", confdir, strerror(errno));
+ return 1;
+ }
+
+ struct dirent *ent;
+ while((ent = readdir(dir))) {
+ if(*ent->d_name == '.')
+ continue;
+
+ if(!strcmp(ent->d_name, "tinc.conf")) {
+ printf(".\n");
+ continue;
+ }
+
+ char *fname;
+ xasprintf(&fname, "%s/%s/tinc.conf", confdir, ent->d_name);
+ if(!access(fname, R_OK))
+ printf("%s\n", ent->d_name);
+ free(fname);
+ }
+
+ closedir(dir);
+
+ return 0;
+}
+