#include "protocol.h"
#include "control_common.h"
#include "ecdsagen.h"
+#include "info.h"
#include "rsagen.h"
#include "utils.h"
#include "tincctl.h"
static int code;
static int req;
static int result;
+static bool force = false;
#ifdef HAVE_MINGW
static struct WSAData wsa_state;
static struct option const long_options[] = {
{"config", required_argument, NULL, 'c'},
+ {"debug", optional_argument, NULL, 0},
+ {"no-detach", no_argument, NULL, 0},
+ {"mlock", no_argument, NULL, 0},
{"net", required_argument, NULL, 'n'},
{"help", no_argument, NULL, 1},
{"version", no_argument, NULL, 2},
{"pidfile", required_argument, NULL, 5},
+ {"logfile", required_argument, NULL, 0},
+ {"bypass-security", no_argument, NULL, 0},
+ {"chroot", no_argument, NULL, 0},
+ {"user", required_argument, NULL, 0},
+ {"option", required_argument, NULL, 0},
+ {"force", no_argument, NULL, 6},
{NULL, 0, NULL, 0}
};
" [set] VARIABLE VALUE - set VARIABLE to VALUE\n"
" add VARIABLE VALUE - add VARIABLE with the given VALUE\n"
" del VARIABLE [VALUE] - remove VARIABLE [only ones with watching VALUE]\n"
- " start Start tincd.\n"
+ " start [tincd options] Start tincd.\n"
" stop Stop tincd.\n"
" restart Restart tincd.\n"
" reload Partially reload configuration of running tincd.\n"
" subnets - all known subnets in the VPN\n"
" connections - all meta connections with ourself\n"
" graph - graph of the VPN in dotty format\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"
" retry Retry all outgoing connections\n"
#endif
" pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
" 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"
"\n");
printf("Report bugs to tinc@tinc-vpn.org.\n");
}
int r;
int option_index = 0;
- while((r = getopt_long(argc, argv, "c:n:", long_options, &option_index)) != EOF) {
+ while((r = getopt_long(argc, argv, "c:n:Dd::Lo:RU:", long_options, &option_index)) != EOF) {
switch (r) {
case 0: /* long option */
break;
pidfilename = xstrdup(optarg);
break;
+ case 6:
+ force = true;
+ break;
+
case '?':
usage(true);
return false;
return 1;
}
+ if(!connect_tincd())
+ return 1;
+
bool do_graph = false;
if(!strcasecmp(argv[1], "nodes"))
return 1;
}
- if(!connect_tincd())
- return 1;
-
if(do_graph)
printf("digraph {\n");
return 0;
}
+static int cmd_info(int argc, char *argv[]) {
+ if(argc != 2) {
+ fprintf(stderr, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ if(!connect_tincd())
+ return 1;
+
+ return info(fd, argv[1]);
+}
+
static const char *conffiles[] = {
"tinc.conf",
"tinc-up",
return 0;
}
+static int export(const char *name, FILE *out) {
+ char *filename;
+ xasprintf(&filename, "%s/%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));
+ return 1;
+ }
+
+ fclose(in);
+ return 0;
+}
+
+static int cmd_export(int argc, char *argv[]) {
+ char *name = get_my_name();
+ if(!name)
+ return 1;
+
+ return export(name, stdout);
+}
+
+static int cmd_export_all(int argc, char *argv[]) {
+ 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);
+ return result;
+}
+
+static int cmd_import(int argc, char *argv[]) {
+ FILE *in = stdin;
+ FILE *out = NULL;
+
+ char buf[4096];
+ char name[4096];
+ char *filename;
+ int count = 0;
+ bool firstline = true;
+
+ while(fgets(buf, sizeof buf, in)) {
+ if(sscanf(buf, "Name = %s", name) == 1) {
+ if(!check_id(name)) {
+ fprintf(stderr, "Invalid Name in input!\n");
+ return 1;
+ }
+
+ if(out)
+ fclose(out);
+
+ free(filename);
+ xasprintf(&filename, "%s/%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++;
+ firstline = false;
+ 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 const struct {
const char *command;
int (*function)(int argc, char *argv[]);
{"generate-ecdsa-keys", cmd_generate_ecdsa_keys},
{"help", cmd_help},
{"version", cmd_version},
+ {"info", cmd_info},
{"edit", cmd_edit},
+ {"export", cmd_export},
+ {"export-all", cmd_export_all},
+ {"import", cmd_import},
{NULL, NULL},
};