tincctl patches

Scott Lamb slamb at slamb.org
Sat Jul 21 21:21:34 CEST 2007


Here are the tincctl patches I've been working on. They apply to
http://www.tinc-vpn.org/svn/tinc/branches/1.1@1545. I intend to commit
them once the crypto stuff's fixed. Since they're basically done, I'm
emailing them now for review and in case I lose my hard drive or something.

They implement a pretty full set of tincctl operations. A few notes:

* I removed most of the weirder signal handlers - didn't see much use
for them once this was added.

* I put in a binary protocol for sending request/responses. Maybe
overkill, but I wanted something that would convey error status and
message boundaries.

* I also removed the GraphDumpFile configuration option. I think now it
makes more sense to do this sort of thing with a cron job based on
"tincctl -n NET dump graph".

Best regards,
Scott

-- 
Scott Lamb <http://www.slamb.org/>
-------------- next part --------------
>From edfdd938ae878ad3f6a0b5942b1493189e29bf22 Mon Sep 17 00:00:00 2001
From: Scott Lamb <slamb at slamb.org>
Date: Sat, 21 Jul 2007 12:20:54 -0700
Subject: [PATCH] Dump through control socket

Note this removes SIGUSR1, SIGUSR2, and the graph dumping config option.
It seems cleaner to do everything through the control socket.
---
 doc/tinc.conf.5.in   |   12 ----------
 doc/tinc.texi        |   16 --------------
 doc/tincctl.8.in     |    2 +-
 doc/tincd.8.in       |    4 ---
 src/connection.c     |   13 ++++++-----
 src/connection.h     |    2 +-
 src/control.c        |   32 ++++++++++++++++++++++++++++-
 src/control_common.h |    5 ++++
 src/edge.c           |   15 ++++++++-----
 src/edge.h           |    2 +-
 src/graph.c          |   55 +++++++++++--------------------------------------
 src/graph.h          |    2 +-
 src/net.c            |   21 -------------------
 src/node.c           |   11 ++++-----
 src/node.h           |    2 +-
 src/subnet.c         |   10 ++++----
 src/subnet.h         |    2 +-
 src/tincctl.c        |   28 +++++++++++++++++++++++++
 18 files changed, 109 insertions(+), 125 deletions(-)

diff --git a/doc/tinc.conf.5.in b/doc/tinc.conf.5.in
index 978bdc1..83d2541 100644
--- a/doc/tinc.conf.5.in
+++ b/doc/tinc.conf.5.in
@@ -147,18 +147,6 @@ instead of
 .Va Device .
 The info pages of the tinc package contain more information
 about configuring the virtual network device.
-.It Va GraphDumpFile Li = Ar filename Bq experimental
-If this option is present,
-.Nm tinc
-will dump the current network graph to the file
-.Ar filename
-every minute, unless there were no changes to the graph.
-The file is in a format that can be read by graphviz tools.
-If
-.Ar filename
-starts with a pipe symbol |,
-then the rest of the filename is interpreted as a shell command
-that is executed, the graph is then sent to stdin.
 .It Va Hostnames Li = yes | no Pq no
 This option selects whether IP addresses (both real and on the VPN) should
 be resolved. Since DNS lookups are blocking, it might affect tinc's
diff --git a/doc/tinc.texi b/doc/tinc.texi
index 2ea54be..541d2de 100644
--- a/doc/tinc.texi
+++ b/doc/tinc.texi
@@ -885,16 +885,6 @@ Under Windows, use @var{Interface} instead of @var{Device}.
 Note that you can only use one device per daemon.
 See also @ref{Device files}.
 
- at cindex GraphDumpFile
- at item GraphDumpFile = <@var{filename}> [experimental]
-If this option is present,
-tinc will dump the current network graph to the file @var{filename}
-every minute, unless there were no changes to the graph.
-The file is in a format that can be read by graphviz tools.
-If @var{filename} starts with a pipe symbol |,
-then the rest of the filename is interpreted as a shell command
-that is executed, the graph is then sent to stdin.
-
 @cindex Hostnames
 @item Hostnames = <yes|no> (no)
 This option selects whether IP addresses (both real and on the VPN)
@@ -1579,12 +1569,6 @@ New outgoing connections specified in @file{tinc.conf} will be made.
 Temporarily increases debug level to 5.
 Send this signal again to revert to the original level.
 
- at item USR1
-Dumps the connection list to syslog.
-
- at item USR2
-Dumps virtual network device statistics, all known nodes, edges and subnets to syslog.
-
 @item WINCH
 Purges all information remembered about unreachable nodes.
 
diff --git a/doc/tincctl.8.in b/doc/tincctl.8.in
index eeb5031..734bb4f 100644
--- a/doc/tincctl.8.in
+++ b/doc/tincctl.8.in
@@ -78,7 +78,7 @@ Dump a graph of the VPN in
 format.
 .El
 .Sh BUGS
-The "start", "restart", "reload", and "dump" commands are not yet implemented.
+The "start", "restart", and "reload" commands are not yet implemented.
 .Pp
 If you find any bugs, report them to tinc at tinc-vpn.org.
 .Sh SEE ALSO
diff --git a/doc/tincd.8.in b/doc/tincd.8.in
index 37a134e..f136a1b 100644
--- a/doc/tincd.8.in
+++ b/doc/tincd.8.in
@@ -98,10 +98,6 @@ will be made.
 .It INT
 Temporarily increases debug level to 5.
 Send this signal again to revert to the original level.
-.It USR1
-Dumps the connection list to syslog.
-.It USR2
-Dumps virtual network device statistics, all known nodes, edges and subnets to syslog.
 .It WINCH
 Purges all information remembered about unreachable nodes.
 .El
diff --git a/src/connection.c b/src/connection.c
index 21cb6aa..06f51b5 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -106,21 +106,22 @@ void connection_del(connection_t *c) {
 	splay_delete(connection_tree, c);
 }
 
-void dump_connections(void) {
+int dump_connections(struct evbuffer *out) {
 	splay_node_t *node;
 	connection_t *c;
 
 	cp();
 
-	logger(LOG_DEBUG, _("Connections:"));
-
 	for(node = connection_tree->head; node; node = node->next) {
 		c = node->data;
-		logger(LOG_DEBUG, _(" %s at %s options %lx socket %d status %04x"),
-			   c->name, c->hostname, c->options, c->socket, c->status.value);
+		if(evbuffer_add_printf(out,
+							   _(" %s at %s options %lx socket %d status %04x\n"),
+							   c->name, c->hostname, c->options, c->socket,
+							   c->status.value) == -1)
+			return errno;
 	}
 
-	logger(LOG_DEBUG, _("End of connections."));
+	return 0;
 }
 
 bool read_connection_config(connection_t *c) {
diff --git a/src/connection.h b/src/connection.h
index 2426a22..ddff03b 100644
--- a/src/connection.h
+++ b/src/connection.h
@@ -109,7 +109,7 @@ extern connection_t *new_connection(void) __attribute__ ((__malloc__));
 extern void free_connection(connection_t *);
 extern void connection_add(connection_t *);
 extern void connection_del(connection_t *);
-extern void dump_connections(void);
+extern int dump_connections(struct evbuffer *);
 extern bool read_connection_config(connection_t *);
 
 #endif							/* __TINC_CONNECTION_H__ */
diff --git a/src/control.c b/src/control.c
index e2d3d3a..2009c22 100644
--- a/src/control.c
+++ b/src/control.c
@@ -60,11 +60,41 @@ static void handle_control_data(struct bufferevent *event, void *data) {
 	}
 
 	if(req->type == REQ_STOP) {
-		logger(LOG_NOTICE, _("Got stop command"));
+		logger(LOG_NOTICE, _("Got '%s' command"), "stop");
 		event_loopexit(NULL);
 		goto respond;
 	}
 
+	if(req->type == REQ_DUMP_NODES) {
+		logger(LOG_NOTICE, _("Got '%s' command"), "dump nodes");
+		res.res_errno = dump_nodes(res_data);
+		goto respond;
+	}
+
+	if(req->type == REQ_DUMP_EDGES) {
+		logger(LOG_NOTICE, _("Got '%s' command"), "dump edges");
+		res.res_errno = dump_edges(res_data);
+		goto respond;
+	}
+
+	if(req->type == REQ_DUMP_SUBNETS) {
+		logger(LOG_NOTICE, _("Got '%s' command"), "dump subnets");
+		res.res_errno = dump_subnets(res_data);
+		goto respond;
+	}
+
+	if(req->type == REQ_DUMP_CONNECTIONS) {
+		logger(LOG_NOTICE, _("Got '%s' command"), "dump connections");
+		res.res_errno = dump_connections(res_data);
+		goto respond;
+	}
+
+	if(req->type == REQ_DUMP_GRAPH) {
+		logger(LOG_NOTICE, _("Got '%s' command"), "dump graph");
+		res.res_errno = dump_graph(res_data);
+		goto respond;
+	}
+
 	logger(LOG_DEBUG, _("Malformed control command received"));
 	res.res_errno = EINVAL;
 
diff --git a/src/control_common.h b/src/control_common.h
index 87b893f..2ac208c 100644
--- a/src/control_common.h
+++ b/src/control_common.h
@@ -26,6 +26,11 @@ enum request_type {
 	REQ_STOP,
 	REQ_RELOAD,
 	REQ_RESTART,
+	REQ_DUMP_NODES,
+	REQ_DUMP_EDGES,
+	REQ_DUMP_SUBNETS,
+	REQ_DUMP_CONNECTIONS,
+	REQ_DUMP_GRAPH,
 };
 
 #define TINC_CTL_VERSION_CURRENT 0
diff --git a/src/edge.c b/src/edge.c
index 861b945..3b584f3 100644
--- a/src/edge.c
+++ b/src/edge.c
@@ -125,7 +125,7 @@ edge_t *lookup_edge(node_t *from, node_t *to) {
 	return splay_search(from->edge_tree, &v);
 }
 
-void dump_edges(void) {
+int dump_edges(struct evbuffer *out) {
 	splay_node_t *node, *node2;
 	node_t *n;
 	edge_t *e;
@@ -133,18 +133,21 @@ void dump_edges(void) {
 
 	cp();
 
-	logger(LOG_DEBUG, _("Edges:"));
-
 	for(node = node_tree->head; node; node = node->next) {
 		n = node->data;
 		for(node2 = n->edge_tree->head; node2; node2 = node2->next) {
 			e = node2->data;
 			address = sockaddr2hostname(&e->address);
-			logger(LOG_DEBUG, _(" %s to %s at %s options %lx weight %d"),
-				   e->from->name, e->to->name, address, e->options, e->weight);
+			if(evbuffer_add_printf(out,
+								   _(" %s to %s at %s options %lx weight %d\n"),
+								   e->from->name, e->to->name, address,
+								   e->options, e->weight) == -1) {
+				free(address);
+				return errno;
+			}
 			free(address);
 		}
 	}
 
-	logger(LOG_DEBUG, _("End of edges."));
+	return 0;
 }
diff --git a/src/edge.h b/src/edge.h
index 082ea5d..dd3d670 100644
--- a/src/edge.h
+++ b/src/edge.h
@@ -51,6 +51,6 @@ extern void free_edge_tree(splay_tree_t *);
 extern void edge_add(edge_t *);
 extern void edge_del(edge_t *);
 extern edge_t *lookup_edge(struct node_t *, struct node_t *);
-extern void dump_edges(void);
+extern int dump_edges(struct evbuffer *);
 
 #endif							/* __TINC_EDGE_H__ */
diff --git a/src/graph.c b/src/graph.c
index 5bf6361..e6d70af 100644
--- a/src/graph.c
+++ b/src/graph.c
@@ -313,66 +313,37 @@ void sssp_bfs(void) {
    dot -Tpng graph_filename -o image_filename.png -Gconcentrate=true
 */
 
-static void dump_graph(int fd, short events, void *data) {
+int dump_graph(struct evbuffer *out) {
 	splay_node_t *node;
 	node_t *n;
 	edge_t *e;
-	char *filename = NULL, *tmpname = NULL;
-	FILE *file;
-	
-	if(!get_config_string(lookup_config(config_tree, "GraphDumpFile"), &filename))
-		return;
 
-	ifdebug(PROTOCOL) logger(LOG_NOTICE, "Dumping graph");
-	
-	if(filename[0] == '|') {
-		file = popen(filename + 1, "w");
-	} else {
-		asprintf(&tmpname, "%s.new", filename);
-		file = fopen(tmpname, "w");
-	}
-
-	if(!file) {
-		logger(LOG_ERR, "Unable to open graph dump file %s: %s", filename, strerror(errno));
-		free(tmpname);
-		return;
-	}
-
-	fprintf(file, "digraph {\n");
+	if(evbuffer_add_printf(out, "digraph {\n") == -1)
+		return errno;
 	
 	/* dump all nodes first */
 	for(node = node_tree->head; node; node = node->next) {
 		n = node->data;
-		fprintf(file, "	%s [label = \"%s\"];\n", n->name, n->name);
+		if(evbuffer_add_printf(out, "	%s [label = \"%s\"];\n",
+							   n->name, n->name) == -1)
+			return errno;
 	}
 
 	/* now dump all edges */
 	for(node = edge_weight_tree->head; node; node = node->next) {
 		e = node->data;
-		fprintf(file, "	%s -> %s;\n", e->from->name, e->to->name);
+		if(evbuffer_add_printf(out, "	%s -> %s;\n",
+							   e->from->name, e->to->name) == -1)
+			return errno;
 	}
 
-	fprintf(file, "}\n");	
-	
-	if(filename[0] == '|') {
-		pclose(file);
-	} else {
-		fclose(file);
-#ifdef HAVE_MINGW
-		unlink(filename);
-#endif
-		rename(tmpname, filename);
-		free(tmpname);
-	}
+	if(evbuffer_add_printf(out, "}\n") == -1)
+		return errno;
+
+	return 0;
 }
 
 void graph(void) {
-	static struct event ev;
-
 	sssp_bfs();
 	mst_kruskal();
-
-	if(!timeout_initialized(&ev))
-		timeout_set(&ev, dump_graph, NULL);
-	event_add(&ev, &(struct timeval){5, 0});
 }
diff --git a/src/graph.h b/src/graph.h
index 0d5be20..88de9f6 100644
--- a/src/graph.h
+++ b/src/graph.h
@@ -26,6 +26,6 @@
 extern void graph(void);
 extern void mst_kruskal(void);
 extern void sssp_bfs(void);
-extern void dump_graph(void);
+extern int dump_graph(struct evbuffer *);
 
 #endif /* __TINC_GRAPH_H__ */
diff --git a/src/net.c b/src/net.c
index 1b5bb16..3b180ff 100644
--- a/src/net.c
+++ b/src/net.c
@@ -252,19 +252,6 @@ static void sigint_handler(int signal, short events, void *data) {
 	}
 }
 
-static void sigusr1_handler(int signal, short events, void *data) {
-	logger(LOG_NOTICE, _("Got %s signal"), strsignal(signal));
-	dump_connections();
-}
-
-static void sigusr2_handler(int signal, short events, void *data) {
-	logger(LOG_NOTICE, _("Got %s signal"), strsignal(signal));
-	dump_device_stats();
-	dump_nodes();
-	dump_edges();
-	dump_subnets();
-}
-
 static void sigwinch_handler(int signal, short events, void *data) {
 	logger(LOG_NOTICE, _("Got %s signal"), strsignal(signal));
 	purge();
@@ -345,8 +332,6 @@ int main_loop(void) {
 	struct event sigint_event;
 	struct event sigterm_event;
 	struct event sigquit_event;
-	struct event sigusr1_event;
-	struct event sigusr2_event;
 	struct event sigwinch_event;
 	struct event sigalrm_event;
 
@@ -362,10 +347,6 @@ int main_loop(void) {
 	signal_add(&sigterm_event, NULL);
 	signal_set(&sigquit_event, SIGQUIT, sigterm_handler, NULL);
 	signal_add(&sigquit_event, NULL);
-	signal_set(&sigusr1_event, SIGUSR1, sigusr1_handler, NULL);
-	signal_add(&sigusr1_event, NULL);
-	signal_set(&sigusr2_event, SIGUSR2, sigusr2_handler, NULL);
-	signal_add(&sigusr2_event, NULL);
 	signal_set(&sigwinch_event, SIGWINCH, sigwinch_handler, NULL);
 	signal_add(&sigwinch_event, NULL);
 	signal_set(&sigalrm_event, SIGALRM, sigalrm_handler, NULL);
@@ -380,8 +361,6 @@ int main_loop(void) {
 	signal_del(&sigint_event);
 	signal_del(&sigterm_event);
 	signal_del(&sigquit_event);
-	signal_del(&sigusr1_event);
-	signal_del(&sigusr2_event);
 	signal_del(&sigwinch_event);
 	signal_del(&sigalrm_event);
 	event_del(&timeout_event);
diff --git a/src/node.c b/src/node.c
index cbafe71..0960ca7 100644
--- a/src/node.c
+++ b/src/node.c
@@ -160,22 +160,21 @@ node_t *lookup_node_udp(const sockaddr_t *sa) {
 	return splay_search(node_udp_tree, &n);
 }
 
-void dump_nodes(void) {
+int dump_nodes(struct evbuffer *out) {
 	splay_node_t *node;
 	node_t *n;
 
 	cp();
 
-	logger(LOG_DEBUG, _("Nodes:"));
-
 	for(node = node_tree->head; node; node = node->next) {
 		n = node->data;
-		logger(LOG_DEBUG, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s pmtu %d (min %d max %d)"),
+		if(evbuffer_add_printf(out, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s pmtu %d (min %d max %d)\n"),
 			   n->name, n->hostname, n->cipher ? n->cipher->nid : 0,
 			   n->digest ? n->digest->type : 0, n->maclength, n->compression,
 			   n->options, *(uint32_t *)&n->status, n->nexthop ? n->nexthop->name : "-",
-			   n->via ? n->via->name : "-", n->mtu, n->minmtu, n->maxmtu);
+			   n->via ? n->via->name : "-", n->mtu, n->minmtu, n->maxmtu) == -1)
+			return errno;
 	}
 
-	logger(LOG_DEBUG, _("End of nodes."));
+	return 0;
 }
diff --git a/src/node.h b/src/node.h
index 83af5e3..0476cdb 100644
--- a/src/node.h
+++ b/src/node.h
@@ -94,6 +94,6 @@ extern void node_add(node_t *);
 extern void node_del(node_t *);
 extern node_t *lookup_node(char *);
 extern node_t *lookup_node_udp(const sockaddr_t *);
-extern void dump_nodes(void);
+extern int dump_nodes(struct evbuffer *);
 
 #endif							/* __TINC_NODE_H__ */
diff --git a/src/subnet.c b/src/subnet.c
index bbf4edd..bc92e18 100644
--- a/src/subnet.c
+++ b/src/subnet.c
@@ -438,7 +438,7 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
 	}
 }
 
-void dump_subnets(void)
+int dump_subnets(struct evbuffer *out)
 {
 	char netstr[MAXNETSTR];
 	subnet_t *subnet;
@@ -446,14 +446,14 @@ void dump_subnets(void)
 
 	cp();
 
-	logger(LOG_DEBUG, _("Subnet list:"));
-
 	for(node = subnet_tree->head; node; node = node->next) {
 		subnet = node->data;
 		if(!net2str(netstr, sizeof netstr, subnet))
 			continue;
-		logger(LOG_DEBUG, _(" %s owner %s"), netstr, subnet->owner->name);
+		if(evbuffer_add_printf(out, _(" %s owner %s"),
+							   netstr, subnet->owner->name) == -1)
+			return errno;
 	}
 
-	logger(LOG_DEBUG, _("End of subnet list."));
+	return 0;
 }
diff --git a/src/subnet.h b/src/subnet.h
index f73aaf9..48eec52 100644
--- a/src/subnet.h
+++ b/src/subnet.h
@@ -81,6 +81,6 @@ extern subnet_t *lookup_subnet(const struct node_t *, const subnet_t *);
 extern subnet_t *lookup_subnet_mac(const mac_t *);
 extern subnet_t *lookup_subnet_ipv4(const ipv4_t *);
 extern subnet_t *lookup_subnet_ipv6(const ipv6_t *);
-extern void dump_subnets(void);
+extern int dump_subnets(struct evbuffer *);
 
 #endif							/* __TINC_SUBNET_H__ */
diff --git a/src/tincctl.c b/src/tincctl.c
index 8a9c883..2e404d0 100644
--- a/src/tincctl.c
+++ b/src/tincctl.c
@@ -538,6 +538,34 @@ int main(int argc, char *argv[], char *envp[]) {
 		return send_ctl_request_cooked(fd, REQ_RESTART, NULL, 0) != -1;
 	}
 
+	if(!strcasecmp(argv[optind], "dump")) {
+		if (argc < optind + 2) {
+			fprintf(stderr, _("Not enough arguments.\n"));
+			usage(true);
+			return 1;
+		}
+
+		if(!strcasecmp(argv[optind+1], "nodes")) {
+			return send_ctl_request_cooked(fd, REQ_DUMP_NODES, NULL, 0) != -1;
+		}
+
+		if(!strcasecmp(argv[optind+1], "edges")) {
+			return send_ctl_request_cooked(fd, REQ_DUMP_EDGES, NULL, 0) != -1;
+		}
+
+		if(!strcasecmp(argv[optind+1], "subnets")) {
+			return send_ctl_request_cooked(fd, REQ_DUMP_SUBNETS, NULL, 0) != -1;
+		}
+
+		if(!strcasecmp(argv[optind+1], "connections")) {
+			return send_ctl_request_cooked(fd, REQ_DUMP_CONNECTIONS, NULL, 0) != -1;
+		}
+
+		if(!strcasecmp(argv[optind+1], "graph")) {
+			return send_ctl_request_cooked(fd, REQ_DUMP_GRAPH, NULL, 0) != -1;
+		}
+	}
+
 	fprintf(stderr, _("Unknown command `%s'.\n"), argv[optind]);
 	usage(true);
 	
-- 
1.5.2.2.238.g7cbf2f2-dirty

-------------- next part --------------
>From 5f817642ddaff916cc93aba26cf7bdd089168177 Mon Sep 17 00:00:00 2001
From: Scott Lamb <slamb at slamb.org>
Date: Sat, 21 Jul 2007 12:20:54 -0700
Subject: [PATCH] Avoid Linux-only credential-based pid passing

In particular, *BSD's closest equivalent (LOCAL_PEERCRED / struct xucred)
does not support pids.
---
 src/control.c        |    1 +
 src/control_common.h |    1 +
 src/tincctl.c        |   10 +---------
 3 files changed, 3 insertions(+), 9 deletions(-)

diff --git a/src/control.c b/src/control.c
index d74f7e4..e2d3d3a 100644
--- a/src/control.c
+++ b/src/control.c
@@ -120,6 +120,7 @@ static void handle_new_control_socket(int fd, short events, void *data) {
 
 	memset(&greeting, 0, sizeof greeting);
 	greeting.version = TINC_CTL_VERSION_CURRENT;
+	greeting.pid = getpid();
 	if(bufferevent_write(ev, &greeting, sizeof greeting) == -1) {
 		logger(LOG_ERR,
 			   _("Cannot send greeting for new control connection: %s"),
diff --git a/src/control_common.h b/src/control_common.h
index 3fa034e..87b893f 100644
--- a/src/control_common.h
+++ b/src/control_common.h
@@ -33,6 +33,7 @@ enum request_type {
 /* This greeting is sent by the server on socket open. */
 typedef struct tinc_ctl_greeting_t {
 	int version;
+	pid_t pid;
 } tinc_ctl_greeting_t;
 
 /* A single request or response header. */
diff --git a/src/tincctl.c b/src/tincctl.c
index 3944fa2..8a9c883 100644
--- a/src/tincctl.c
+++ b/src/tincctl.c
@@ -521,16 +521,8 @@ int main(int argc, char *argv[], char *envp[]) {
 		return 1;
 	}
 
-	struct ucred cred;
-	socklen_t credlen = sizeof cred;
-
-	if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &credlen) < 0) {
-		fprintf(stderr, _("Could not obtain PID: %s\n"), strerror(errno));
-		return 1;
-	}
-
 	if(!strcasecmp(argv[optind], "pid")) {
-		printf("%d\n", cred.pid);
+		printf("%d\n", greeting.pid);
 		return 0;
 	}
 
-- 
1.5.2.2.238.g7cbf2f2-dirty

-------------- next part --------------
>From 44a6cef28c3337992d8d3badfbb032264e3b7ef4 Mon Sep 17 00:00:00 2001
From: Scott Lamb <slamb at slamb.org>
Date: Sat, 21 Jul 2007 12:20:22 -0700
Subject: [PATCH] Fancier protocol for control socket

* pass error status back
* pass message boundaries
---
 src/control.c        |   62 +++++++++++++++++++++--
 src/control_common.h |   46 +++++++++++++++++
 src/tincctl.c        |  135 +++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 230 insertions(+), 13 deletions(-)
 create mode 100644 src/control_common.h

diff --git a/src/control.c b/src/control.c
index b3cce92..d74f7e4 100644
--- a/src/control.c
+++ b/src/control.c
@@ -24,6 +24,7 @@
 #include "system.h"
 #include "conf.h"
 #include "control.h"
+#include "control_common.h"
 #include "logger.h"
 #include "xalloc.h"
 
@@ -33,17 +34,56 @@ static splay_tree_t *control_socket_tree;
 extern char *controlsocketname;
 
 static void handle_control_data(struct bufferevent *event, void *data) {
-	char *line = evbuffer_readline(event->input);
-	if(!line)
+	tinc_ctl_request_t *req;
+	size_t size;
+	tinc_ctl_request_t res;
+	struct evbuffer *res_data = NULL;
+
+	if(EVBUFFER_LENGTH(event->input) < sizeof(tinc_ctl_request_t))
 		return;
-	
-	if(!strcasecmp(line, "stop")) {
+
+	req = (tinc_ctl_request_t*) EVBUFFER_DATA(event->input);
+	if(EVBUFFER_LENGTH(event->input) < req->length)
+		return;
+
+	if(req->length < sizeof(tinc_ctl_request_t))
+		goto failure;
+
+	memset(&res, 0, sizeof res);
+	res.type = req->type;
+	res.id = req->id;
+
+	res_data = evbuffer_new();
+	if (res_data == NULL) {
+		res.res_errno = ENOMEM;
+		goto respond;
+	}
+
+	if(req->type == REQ_STOP) {
 		logger(LOG_NOTICE, _("Got stop command"));
 		event_loopexit(NULL);
-		return;
+		goto respond;
 	}
 
 	logger(LOG_DEBUG, _("Malformed control command received"));
+	res.res_errno = EINVAL;
+
+respond:
+	res.length = (sizeof res)
+				 + ((res_data == NULL) ? 0 : EVBUFFER_LENGTH(res_data));
+	evbuffer_drain(event->input, req->length);
+	if(bufferevent_write(event, &res, sizeof res) == -1)
+		goto failure;
+	if(res_data != NULL) {
+		if(bufferevent_write_buffer(event, res_data) == -1)
+			goto failure;
+		evbuffer_free(res_data);
+	}
+	return;
+
+failure:
+	logger(LOG_INFO, _("Closing control socket on error"));
+	evbuffer_free(res_data);
 	close(event->ev_read.ev_fd);
 	splay_delete(control_socket_tree, event);
 }
@@ -61,6 +101,7 @@ static void handle_control_error(struct bufferevent *event, short what, void *da
 static void handle_new_control_socket(int fd, short events, void *data) {
 	int newfd;
 	struct bufferevent *ev;
+	tinc_ctl_greeting_t greeting;
 
 	newfd = accept(fd, NULL, NULL);
 
@@ -77,6 +118,17 @@ static void handle_new_control_socket(int fd, short events, void *data) {
 		return;
 	}
 
+	memset(&greeting, 0, sizeof greeting);
+	greeting.version = TINC_CTL_VERSION_CURRENT;
+	if(bufferevent_write(ev, &greeting, sizeof greeting) == -1) {
+		logger(LOG_ERR,
+			   _("Cannot send greeting for new control connection: %s"),
+			   strerror(errno));
+		bufferevent_free(ev);
+		close(newfd);
+		return;
+	}
+
 	bufferevent_enable(ev, EV_READ);
 	splay_insert(control_socket_tree, ev);
 
diff --git a/src/control_common.h b/src/control_common.h
new file mode 100644
index 0000000..3fa034e
--- /dev/null
+++ b/src/control_common.h
@@ -0,0 +1,46 @@
+/*
+    control_protocol.h -- control socket protocol.
+    Copyright (C) 2007 Scott Lamb <slamb at slamb.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
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    $Id$
+*/
+
+#ifndef __TINC_CONTROL_PROTOCOL_H__
+#define __TINC_CONTROL_PROTOCOL_H__
+
+enum request_type {
+	REQ_STOP,
+	REQ_RELOAD,
+	REQ_RESTART,
+};
+
+#define TINC_CTL_VERSION_CURRENT 0
+
+/* This greeting is sent by the server on socket open. */
+typedef struct tinc_ctl_greeting_t {
+	int version;
+} tinc_ctl_greeting_t;
+
+/* A single request or response header. */
+typedef struct tinc_ctl_request_t {
+	size_t length; /* total length, including the header */
+	enum request_type type;
+	int id;
+	int res_errno; /* used only for responses */
+} tinc_ctl_request_t;
+
+#endif
diff --git a/src/tincctl.c b/src/tincctl.c
index 5d65f3a..3944fa2 100644
--- a/src/tincctl.c
+++ b/src/tincctl.c
@@ -30,8 +30,9 @@
 
 #include <getopt.h>
 
-#include "protocol.h"
 #include "xalloc.h"
+#include "protocol.h"
+#include "control_common.h"
 
 /* The name this program was run with. */
 char *program_name = NULL;
@@ -327,9 +328,118 @@ static void make_names(void) {
 	}
 }
 
+static int fullread(int fd, void *data, size_t datalen) {
+	int rv, len = 0;
+
+	while (len < datalen) {
+		rv = read(fd, data + len, datalen - len);
+		if(rv == -1 && errno == EINTR)
+			continue;
+		else if (rv == -1)
+			return rv;
+		else if (rv == 0) {
+			errno = ENODATA;
+			return -1;
+		}
+		len += rv;
+	}
+	return 0;
+}
+
+/*
+   Send a request (raw)
+*/
+static int send_ctl_request(int fd, enum request_type type,
+						   void const *outdata, size_t outdatalen,
+						   int *res_errno_p, void **indata_p,
+						   size_t *indatalen_p) {
+	tinc_ctl_request_t req;
+	int rv;
+	struct iovec vector[2] = {
+		{&req, sizeof(req)},
+		{(void*) outdata, outdatalen}
+	};
+	void *indata;
+
+	if(res_errno_p == NULL)
+		return -1;
+
+	memset(&req, 0, sizeof req);
+	req.length = sizeof req + outdatalen;
+	req.type = type;
+	req.res_errno = 0;
+
+	while((rv = writev(fd, vector, 2)) == -1 && errno == EINTR) ;
+	if(rv != req.length)
+		return -1;
+
+	if(fullread(fd, &req, sizeof req) == -1)
+		return -1;
+
+	if(req.length < sizeof req) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if(req.length > sizeof req) {
+		if (indata_p == NULL) {
+			errno = EINVAL;
+			return -1;
+		}
+
+		indata = xmalloc(req.length - sizeof req);
+
+		if(fullread(fd, indata, req.length - sizeof req) == -1) {
+			free(indata);
+			return -1;
+		}
+
+		*indata_p = indata;
+		if(indatalen_p != NULL)
+			*indatalen_p = req.length - sizeof req;
+	}
+
+	*res_errno_p = req.res_errno;
+
+	return 0;
+}
+
+/*
+   Send a request (with printfs)
+*/
+static int send_ctl_request_cooked(int fd, enum request_type type,
+								   void const *outdata, size_t outdatalen)
+{
+	int res_errno = -1;
+	char *buf = NULL;
+	size_t buflen = 0;
+
+	if(send_ctl_request(fd, type, outdata, outdatalen, &res_errno,
+						(void**) &buf, &buflen)) {
+		fprintf(stderr, _("Error sending request: %s\n"), strerror(errno));
+		return -1;
+	}
+
+	if(buf != NULL) {
+		printf("%*s", buflen, buf);
+		free(buf);
+	}
+
+	if(res_errno != 0) {
+		fprintf(stderr, _("Server reported error: %s\n"), strerror(res_errno));
+		return -1;
+	}
+
+	return 0;
+}
+
 int main(int argc, char *argv[], char *envp[]) {
-	int fd;
 	struct sockaddr_un addr;
+	int fd;
+	int len;
+	tinc_ctl_greeting_t greeting;
+	tinc_ctl_request_t req;
+
 	program_name = argv[0];
 
 	setlocale(LC_ALL, "");
@@ -399,6 +509,18 @@ int main(int argc, char *argv[], char *envp[]) {
 		return 1;
 	}
 
+	if(fullread(fd, &greeting, sizeof greeting) == -1) {
+		fprintf(stderr, _("Cannot read greeting from control socket: %s\n"),
+				strerror(errno));
+		return 1;
+	}
+
+	if(greeting.version != TINC_CTL_VERSION_CURRENT) {
+		fprintf(stderr, _("Version mismatch: server %d, client %d\n"),
+				greeting.version, TINC_CTL_VERSION_CURRENT);
+		return 1;
+	}
+
 	struct ucred cred;
 	socklen_t credlen = sizeof cred;
 
@@ -413,18 +535,15 @@ int main(int argc, char *argv[], char *envp[]) {
 	}
 
 	if(!strcasecmp(argv[optind], "stop")) {
-		write(fd, "stop\n", 5);
-		return 0;
+		return send_ctl_request_cooked(fd, REQ_STOP, NULL, 0) != -1;
 	}
 
 	if(!strcasecmp(argv[optind], "reload")) {
-		write(fd, "reload\n", 7);
-		return 0;
+		return send_ctl_request_cooked(fd, REQ_RELOAD, NULL, 0) != -1;
 	}
 	
 	if(!strcasecmp(argv[optind], "restart")) {
-		write(fd, "restart\n", 8);
-		return 0;
+		return send_ctl_request_cooked(fd, REQ_RESTART, NULL, 0) != -1;
 	}
 
 	fprintf(stderr, _("Unknown command `%s'.\n"), argv[optind]);
-- 
1.5.2.2.238.g7cbf2f2-dirty

-------------- next part --------------
>From f8216cdf8a3cfcf9a9f1f9e5835f816cc7d50aad Mon Sep 17 00:00:00 2001
From: Scott Lamb <slamb at slamb.org>
Date: Sat, 21 Jul 2007 12:20:54 -0700
Subject: [PATCH] Purge through the control socket
---
 doc/tinc.texi        |    6 +++---
 doc/tincctl.8.in     |    2 ++
 doc/tincd.8.in       |    2 --
 src/control.c        |    6 ++++++
 src/control_common.h |    1 +
 src/net.c            |   11 +----------
 src/net.h            |    2 +-
 src/tincctl.c        |    5 +++++
 8 files changed, 19 insertions(+), 16 deletions(-)

diff --git a/doc/tinc.texi b/doc/tinc.texi
index 541d2de..e953472 100644
--- a/doc/tinc.texi
+++ b/doc/tinc.texi
@@ -1569,9 +1569,6 @@ New outgoing connections specified in @file{tinc.conf} will be made.
 Temporarily increases debug level to 5.
 Send this signal again to revert to the original level.
 
- at item WINCH
-Purges all information remembered about unreachable nodes.
-
 @end table
 
 @c ==================================================================
@@ -1854,6 +1851,9 @@ Dump a list of all meta connections with ourself.
 @item dump graph
 Dump a graph of the VPN in dotty format.
 
+ at item purge
+Purges all information remembered about unreachable nodes.
+
 @end table
 
 
diff --git a/doc/tincctl.8.in b/doc/tincctl.8.in
index 734bb4f..40bd136 100644
--- a/doc/tincctl.8.in
+++ b/doc/tincctl.8.in
@@ -76,6 +76,8 @@ Dump a list of all meta connections with ourself.
 Dump a graph of the VPN in
 .Xr dotty 1
 format.
+.It purge
+Purges all information remembered about unreachable nodes.
 .El
 .Sh BUGS
 The "start", "restart", and "reload" commands are not yet implemented.
diff --git a/doc/tincd.8.in b/doc/tincd.8.in
index f136a1b..96c773f 100644
--- a/doc/tincd.8.in
+++ b/doc/tincd.8.in
@@ -98,8 +98,6 @@ will be made.
 .It INT
 Temporarily increases debug level to 5.
 Send this signal again to revert to the original level.
-.It WINCH
-Purges all information remembered about unreachable nodes.
 .El
 .Sh DEBUG LEVELS
 The tinc daemon can send a lot of messages to the syslog.
diff --git a/src/control.c b/src/control.c
index 2009c22..a78f78a 100644
--- a/src/control.c
+++ b/src/control.c
@@ -95,6 +95,12 @@ static void handle_control_data(struct bufferevent *event, void *data) {
 		goto respond;
 	}
 
+	if(req->type == REQ_PURGE) {
+		logger(LOG_NOTICE, _("Got '%s' command"), "purge");
+		purge();
+		goto respond;
+	}
+
 	logger(LOG_DEBUG, _("Malformed control command received"));
 	res.res_errno = EINVAL;
 
diff --git a/src/control_common.h b/src/control_common.h
index 2ac208c..cbb8aa9 100644
--- a/src/control_common.h
+++ b/src/control_common.h
@@ -31,6 +31,7 @@ enum request_type {
 	REQ_DUMP_SUBNETS,
 	REQ_DUMP_CONNECTIONS,
 	REQ_DUMP_GRAPH,
+	REQ_PURGE,
 };
 
 #define TINC_CTL_VERSION_CURRENT 0
diff --git a/src/net.c b/src/net.c
index 3b180ff..d2b3134 100644
--- a/src/net.c
+++ b/src/net.c
@@ -41,7 +41,7 @@
 
 /* Purge edges and subnets of unreachable nodes. Use carefully. */
 
-static void purge(void) {
+void purge(void) {
 	splay_node_t *nnode, *nnext, *enode, *enext, *snode, *snext;
 	node_t *n;
 	edge_t *e;
@@ -252,11 +252,6 @@ static void sigint_handler(int signal, short events, void *data) {
 	}
 }
 
-static void sigwinch_handler(int signal, short events, void *data) {
-	logger(LOG_NOTICE, _("Got %s signal"), strsignal(signal));
-	purge();
-}
-
 static void sighup_handler(int signal, short events, void *data) {
 	connection_t *c;
 	splay_node_t *node;
@@ -332,7 +327,6 @@ int main_loop(void) {
 	struct event sigint_event;
 	struct event sigterm_event;
 	struct event sigquit_event;
-	struct event sigwinch_event;
 	struct event sigalrm_event;
 
 	cp();
@@ -347,8 +341,6 @@ int main_loop(void) {
 	signal_add(&sigterm_event, NULL);
 	signal_set(&sigquit_event, SIGQUIT, sigterm_handler, NULL);
 	signal_add(&sigquit_event, NULL);
-	signal_set(&sigwinch_event, SIGWINCH, sigwinch_handler, NULL);
-	signal_add(&sigwinch_event, NULL);
 	signal_set(&sigalrm_event, SIGALRM, sigalrm_handler, NULL);
 	signal_add(&sigalrm_event, NULL);
 
@@ -361,7 +353,6 @@ int main_loop(void) {
 	signal_del(&sigint_event);
 	signal_del(&sigterm_event);
 	signal_del(&sigquit_event);
-	signal_del(&sigwinch_event);
 	signal_del(&sigalrm_event);
 	event_del(&timeout_event);
 
diff --git a/src/net.h b/src/net.h
index 943b7e6..fd14c37 100644
--- a/src/net.h
+++ b/src/net.h
@@ -126,7 +126,6 @@ extern listen_socket_t listen_socket[MAXSOCKETS];
 extern int listen_sockets;
 extern int keylifetime;
 extern bool do_prune;
-extern bool do_purge;
 extern char *myport;
 extern EVP_CIPHER_CTX packet_ctx;
 
@@ -156,6 +155,7 @@ extern void send_mtu_probe(struct node_t *);
 extern void handle_device_data(int, short, void *);
 extern void handle_meta_connection_data(int, short, void *);
 extern void regenerate_key();
+extern void purge(void);
 
 #ifndef HAVE_MINGW
 #define closesocket(s) close(s)
diff --git a/src/tincctl.c b/src/tincctl.c
index 2e404d0..96f2ae2 100644
--- a/src/tincctl.c
+++ b/src/tincctl.c
@@ -89,6 +89,7 @@ static void usage(bool status) {
 				"    subnets                  - all known subnets in the VPN\n"
 				"    connections              - all meta connections with ourself\n"
 				"    graph                    - graph of the VPN in dotty format\n"
+				"    purge                    Purge unreachable nodes\n"
 				"\n"));
 		printf(_("Report bugs to tinc at tinc-vpn.org.\n"));
 	}
@@ -566,6 +567,10 @@ int main(int argc, char *argv[], char *envp[]) {
 		}
 	}
 
+	if(!strcasecmp(argv[optind], "purge")) {
+		return send_ctl_request_cooked(fd, REQ_PURGE, NULL, 0) != -1;
+	}
+
 	fprintf(stderr, _("Unknown command `%s'.\n"), argv[optind]);
 	usage(true);
 	
-- 
1.5.2.2.238.g7cbf2f2-dirty

-------------- next part --------------
>From d53fc7d4f6715a02427d7007a6445559fa27356e Mon Sep 17 00:00:00 2001
From: Scott Lamb <slamb at slamb.org>
Date: Sat, 21 Jul 2007 12:20:54 -0700
Subject: [PATCH] Retry connections through control socket

---
 doc/tinc.texi        |   14 +++++++-------
 doc/tincctl.8.in     |   12 ++++++++++++
 doc/tincd.8.in       |   12 ------------
 src/control.c        |    6 ++++++
 src/control_common.h |    1 +
 src/net.c            |    8 +-------
 src/net.h            |    1 +
 src/tincctl.c        |    5 +++++
 8 files changed, 33 insertions(+), 26 deletions(-)

diff --git a/doc/tinc.texi b/doc/tinc.texi
index b3f570c..9baf177 100644
--- a/doc/tinc.texi
+++ b/doc/tinc.texi
@@ -1553,13 +1553,6 @@ You can also send the following signals to a running tincd process:
 @c from the manpage
 @table @samp
 
- at item ALRM
-Forces tinc to try to connect to all uplinks immediately.
-Usually tinc attempts to do this itself,
-but increases the time it waits between the attempts each time it failed,
-and if tinc didn't succeed to connect to an uplink the first time after it started,
-it defaults to the maximum time of 15 minutes.
-
 @item HUP
 Partially rereads configuration files.
 Connections to hosts whose host config file are removed are closed.
@@ -1853,6 +1846,13 @@ Purges all information remembered about unreachable nodes.
 @item debug @var{level}
 Sets debug level to @var{level}.
 
+ at item retry
+Forces tinc to try to connect to all uplinks immediately.
+Usually tinc attempts to do this itself,
+but increases the time it waits between the attempts each time it failed,
+and if tinc didn't succeed to connect to an uplink the first time after it started,
+it defaults to the maximum time of 15 minutes.
+
 @end table
 
 
diff --git a/doc/tincctl.8.in b/doc/tincctl.8.in
index 623c908..899b066 100644
--- a/doc/tincctl.8.in
+++ b/doc/tincctl.8.in
@@ -81,6 +81,18 @@ Purges all information remembered about unreachable nodes.
 .It debug Ar N
 Sets debug level to
 .Ar N .
+.It retry
+Forces
+.Xr tincd 8
+to try to connect to all uplinks immediately.
+Usually
+.Xr tincd 8
+attempts to do this itself,
+but increases the time it waits between the attempts each time it failed,
+and if
+.Xr tincd 8
+didn't succeed to connect to an uplink the first time after it started,
+it defaults to the maximum time of 15 minutes.
 .El
 .Sh BUGS
 The "start", "restart", and "reload" commands are not yet implemented.
diff --git a/doc/tincd.8.in b/doc/tincd.8.in
index b3142e8..e4bbaeb 100644
--- a/doc/tincd.8.in
+++ b/doc/tincd.8.in
@@ -77,18 +77,6 @@ Output version information and exit.
 .El
 .Sh SIGNALS
 .Bl -tag -width indent
-.It ALRM
-Forces
-.Nm
-to try to connect to all uplinks immediately.
-Usually
-.Nm
-attempts to do this itself,
-but increases the time it waits between the attempts each time it failed,
-and if
-.Nm
-didn't succeed to connect to an uplink the first time after it started,
-it defaults to the maximum time of 15 minutes.
 .It HUP
 Partially rereads configuration files.
 Connections to hosts whose host config file are removed are closed.
diff --git a/src/control.c b/src/control.c
index 775bb98..45ef51e 100644
--- a/src/control.c
+++ b/src/control.c
@@ -120,6 +120,12 @@ static void handle_control_data(struct bufferevent *event, void *data) {
 		goto respond;
 	}
 
+	if(req->type == REQ_RETRY) {
+		logger(LOG_NOTICE, _("Got '%s' command"), "retry");
+		retry();
+		goto respond;
+	}
+
 	logger(LOG_DEBUG, _("Malformed control command received"));
 	res.res_errno = EINVAL;
 
diff --git a/src/control_common.h b/src/control_common.h
index 40f6a50..6384651 100644
--- a/src/control_common.h
+++ b/src/control_common.h
@@ -33,6 +33,7 @@ enum request_type {
 	REQ_DUMP_GRAPH,
 	REQ_PURGE,
 	REQ_SET_DEBUG,
+	REQ_RETRY,
 };
 
 #define TINC_CTL_VERSION_CURRENT 0
diff --git a/src/net.c b/src/net.c
index a38c68b..2593628 100644
--- a/src/net.c
+++ b/src/net.c
@@ -279,9 +279,7 @@ static void sighup_handler(int signal, short events, void *data) {
 	try_outgoing_connections();
 }
 
-static void sigalrm_handler(int signal, short events, void *data) {
-	logger(LOG_NOTICE, _("Got %s signal"), strsignal(signal));
-
+void retry(void) {
 	connection_t *c;
 	splay_node_t *node;
 
@@ -307,7 +305,6 @@ int main_loop(void) {
 	struct event sighup_event;
 	struct event sigterm_event;
 	struct event sigquit_event;
-	struct event sigalrm_event;
 
 	cp();
 
@@ -319,8 +316,6 @@ int main_loop(void) {
 	signal_add(&sigterm_event, NULL);
 	signal_set(&sigquit_event, SIGQUIT, sigterm_handler, NULL);
 	signal_add(&sigquit_event, NULL);
-	signal_set(&sigalrm_event, SIGALRM, sigalrm_handler, NULL);
-	signal_add(&sigalrm_event, NULL);
 
 	if(event_loop(0) < 0) {
 		logger(LOG_ERR, _("Error while waiting for input: %s"), strerror(errno));
@@ -330,7 +325,6 @@ int main_loop(void) {
 	signal_del(&sighup_event);
 	signal_del(&sigterm_event);
 	signal_del(&sigquit_event);
-	signal_del(&sigalrm_event);
 	event_del(&timeout_event);
 
 	return 0;
diff --git a/src/net.h b/src/net.h
index fd14c37..b8e9e37 100644
--- a/src/net.h
+++ b/src/net.h
@@ -156,6 +156,7 @@ extern void handle_device_data(int, short, void *);
 extern void handle_meta_connection_data(int, short, void *);
 extern void regenerate_key();
 extern void purge(void);
+extern void retry(void);
 
 #ifndef HAVE_MINGW
 #define closesocket(s) close(s)
diff --git a/src/tincctl.c b/src/tincctl.c
index bd82ec4..e276445 100644
--- a/src/tincctl.c
+++ b/src/tincctl.c
@@ -91,6 +91,7 @@ static void usage(bool status) {
 				"    graph                    - graph of the VPN in dotty format\n"
 				"    purge                    Purge unreachable nodes\n"
 				"  debug N                    Set debug level\n"
+				"  retry                      Retry all outgoing connections\n"
 				"\n"));
 		printf(_("Report bugs to tinc at tinc-vpn.org.\n"));
 	}
@@ -584,6 +585,10 @@ int main(int argc, char *argv[], char *envp[]) {
 									   sizeof(debuglevel)) != -1;
 	}
 
+	if(!strcasecmp(argv[optind], "retry")) {
+		return send_ctl_request_cooked(fd, REQ_RETRY, NULL, 0) != -1;
+	}
+
 	fprintf(stderr, _("Unknown command `%s'.\n"), argv[optind]);
 	usage(true);
 	
-- 
1.5.2.2.238.g7cbf2f2-dirty

-------------- next part --------------
>From 17d66c92133b0956e508b4a4f43daa19413ec81f Mon Sep 17 00:00:00 2001
From: Scott Lamb <slamb at slamb.org>
Date: Sat, 21 Jul 2007 12:18:29 -0700
Subject: [PATCH] Update documentation to match tincctl changes

---
 doc/Makefile.am  |   12 ++++-
 doc/tinc.texi    |  117 +++++++++++++++++++++++++++++++++++++++++++++--------
 doc/tincctl.8.in |  104 ++++++++++++++++++++++++++++++++++++++++++++++++
 doc/tincd.8.in   |   32 ++++-----------
 src/tincd.c      |    1 +
 5 files changed, 221 insertions(+), 45 deletions(-)
 create mode 100644 doc/tincctl.8.in

diff --git a/doc/Makefile.am b/doc/Makefile.am
index 7c49d84..66de6d9 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -2,11 +2,11 @@
 
 info_TEXINFOS = tinc.texi
 
-man_MANS = tincd.8 tinc.conf.5
+man_MANS = tincd.8 tincctl.8 tinc.conf.5
 
-EXTRA_DIST = tincinclude.texi.in tincd.8.in tinc.conf.5.in sample-config.tar.gz
+EXTRA_DIST = tincinclude.texi.in tincd.8.in tincctl.8.in tinc.conf.5.in sample-config.tar.gz
 
-CLEANFILES = *.html tinc.info tincd.8 tinc.conf.5 tincinclude.texi
+CLEANFILES = *.html tinc.info tincd.8 tincctl.8 tinc.conf.5 tincinclude.texi
 
 # Use `ginstall' in the definition of man_MANS to avoid
 # confusion with the `install' target.  The install rule transforms `ginstall'
@@ -25,6 +25,9 @@ texi2html: tinc.texi
 tincd.8.html: tincd.8
 	w3mman2html $< > $@
 
+tincctl.8.html: tincctl.8
+	w3mman2html $< > $@
+
 tinc.conf.5.html: tinc.conf.5
 	w3mman2html $< > $@
 
@@ -37,6 +40,9 @@ substitute = sed \
 tincd.8: tincd.8.in
 	$(substitute) tincd.8.in > tincd.8
 
+tincctl.8: tincctl.8.in
+	$(substitute) tincctl.8.in > tincctl.8
+
 tinc.conf.5: tinc.conf.5.in
 	$(substitute) tinc.conf.5.in > tinc.conf.5
 
diff --git a/doc/tinc.texi b/doc/tinc.texi
index c968eb3..2ea54be 100644
--- a/doc/tinc.texi
+++ b/doc/tinc.texi
@@ -71,6 +71,7 @@ permission notice identical to this one.
 * Installation::
 * Configuration::
 * Running tinc::
+* Controlling tinc::
 * Technical information::
 * Platform specific information::
 * About us::
@@ -981,7 +982,7 @@ accidental eavesdropping if you are editting the configuration file.
 @cindex PrivateKeyFile
 @item PrivateKeyFile = <@var{path}> (@file{@value{sysconfdir}/tinc/@var{netname}/rsa_key.priv})
 This is the full path name of the RSA private key file that was
-generated by @samp{tincd --generate-keys}.  It must be a full path, not a
+generated by @samp{tincctl generate-keys}.  It must be a full path, not a
 relative directory.
 
 Note that there must be exactly one of PrivateKey
@@ -1053,7 +1054,7 @@ This is the RSA public key for this host.
 @cindex PublicKeyFile
 @item PublicKeyFile = <@var{path}> [obsolete]
 This is the full path name of the RSA public key file that was generated
-by @samp{tincd --generate-keys}.  It must be a full path, not a relative
+by @samp{tincctl generate-keys}.  It must be a full path, not a relative
 directory.
 
 @cindex PEM format
@@ -1230,7 +1231,7 @@ Now that you have already created the main configuration file and your host conf
 you can easily create a public/private keypair by entering the following command:
 
 @example
-tincd -n @var{netname} -K
+tincctl -n @var{netname} generate-keys
 @end example
 
 Tinc will generate a public and a private key and ask you where to put them.
@@ -1459,7 +1460,7 @@ Address = 4.5.6.7
 A, B, C and D all have generated a public/private keypair with the following command:
 
 @example
-tincd -n company -K
+tincctl -n company generate-keys
 @end example
 
 The private key is stored in @file{@value{sysconfdir}/tinc/company/rsa_key.priv},
@@ -1525,20 +1526,12 @@ This will also disable the automatic restart mechanism for fatal errors.
 Set debug level to @var{level}.  The higher the debug level, the more gets
 logged.  Everything goes via syslog.
 
- at item -k, --kill[=@var{signal}]
-Attempt to kill a running tincd (optionally with the specified @var{signal} instead of SIGTERM) and exit.
-Use it in conjunction with the -n option to make sure you kill the right tinc daemon.
-Under native Windows the optional argument is ignored,
-the service will always be stopped and removed.
-
 @item -n, --net=@var{netname}
 Use configuration for net @var{netname}. @xref{Multiple networks}.
 
- at item -K, --generate-keys[=@var{bits}]
-Generate public/private keypair of @var{bits} length. If @var{bits} is not specified,
-1024 is the default. tinc will ask where you want to store the files,
-but will default to the configuration directory (you can use the -c or -n option
-in combination with -K). After that, tinc will quit.
+ at item --controlsocket=@var{filename}
+Open control socket at @var{filename}. If unspecified, the default is
+ at file{@value{localstatedir}/run/tinc. at var{netname}.control}.
 
 @item -L, --mlock
 Lock tinc into main memory.
@@ -1548,9 +1541,6 @@ This will prevent sensitive data like shared private keys to be written to the s
 Write log entries to a file instead of to the system logging facility.
 If @var{file} is omitted, the default is @file{@value{localstatedir}/log/tinc. at var{netname}.log}.
 
- at item --pidfile=@var{file}
-Write PID to @var{file} instead of @file{@value{localstatedir}/run/tinc. at var{netname}.pid}.
-
 @item --bypass-security
 Disables encryption and authentication.
 Only useful for debugging.
@@ -1793,6 +1783,97 @@ Be sure to include the following information in your bugreport:
 @end itemize
 
 @c ==================================================================
+ at node    Controlling tinc
+ at chapter Controlling tinc
+
+You can control and inspect a running @samp{tincd} through the @samp{tincctl}
+command. A quick example:
+
+ at example
+tincctl -n @var{netname} reload
+ at end example
+
+ at menu
+* tincctl runtime options::
+* tincctl commands::
+ at end menu
+
+
+ at c ==================================================================
+ at node    tincctl runtime options
+ at section tincctl runtime options
+
+ at c from the manpage
+ at table @option
+ at item -c, --config=@var{path}
+Read configuration options from the directory @var{path}.  The default is
+ at file{@value{sysconfdir}/tinc/@var{netname}/}.
+
+ at item -n, --net=@var{netname}
+Use configuration for net @var{netname}. @xref{Multiple networks}.
+
+ at item --controlsocket=@var{filename}
+Open control socket at @var{filename}. If unspecified, the default is
+ at file{@value{localstatedir}/run/tinc. at var{netname}.control}.
+
+ at item --help
+Display a short reminder of runtime options and commands, then terminate.
+
+ at item --version
+Output version information and exit.
+
+ at end table
+
+
+ at c ==================================================================
+ at node    tincctl commands
+ at section tincctl commands
+
+ at c from the manpage
+ at table @code
+
+ at item start
+Start @samp{tincd}.
+
+ at item stop
+Stop @samp{tincd}.
+
+ at item restart
+Restart @samp{tincd}.
+
+ at item reload
+Partially rereads configuration files. Connections to hosts whose host
+config files are removed are closed. New outgoing connections specified
+in @file{tinc.conf} will be made.
+
+ at item pid
+Shows the PID of the currently running @samp{tincd}.
+
+ at item generate-keys [@var{bits}]
+Generate public/private keypair of @var{bits} length. If @var{bits} is not specified,
+1024 is the default. tinc will ask where you want to store the files,
+but will default to the configuration directory (you can use the -c or -n
+option).
+
+ at item dump nodes
+Dump a list of all known nodes in the VPN.
+
+ at item dump edges
+Dump a list of all known connections in the VPN.
+
+ at item dump subnets
+Dump a list of all known subnets in the VPN.
+
+ at item dump connections
+Dump a list of all meta connections with ourself.
+
+ at item dump graph
+Dump a graph of the VPN in dotty format.
+
+ at end table
+
+
+ at c ==================================================================
 @node    Technical information
 @chapter Technical information
 
diff --git a/doc/tincctl.8.in b/doc/tincctl.8.in
new file mode 100644
index 0000000..eeb5031
--- /dev/null
+++ b/doc/tincctl.8.in
@@ -0,0 +1,104 @@
+.Dd 2007-07-20
+.Dt TINCCTL 8
+.\" Manual page created by:
+.\" Scott Lamb
+.Sh NAME
+.Nm tincctl
+.Nd tinc VPN control
+.Sh SYNOPSIS
+.Nm
+.Op Fl cn
+.Op Fl -config Ns = Ns Ar DIR
+.Op Fl -net Ns = Ns Ar NETNAME
+.Op Fl -controlsocket Ns = Ns Ar FILENAME
+.Op Fl -help
+.Op Fl -version
+.Ar COMMAND
+.Sh DESCRIPTION
+This is the control program of tinc, a secure virtual private network (VPN)
+project.
+.Nm
+communicates with
+.Xr tincd 8
+to alter and inspect the running VPN's state.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl n, -net Ns = Ns Ar NETNAME
+Communicate with tincd(8) connected with
+.Ar NETNAME .
+.It Fl -controlsocket Ns = Ns Ar FILENAME
+Open control socket at
+.Ar FILENAME .
+If unspecified, the default is
+.Pa @localstatedir@/run/tinc. Ns Ar NETNAME Ns Pa .control.
+.It Fl -help
+Display short list of options.
+.It Fl -version
+Output version information and exit.
+.El
+.Sh COMMANDS
+.zZ
+.Bl -tag -width indent
+.It start
+Start
+.Xr tincd 8 .
+.It stop
+Stop
+.Xr tincd 8 .
+.It restart
+Restart
+.Xr tincd 8 .
+.It reload
+Partially rereads configuration files. Connections to hosts whose host
+config files are removed are closed. New outgoing connections specified
+in
+.Xr tinc.conf 5
+will be made.
+.It pid
+Shows the PID of the currently running
+.Xr tincd 8 .
+.It generate-keys Op bits
+Generate public/private RSA keypair and exit.
+If
+.Ar bits
+is omitted, the default length will be 1024 bits.
+When saving keys to existing files, tinc will not delete the old keys;
+you have to remove them manually.
+.It dump nodes
+Dump a list of all known nodes in the VPN.
+.It dump edges
+Dump a list of all known connections in the VPN.
+.It dump subnets
+Dump a list of all known subnets in the VPN.
+.It dump connections
+Dump a list of all meta connections with ourself.
+.It dump graph
+Dump a graph of the VPN in
+.Xr dotty 1
+format.
+.El
+.Sh BUGS
+The "start", "restart", "reload", and "dump" commands are not yet implemented.
+.Pp
+If you find any bugs, report them to tinc at tinc-vpn.org.
+.Sh SEE ALSO
+.Xr tincd 8 ,
+.Xr tinc.conf 5 ,
+.Xr dotty 1 ,
+.Pa http://www.tinc-vpn.org/ ,
+.Pa http://www.cabal.org/ .
+.Pp
+The full documentation for tinc is maintained as a Texinfo manual.
+If the info and tinc programs are properly installed at your site,
+the command
+.Ic info tinc
+should give you access to the complete manual.
+.Pp
+tinc comes with ABSOLUTELY NO WARRANTY.
+This is free software, and you are welcome to redistribute it under certain conditions;
+see the file COPYING for details.
+.Sh AUTHORS
+.An "Ivo Timmermans"
+.An "Guus Sliepen" Aq guus at tinc-vpn.org
+.Pp
+And thanks to many others for their contributions to tinc!
diff --git a/doc/tincd.8.in b/doc/tincd.8.in
index 97654f3..37a134e 100644
--- a/doc/tincd.8.in
+++ b/doc/tincd.8.in
@@ -8,16 +8,13 @@
 .Nd tinc VPN daemon
 .Sh SYNOPSIS
 .Nm
-.Op Fl cdDkKnL
+.Op Fl cdDKnL
 .Op Fl -config Ns = Ns Ar DIR
 .Op Fl -no-detach
 .Op Fl -debug Ns Op = Ns Ar LEVEL
-.Op Fl -kill Ns Op = Ns Ar SIGNAL
 .Op Fl -net Ns = Ns Ar NETNAME
-.Op Fl -generate-keys Ns Op = Ns Ar BITS
 .Op Fl -mlock
 .Op Fl -logfile Ns Op = Ns Ar FILE
-.Op Fl -pidfile Ns = Ns Ar FILE
 .Op Fl -bypass-security
 .Op Fl -help
 .Op Fl -version
@@ -51,24 +48,9 @@ If not mentioned otherwise, this will show log messages on the standard error ou
 Increase debug level or set it to
 .Ar LEVEL
 (see below).
-.It Fl k, -kill Ns Op = Ns Ar SIGNAL
-Attempt to kill a running
-.Nm
-(optionally with the specified
-.Ar SIGNAL
-instead of SIGTERM) and exit.
-Under Windows (not Cygwin) the optional argument is ignored,
-the service will always be stopped and removed.
 .It Fl n, -net Ns = Ns Ar NETNAME
 Connect to net
 .Ar NETNAME .
-.It Fl K, -generate-keys Ns Op = Ns Ar BITS
-Generate public/private RSA keypair and exit.
-If
-.Ar BITS
-is omitted, the default length will be 1024 bits.
-When saving keys to existing files, tinc will not delete the old keys,
-you have to remove them manually.
 .It Fl L, -mlock
 Lock tinc into main memory.
 This will prevent sensitive data like shared private keys to be written to the system swap files/partitions.
@@ -78,12 +60,13 @@ If
 .Ar FILE
 is omitted, the default is
 .Pa @localstatedir@/log/tinc. Ns Ar NETNAME Ns Pa .log.
-.It Fl -pidfile Ns = Ns Ar FILE
-Write PID to
+.It Fl -controlsocket Ns = Ns Ar FILENAME
+Open control socket at
+.Ar FILENAME .
+If
 .Ar FILE
-instead of
-.Pa @localstatedir@/run/tinc. Ns Ar NETNAME Ns Pa .pid.
-Under Windows this option will be ignored.
+is omitted, the default is
+.Pa @localstatedir@/run/tinc. Ns Ar NETNAME Ns Pa .control.
 .It Fl -bypass-security
 Disables encryption and authentication of the meta protocol.
 Only useful for debugging.
@@ -167,6 +150,7 @@ If you find any bugs, report them to tinc at tinc-vpn.org.
 .Sh TODO
 A lot, especially security auditing.
 .Sh SEE ALSO
+.Xr tincctl 8 ,
 .Xr tinc.conf 5 ,
 .Pa http://www.tinc-vpn.org/ ,
 .Pa http://www.cabal.org/ .
diff --git a/src/tincd.c b/src/tincd.c
index 95c45d3..65c452a 100644
--- a/src/tincd.c
+++ b/src/tincd.c
@@ -109,6 +109,7 @@ static void usage(bool status)
 				"  -L, --mlock                   Lock tinc into main memory.\n"
 				"      --logfile[=FILENAME]      Write log entries to a logfile.\n"
 				"      --controlsocket=FILENAME  Open control socket at FILENAME.\n"
+				"      --bypass-security         Disables meta protocol security, for debugging.\n"
 				"      --help                    Display this help and exit.\n"
 				"      --version                 Output version information and exit.\n\n"));
 		printf(_("Report bugs to tinc at tinc-vpn.org.\n"));
-- 
1.5.2.2.238.g7cbf2f2-dirty

-------------- next part --------------
>From c30f4103d030826de267b20561f1677d4d0de3ad Mon Sep 17 00:00:00 2001
From: Scott Lamb <slamb at slamb.org>
Date: Sat, 21 Jul 2007 12:20:54 -0700
Subject: [PATCH] Alter debugging levels through control socket
---
 doc/tinc.texi        |    7 +++----
 doc/tincctl.8.in     |    3 +++
 doc/tincd.8.in       |    3 ---
 src/control.c        |   19 +++++++++++++++++++
 src/control_common.h |    1 +
 src/net.c            |   23 -----------------------
 src/tincctl.c        |   13 +++++++++++++
 7 files changed, 39 insertions(+), 30 deletions(-)

diff --git a/doc/tinc.texi b/doc/tinc.texi
index e953472..b3f570c 100644
--- a/doc/tinc.texi
+++ b/doc/tinc.texi
@@ -1565,10 +1565,6 @@ Partially rereads configuration files.
 Connections to hosts whose host config file are removed are closed.
 New outgoing connections specified in @file{tinc.conf} will be made.
 
- at item INT
-Temporarily increases debug level to 5.
-Send this signal again to revert to the original level.
-
 @end table
 
 @c ==================================================================
@@ -1854,6 +1850,9 @@ Dump a graph of the VPN in dotty format.
 @item purge
 Purges all information remembered about unreachable nodes.
 
+ at item debug @var{level}
+Sets debug level to @var{level}.
+
 @end table
 
 
diff --git a/doc/tincctl.8.in b/doc/tincctl.8.in
index 40bd136..623c908 100644
--- a/doc/tincctl.8.in
+++ b/doc/tincctl.8.in
@@ -78,6 +78,9 @@ Dump a graph of the VPN in
 format.
 .It purge
 Purges all information remembered about unreachable nodes.
+.It debug Ar N
+Sets debug level to
+.Ar N .
 .El
 .Sh BUGS
 The "start", "restart", and "reload" commands are not yet implemented.
diff --git a/doc/tincd.8.in b/doc/tincd.8.in
index 96c773f..b3142e8 100644
--- a/doc/tincd.8.in
+++ b/doc/tincd.8.in
@@ -95,9 +95,6 @@ Connections to hosts whose host config file are removed are closed.
 New outgoing connections specified in
 .Pa tinc.conf
 will be made.
-.It INT
-Temporarily increases debug level to 5.
-Send this signal again to revert to the original level.
 .El
 .Sh DEBUG LEVELS
 The tinc daemon can send a lot of messages to the syslog.
diff --git a/src/control.c b/src/control.c
index a78f78a..775bb98 100644
--- a/src/control.c
+++ b/src/control.c
@@ -101,6 +101,25 @@ static void handle_control_data(struct bufferevent *event, void *data) {
 		goto respond;
 	}
 
+	if(req->type == REQ_SET_DEBUG) {
+		debug_t new_debug_level;
+
+		logger(LOG_NOTICE, _("Got '%s' command"), "debug");
+		if(req->length != sizeof(*req) + sizeof debug_level)
+			res.res_errno = EINVAL;
+		else {
+			memcpy(&new_debug_level, req + 1, sizeof(debug_t));
+			logger(LOG_NOTICE, _("Changing debug level from %d to %d"),
+				   debug_level, new_debug_level);
+			if(evbuffer_add_printf(res_data,
+								   _("Changing debug level from %d to %d\n"),
+								   debug_level, new_debug_level) == -1)
+				res.res_errno = errno;
+			debug_level = new_debug_level;
+		}
+		goto respond;
+	}
+
 	logger(LOG_DEBUG, _("Malformed control command received"));
 	res.res_errno = EINVAL;
 
diff --git a/src/control_common.h b/src/control_common.h
index cbb8aa9..40f6a50 100644
--- a/src/control_common.h
+++ b/src/control_common.h
@@ -32,6 +32,7 @@ enum request_type {
 	REQ_DUMP_CONNECTIONS,
 	REQ_DUMP_GRAPH,
 	REQ_PURGE,
+	REQ_SET_DEBUG,
 };
 
 #define TINC_CTL_VERSION_CURRENT 0
diff --git a/src/net.c b/src/net.c
index d2b3134..a38c68b 100644
--- a/src/net.c
+++ b/src/net.c
@@ -233,25 +233,6 @@ static void sigterm_handler(int signal, short events, void *data) {
 	event_loopexit(NULL);
 }
 
-static void sigint_handler(int signal, short events, void *data) {
-	static int saved_debug_level = -1;
-
-	logger(LOG_NOTICE, _("Got %s signal"), strsignal(signal));
-
-	if(saved_debug_level != -1) {
-		logger(LOG_NOTICE, _("Reverting to old debug level (%d)"),
-			saved_debug_level);
-		debug_level = saved_debug_level;
-		saved_debug_level = -1;
-	} else {
-		logger(LOG_NOTICE,
-			_("Temporarily setting debug level to 5.  Kill me with SIGINT again to go back to level %d."),
-			debug_level);
-		saved_debug_level = debug_level;
-		debug_level = 5;
-	}
-}
-
 static void sighup_handler(int signal, short events, void *data) {
 	connection_t *c;
 	splay_node_t *node;
@@ -324,7 +305,6 @@ static void sigalrm_handler(int signal, short events, void *data) {
 int main_loop(void) {
 	struct event timeout_event;
 	struct event sighup_event;
-	struct event sigint_event;
 	struct event sigterm_event;
 	struct event sigquit_event;
 	struct event sigalrm_event;
@@ -335,8 +315,6 @@ int main_loop(void) {
 	event_add(&timeout_event, &(struct timeval){pingtimeout, 0});
 	signal_set(&sighup_event, SIGHUP, sighup_handler, NULL);
 	signal_add(&sighup_event, NULL);
-	signal_set(&sigint_event, SIGINT, sigint_handler, NULL);
-	signal_add(&sigint_event, NULL);
 	signal_set(&sigterm_event, SIGTERM, sigterm_handler, NULL);
 	signal_add(&sigterm_event, NULL);
 	signal_set(&sigquit_event, SIGQUIT, sigterm_handler, NULL);
@@ -350,7 +328,6 @@ int main_loop(void) {
 	}
 
 	signal_del(&sighup_event);
-	signal_del(&sigint_event);
 	signal_del(&sigterm_event);
 	signal_del(&sigquit_event);
 	signal_del(&sigalrm_event);
diff --git a/src/tincctl.c b/src/tincctl.c
index 96f2ae2..bd82ec4 100644
--- a/src/tincctl.c
+++ b/src/tincctl.c
@@ -90,6 +90,7 @@ static void usage(bool status) {
 				"    connections              - all meta connections with ourself\n"
 				"    graph                    - graph of the VPN in dotty format\n"
 				"    purge                    Purge unreachable nodes\n"
+				"  debug N                    Set debug level\n"
 				"\n"));
 		printf(_("Report bugs to tinc at tinc-vpn.org.\n"));
 	}
@@ -571,6 +572,18 @@ int main(int argc, char *argv[], char *envp[]) {
 		return send_ctl_request_cooked(fd, REQ_PURGE, NULL, 0) != -1;
 	}
 
+	if(!strcasecmp(argv[optind], "debug")) {
+		int debuglevel;
+
+		if(argc != optind + 2) {
+			fprintf(stderr, "Invalid arguments.\n");
+			return 1;
+		}
+		debuglevel = atoi(argv[optind+1]);
+		return send_ctl_request_cooked(fd, REQ_SET_DEBUG, &debuglevel,
+									   sizeof(debuglevel)) != -1;
+	}
+
 	fprintf(stderr, _("Unknown command `%s'.\n"), argv[optind]);
 	usage(true);
 	
-- 
1.5.2.2.238.g7cbf2f2-dirty

-------------- next part --------------
>From 2384bfa9bd5a1447ce645fa07bdf492bc6be9937 Mon Sep 17 00:00:00 2001
From: Scott Lamb <slamb at slamb.org>
Date: Sat, 21 Jul 2007 12:20:54 -0700
Subject: [PATCH] Reload configuration through control socket

I also kept the SIGHUP handler, which many people will expect to see.
The control socket is better, though - it will tell you if there is a
problem.
---
 doc/tincctl.8.in |    8 +++++++-
 src/control.c    |    6 ++++++
 src/net.c        |   11 ++++++++---
 src/net.h        |    1 +
 src/tincctl.c    |    5 +++++
 5 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/doc/tincctl.8.in b/doc/tincctl.8.in
index 899b066..493adfd 100644
--- a/doc/tincctl.8.in
+++ b/doc/tincctl.8.in
@@ -93,9 +93,15 @@ and if
 .Xr tincd 8
 didn't succeed to connect to an uplink the first time after it started,
 it defaults to the maximum time of 15 minutes.
+.It reload
+Partially rereads configuration files.
+Connections to hosts whose host config files are removed are closed.
+New outgoing connections specified in
+.Pa tinc.conf
+will be made.
 .El
 .Sh BUGS
-The "start", "restart", and "reload" commands are not yet implemented.
+The "start" and "restart" commands are not yet implemented.
 .Pp
 If you find any bugs, report them to tinc at tinc-vpn.org.
 .Sh SEE ALSO
diff --git a/src/control.c b/src/control.c
index 45ef51e..5665cbb 100644
--- a/src/control.c
+++ b/src/control.c
@@ -126,6 +126,12 @@ static void handle_control_data(struct bufferevent *event, void *data) {
 		goto respond;
 	}
 
+	if(req->type == REQ_RELOAD) {
+		logger(LOG_NOTICE, _("Got '%s' command"), "reload");
+		res.res_errno = reload_configuration();
+		goto respond;
+	}
+
 	logger(LOG_DEBUG, _("Malformed control command received"));
 	res.res_errno = EINVAL;
 
diff --git a/src/net.c b/src/net.c
index 2593628..fb06c8a 100644
--- a/src/net.c
+++ b/src/net.c
@@ -234,13 +234,16 @@ static void sigterm_handler(int signal, short events, void *data) {
 }
 
 static void sighup_handler(int signal, short events, void *data) {
+	logger(LOG_NOTICE, _("Got %s signal"), strsignal(signal));
+	reload_configuration();
+}
+
+int reload_configuration(void) {
 	connection_t *c;
 	splay_node_t *node;
 	char *fname;
 	struct stat s;
 	static time_t last_config_check = 0;
-	
-	logger(LOG_NOTICE, _("Got %s signal"), strsignal(signal));
 
 	/* Reread our own configuration file */
 
@@ -250,7 +253,7 @@ static void sighup_handler(int signal, short events, void *data) {
 	if(!read_server_config()) {
 		logger(LOG_ERR, _("Unable to reread configuration file, exitting."));
 		event_loopexit(NULL);
-		return;
+		return EINVAL;
 	}
 
 	/* Close connections to hosts that have a changed or deleted host config file */
@@ -277,6 +280,8 @@ static void sighup_handler(int signal, short events, void *data) {
 	/* Try to make outgoing connections */
 	
 	try_outgoing_connections();
+
+	return 0;
 }
 
 void retry(void) {
diff --git a/src/net.h b/src/net.h
index b8e9e37..c97d931 100644
--- a/src/net.h
+++ b/src/net.h
@@ -157,6 +157,7 @@ extern void handle_meta_connection_data(int, short, void *);
 extern void regenerate_key();
 extern void purge(void);
 extern void retry(void);
+extern int reload_configuration(void);
 
 #ifndef HAVE_MINGW
 #define closesocket(s) close(s)
diff --git a/src/tincctl.c b/src/tincctl.c
index e276445..c254d51 100644
--- a/src/tincctl.c
+++ b/src/tincctl.c
@@ -92,6 +92,7 @@ static void usage(bool status) {
 				"    purge                    Purge unreachable nodes\n"
 				"  debug N                    Set debug level\n"
 				"  retry                      Retry all outgoing connections\n"
+				"  reload                     Partial reload of configuration\n"
 				"\n"));
 		printf(_("Report bugs to tinc at tinc-vpn.org.\n"));
 	}
@@ -589,6 +590,10 @@ int main(int argc, char *argv[], char *envp[]) {
 		return send_ctl_request_cooked(fd, REQ_RETRY, NULL, 0) != -1;
 	}
 
+	if(!strcasecmp(argv[optind], "reload")) {
+		return send_ctl_request_cooked(fd, REQ_RELOAD, NULL, 0) != -1;
+	}
+
 	fprintf(stderr, _("Unknown command `%s'.\n"), argv[optind]);
 	usage(true);
 	
-- 
1.5.2.2.238.g7cbf2f2-dirty



More information about the tinc-devel mailing list