[PATCH] New '-o' option to configure server or hosts from command line

Julien Muchembled jm at jmuchemb.eu
Fri Sep 3 13:24:54 CEST 2010


Options given on the command line have precedence over configuration 
from files.

This can be useful, for example, for a roaming node, for which 
'ConnectTo' and <host>.Address depends on its location.
---
This patch is against stable branch. Merging this patch to the 1.1 
branch is trivial.

I use this patch on my Debian for several weeks.
Here is the ifupdown script I use (some information are obfuscated with 
'X' or 'Y'):

#!/bin/sh -e

IP=X.X.X.X

post_up () {
   [ -e /sys/class/net/jmuchemb ] && return
   set -- -d -o ConnectTo=srv  -o srv.Address=$IP \
             -o ConnectTo=srv2 -o srv2.Address=Y.Y.Y.Y
   case $LOGICAL in
     wifi-amuchemb)
       set -- -o ConnectTo=srv2 -o srv2.Address=192.168.1.1
       ;;
     wifi-jmuchemb|eth-jmuchemb)
       set -- -o ConnectTo=srv -o srv.Address=192.168.2.2
       ;;
     *)
       ip route get $IP 2>&- | grep -q "^$IP \\(via [^ ]* \\)\\?dev 
$IFACE " || return 0
       ;;
   esac
   tincd -n jmuchemb "$@"
}

pre_down () {
   tincd -n jmuchemb -k || :
}

${PHASE//-/_}

  src/conf.c       |   92 
+++++++++++++++++++++++++++++++++++------------------
  src/conf.h       |    3 ++
  src/connection.c |   15 +++++++++
  src/tincd.c      |   14 +++++++-
  4 files changed, 92 insertions(+), 32 deletions(-)

diff --git a/src/conf.c b/src/conf.c
index ea33e9c..dc5d2dc 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -36,6 +36,8 @@ int pinginterval = 0;			/* seconds between pings */
  int pingtimeout = 0;			/* seconds to wait for response */
  char *confbase = NULL;			/* directory in which all config files are */
  char *netname = NULL;			/* name of the vpn network */
+list_t *cmdline_conf = NULL;	/* global/host configuration values given 
at the command line */
+
   static int config_compare(const config_t *a, const config_t *b) {
  	int result;
@@ -45,12 +47,17 @@ static int config_compare(const config_t *a, const 
config_t *b) {
  	if(result)
  		return result;
  +	/* give priority to command line options */
+	result = !b->file - !a->file;
+	if (result)
+		return result;
+
  	result = a->line - b->line;
   	if(result)
  		return result;
  	else
-		return strcmp(a->file, b->file);
+		return a->file ? strcmp(a->file, b->file) : 0;
  }
   void init_configuration(avl_tree_t ** config_tree) {
@@ -87,7 +94,7 @@ config_t *lookup_config(avl_tree_t *config_tree, char 
*variable) {
  	config_t cfg, *found;
   	cfg.variable = variable;
-	cfg.file = "";
+	cfg.file = NULL;
  	cfg.line = 0;
   	found = avl_search_closest_greater(config_tree, &cfg);
@@ -233,6 +240,45 @@ static char *readline(FILE * fp, char *buf, size_t 
buflen) {
  	return buf;
  }
  +config_t *parse_config_line(char *line, const char *fname, int lineno) {
+	config_t *cfg;
+	int len;
+	char *variable, *value, *eol;
+	variable = value = line;
+
+	eol = line + strlen(line);
+	while(strchr("\t ", *--eol))
+		*eol = '\0';
+
+	len = strcspn(value, "\t =");
+	value += len;
+	value += strspn(value, "\t ");
+	if(*value == '=') {
+		value++;
+		value += strspn(value, "\t ");
+	}
+	variable[len] = '\0';
+
+	if(!*value) {
+		const char err[] = "No value for variable";
+		if (fname)
+			logger(LOG_ERR, "%s `%s' on line %d while reading config file %s",
+				err, variable, lineno, fname);
+		else
+			logger(LOG_ERR, "%s `%s' in command line option %d",
+				err, variable, lineno);
+		return NULL;
+	}
+
+	cfg = new_config();
+	cfg->variable = xstrdup(variable);
+	cfg->value = xstrdup(value);
+	cfg->file = fname ? xstrdup(fname) : NULL;
+	cfg->line = lineno;
+
+	return cfg;
+}
+
  /*
    Parse a configuration file and put the results in the configuration tree
    starting at *base.
@@ -241,9 +287,7 @@ bool read_config_file(avl_tree_t *config_tree, const 
char *fname) {
  	FILE *fp;
  	char buffer[MAX_STRING_SIZE];
  	char *line;
-	char *variable, *value, *eol;
  	int lineno = 0;
-	int len;
  	bool ignore = false;
  	config_t *cfg;
  	bool result = false;
@@ -280,34 +324,9 @@ bool read_config_file(avl_tree_t *config_tree, 
const char *fname) {
  			continue;
  		}
  -		variable = value = line;
-
-		eol = line + strlen(line);
-		while(strchr("\t ", *--eol))
-			*eol = '\0';
-
-		len = strcspn(value, "\t =");
-		value += len;
-		value += strspn(value, "\t ");
-		if(*value == '=') {
-			value++;
-			value += strspn(value, "\t ");
-		}
-		variable[len] = '\0';
-
-	
-		if(!*value) {
-			logger(LOG_ERR, "No value for variable `%s' on line %d while reading 
config file %s",
-				   variable, lineno, fname);
+		cfg = parse_config_line(line, fname, lineno);
+		if (!cfg)
  			break;
-		}
-
-		cfg = new_config();
-		cfg->variable = xstrdup(variable);
-		cfg->value = xstrdup(value);
-		cfg->file = xstrdup(fname);
-		cfg->line = lineno;
-
  		config_add(config_tree, cfg);
  	}
  @@ -317,9 +336,20 @@ bool read_config_file(avl_tree_t *config_tree, 
const char *fname) {
  }
   bool read_server_config() {
+	list_node_t *node, *next;
  	char *fname;
  	bool x;
  +	for(node = cmdline_conf->tail; node; node = next) {
+		config_t *cfg = (config_t *)node->data;
+		next = node->prev;
+		if (!strchr(cfg->variable, '.')) {
+			config_add(config_tree, cfg);
+			node->data = NULL;
+			list_unlink_node(cmdline_conf, node);
+		}
+	}
+
  	xasprintf(&fname, "%s/tinc.conf", confbase);
  	x = read_config_file(config_tree, fname);
  diff --git a/src/conf.h b/src/conf.h
index dae4eab..a7e42d3 100644
--- a/src/conf.h
+++ b/src/conf.h
@@ -22,6 +22,7 @@
  #define __TINC_CONF_H__
   #include "avl_tree.h"
+#include "list.h"
   typedef struct config_t {
  	char *variable;
@@ -40,6 +41,7 @@ extern int maxtimeout;
  extern bool bypass_security;
  extern char *confbase;
  extern char *netname;
+extern list_t *cmdline_conf;
   extern void init_configuration(avl_tree_t **);
  extern void exit_configuration(avl_tree_t **);
@@ -54,6 +56,7 @@ extern bool get_config_string(const config_t *, char **);
  extern bool get_config_address(const config_t *, struct addrinfo **);
  extern bool get_config_subnet(const config_t *, struct subnet_t **);
  +extern config_t *parse_config_line(char *, const char *, int);
  extern bool read_config_file(avl_tree_t *, const char *);
  extern bool read_server_config(void);
  extern FILE *ask_and_open(const char *, const char *);
diff --git a/src/connection.c b/src/connection.c
index 6229e79..36c0fdb 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -129,9 +129,24 @@ void dump_connections(void) {
  }
   bool read_connection_config(connection_t *c) {
+	list_node_t *node, *next;
+	size_t name_len = strlen(c->name);
  	char *fname;
  	bool x;
  +	for(node = cmdline_conf->tail; node; node = next) {
+		config_t *cfg = (config_t *)node->data;
+		next = node->prev;
+		if (!strncmp(c->name, cfg->variable, name_len) && 
cfg->variable[name_len] == '.') {
+			config_t *new_cfg = new_config();
+			new_cfg->variable = xstrdup(cfg->variable + name_len + 1);
+			new_cfg->value = xstrdup(cfg->value);
+			new_cfg->file = NULL;
+			new_cfg->line = cfg->line;
+			config_add(c->config_tree, new_cfg);
+		}
+	}
+
  	xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
  	x = read_config_file(c->config_tree, fname);
  	free(fname);
diff --git a/src/tincd.c b/src/tincd.c
index 70aa6ba..dc9bf46 100644
--- a/src/tincd.c
+++ b/src/tincd.c
@@ -136,6 +136,7 @@ static void usage(bool status) {
  				"  -L, --mlock                Lock tinc into main memory.\n"
  				"      --logfile[=FILENAME]   Write log entries to a logfile.\n"
  				"      --pidfile=FILENAME     Write PID to FILENAME.\n"
+				"  -o [HOST.]KEY=VALUE        Set global/host configuration value.\n"
  				"  -R, --chroot               chroot to NET dir at startup.\n"
  				"  -U, --user=USER            setuid to given USER at startup.\n"
  				"      --help                 Display this help and exit.\n"
@@ -145,10 +146,14 @@ static void usage(bool status) {
  }
   static bool parse_options(int argc, char **argv) {
+	config_t *cfg;
  	int r;
  	int option_index = 0;
+	int lineno = 0;
  -	while((r = getopt_long(argc, argv, "c:DLd::k::n:K::RU:", 
long_options, &option_index)) != EOF) {
+	cmdline_conf = list_alloc((list_action_t)free_config);
+
+	while((r = getopt_long(argc, argv, "c:DLd::k::n:o:K::RU:", 
long_options, &option_index)) != EOF) {
  		switch (r) {
  			case 0:				/* long option */
  				break;
@@ -217,6 +222,13 @@ static bool parse_options(int argc, char **argv) {
  				netname = xstrdup(optarg);
  				break;
  +			case 'o':				/* option */
+				cfg = parse_config_line(optarg, NULL, ++lineno);
+				if (!cfg)
+					return false;
+				list_insert_tail(cmdline_conf, cfg);
+				break;
+
  			case 'K':				/* generate public/private keypair */
  				if(optarg) {
  					generate_keys = atoi(optarg);
-- 
1.7.1.1.543.g30edb.dirty



More information about the tinc-devel mailing list