Add an easy way to export and import host configuration files.
This commit is contained in:
parent
6319dc9dde
commit
c52c46f871
3 changed files with 158 additions and 0 deletions
|
@ -2060,6 +2060,16 @@ If no @var{value} is given, all configuration variables with the same name will
|
||||||
Start an editor for the given configuration file.
|
Start an editor for the given configuration file.
|
||||||
You do not need to specify the full path to the file.
|
You do not need to specify the full path to the file.
|
||||||
|
|
||||||
|
@item export
|
||||||
|
Export the host configuration file of the local node to standard output.
|
||||||
|
|
||||||
|
@item export-all
|
||||||
|
Export all host configuration files to standard output.
|
||||||
|
|
||||||
|
@item import [--force]
|
||||||
|
Import host configuration file(s) from standard input.
|
||||||
|
Already existing host configuration files are not overwritten unless the option --force is used.
|
||||||
|
|
||||||
@item start [tincd options]
|
@item start [tincd options]
|
||||||
Start @samp{tincd}, optionally with the given extra options.
|
Start @samp{tincd}, optionally with the given extra options.
|
||||||
|
|
||||||
|
@ -2153,6 +2163,7 @@ tincctl -n vpn init foo
|
||||||
tincctl -n vpn config Subnet 192.168.1.0/24
|
tincctl -n vpn config Subnet 192.168.1.0/24
|
||||||
tincctl -n vpn config bar.Address bar.example.com
|
tincctl -n vpn config bar.Address bar.example.com
|
||||||
tincctl -n vpn config ConnectTo bar
|
tincctl -n vpn config ConnectTo bar
|
||||||
|
tincctl -n vpn export | gpg --clearsign | mail -s "My config" vpnmaster@@example.com
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
@c ==================================================================
|
@c ==================================================================
|
||||||
|
|
|
@ -71,6 +71,15 @@ is given, all configuration variables with the same name will be removed.
|
||||||
.It edit Ar filename
|
.It edit Ar filename
|
||||||
Start an editor for the given configuration file.
|
Start an editor for the given configuration file.
|
||||||
You do not need to specify the full path to the file.
|
You do not need to specify the full path to the file.
|
||||||
|
.It export
|
||||||
|
Export the host configuration file of the local node to standard output.
|
||||||
|
.It export-all
|
||||||
|
Export all host configuration files to standard output.
|
||||||
|
.It import Op Fl -force
|
||||||
|
Import host configuration file(s) from standard input.
|
||||||
|
Already existing host configuration files are not overwritten unless the option
|
||||||
|
.Fl -force
|
||||||
|
is used.
|
||||||
.It start Op tincd options
|
.It start Op tincd options
|
||||||
Start
|
Start
|
||||||
.Xr tincd 8 ,
|
.Xr tincd 8 ,
|
||||||
|
@ -172,6 +181,7 @@ tincctl -n vpn init foo
|
||||||
tincctl -n vpn config Subnet 192.168.1.0/24
|
tincctl -n vpn config Subnet 192.168.1.0/24
|
||||||
tincctl -n vpn config bar.Address bar.example.com
|
tincctl -n vpn config bar.Address bar.example.com
|
||||||
tincctl -n vpn config ConnectTo bar
|
tincctl -n vpn config ConnectTo bar
|
||||||
|
tincctl -n vpn export | gpg --clearsign | mail -s "My config" vpnmaster@example.com
|
||||||
.Sh TOP
|
.Sh TOP
|
||||||
The top command connects to a running tinc daemon and repeatedly queries its per-node traffic counters.
|
The top command connects to a running tinc daemon and repeatedly queries its per-node traffic counters.
|
||||||
It displays a list of all the known nodes in the left-most column,
|
It displays a list of all the known nodes in the left-most column,
|
||||||
|
|
137
src/tincctl.c
137
src/tincctl.c
|
@ -56,6 +56,7 @@ static char line[4096];
|
||||||
static int code;
|
static int code;
|
||||||
static int req;
|
static int req;
|
||||||
static int result;
|
static int result;
|
||||||
|
static bool force = false;
|
||||||
|
|
||||||
#ifdef HAVE_MINGW
|
#ifdef HAVE_MINGW
|
||||||
static struct WSAData wsa_state;
|
static struct WSAData wsa_state;
|
||||||
|
@ -75,6 +76,7 @@ static struct option const long_options[] = {
|
||||||
{"chroot", no_argument, NULL, 0},
|
{"chroot", no_argument, NULL, 0},
|
||||||
{"user", required_argument, NULL, 0},
|
{"user", required_argument, NULL, 0},
|
||||||
{"option", required_argument, NULL, 0},
|
{"option", required_argument, NULL, 0},
|
||||||
|
{"force", no_argument, NULL, 6},
|
||||||
{NULL, 0, NULL, 0}
|
{NULL, 0, NULL, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -131,6 +133,9 @@ static void usage(bool status) {
|
||||||
#endif
|
#endif
|
||||||
" pcap [snaplen] Dump traffic in pcap format [up to snaplen bytes per packet]\n"
|
" 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"
|
" 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");
|
"\n");
|
||||||
printf("Report bugs to tinc@tinc-vpn.org.\n");
|
printf("Report bugs to tinc@tinc-vpn.org.\n");
|
||||||
}
|
}
|
||||||
|
@ -165,6 +170,10 @@ static bool parse_options(int argc, char **argv) {
|
||||||
pidfilename = xstrdup(optarg);
|
pidfilename = xstrdup(optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
force = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
usage(true);
|
usage(true);
|
||||||
return false;
|
return false;
|
||||||
|
@ -1363,6 +1372,131 @@ static int cmd_edit(int argc, char *argv[]) {
|
||||||
return 0;
|
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))
|
||||||
|
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 {
|
static const struct {
|
||||||
const char *command;
|
const char *command;
|
||||||
int (*function)(int argc, char *argv[]);
|
int (*function)(int argc, char *argv[]);
|
||||||
|
@ -1390,6 +1524,9 @@ static const struct {
|
||||||
{"version", cmd_version},
|
{"version", cmd_version},
|
||||||
{"info", cmd_info},
|
{"info", cmd_info},
|
||||||
{"edit", cmd_edit},
|
{"edit", cmd_edit},
|
||||||
|
{"export", cmd_export},
|
||||||
|
{"export-all", cmd_export_all},
|
||||||
|
{"import", cmd_import},
|
||||||
{NULL, NULL},
|
{NULL, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue