[lxc-devel] [PATCH] add lxc.network.script(.pre|.post|) configuration hooks

Stefan Tomanek stefan.tomanek at wertarbyte.de
Fri Oct 8 11:23:24 UTC 2010


This commit adds an configuration option to specify a script to be
executed before, during and after creating or configuring the pair of
veth devices. The name of the host sided device is passed as an
argument, so the script can be used to configures routes or firewall
rules related to the container. The interface can be extended to service
other network types, and as well can be used to introduce scriptable
hooks into other sections of the configuration.
---
 src/lxc/conf.c    |  103 ++++++++++++++++++++++++++++++++++++++++++++++-------
 src/lxc/conf.h    |   23 +++++++-----
 src/lxc/confile.c |   35 ++++++++++++++++++
 src/lxc/start.c   |    2 +-
 4 files changed, 140 insertions(+), 23 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index adfe862..2993bce 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -24,11 +24,13 @@
 #include <stdio.h>
 #undef _GNU_SOURCE
 #include <stdlib.h>
+#include <stdarg.h>
 #include <errno.h>
 #include <string.h>
 #include <dirent.h>
 #include <mntent.h>
 #include <unistd.h>
+#include <sys/wait.h>
 #include <pty.h>
 
 #include <sys/types.h>
@@ -92,7 +94,7 @@ lxc_log_define(lxc_conf, lxc);
 
 extern int pivot_root(const char * new_root, const char * put_old);
 
-typedef int (*instanciate_cb)(struct lxc_netdev *);
+typedef int (*instanciate_cb)(struct lxc_handler *, struct lxc_netdev *);
 
 struct mount_opt {
 	char *name;
@@ -105,11 +107,11 @@ struct caps_opt {
 	int value;
 };
 
-static int instanciate_veth(struct lxc_netdev *);
-static int instanciate_macvlan(struct lxc_netdev *);
-static int instanciate_vlan(struct lxc_netdev *);
-static int instanciate_phys(struct lxc_netdev *);
-static int instanciate_empty(struct lxc_netdev *);
+static int instanciate_veth(struct lxc_handler *, struct lxc_netdev *);
+static int instanciate_macvlan(struct lxc_handler *, struct lxc_netdev *);
+static int instanciate_vlan(struct lxc_handler *, struct lxc_netdev *);
+static int instanciate_phys(struct lxc_handler *, struct lxc_netdev *);
+static int instanciate_empty(struct lxc_handler *, struct lxc_netdev *);
 
 static  instanciate_cb netdev_conf[LXC_NET_MAXCONFTYPE + 1] = {
 	[LXC_NET_VETH]    = instanciate_veth,
@@ -184,6 +186,52 @@ static struct caps_opt caps_opt[] = {
 	{ "mac_admin",         CAP_MAC_ADMIN         },
 };
 
+static int run_script(const char *name, const char *section, const char *script, ...)
+{
+	va_list argp;
+	int vargc = 4;
+	/* count variable arguments and add 4 for script, container
+	 * and section name  as well as the terminating NULL
+	 */
+	va_start(argp, script);
+	while (va_arg(argp, char*)) vargc++;
+	va_end(argp);
+	INFO("Executing script '%s' for section '%s'", script, section);
+
+	int pid = fork();
+	if (pid < 0) {
+		ERROR("Error forking");
+	} else if (pid == 0) {
+		/* prepare command line arguments */
+		char *args[vargc];
+		args[0] = strdup(script);
+		args[1] = strdup(name);
+		args[2] = strdup(section);
+		args[vargc-1] = (char*) NULL;
+		va_start(argp, script);
+		int i;
+		for (i=3; i<vargc; i++) {
+			args[i] = va_arg(argp, char*);
+		}
+		va_end(argp);
+
+		execv(script, args);
+		/* if we cannot exex, we exit this fork */
+		exit(1);
+	} else {
+		int status = 0;
+		waitpid( pid, &status, 0 );
+		if (status != 0) {
+			/* something weird happened */
+			SYSERROR("Script '%s' terminated with non-zero exitcode %d",  name, status);
+			return -1;
+		} else {
+			return 1; /* all is well */
+		}
+	}
+	return -1;
+}
+
 static int find_fstype_cb(char* buffer, void *data)
 {
 	struct cbarg {
@@ -1204,7 +1252,7 @@ struct lxc_conf *lxc_conf_init(void)
 	return new;
 }
 
-static int instanciate_veth(struct lxc_netdev *netdev)
+static int instanciate_veth(struct lxc_handler *handler, struct lxc_netdev *netdev)
 {
 	char veth1buf[IFNAMSIZ], *veth1;
 	char veth2buf[IFNAMSIZ], *veth2;
@@ -1267,6 +1315,16 @@ static int instanciate_veth(struct lxc_netdev *netdev)
 		}
 	}
 
+	if (netdev->script) {
+		err = run_script(handler->name, "net", netdev->script, "veth",
+			         veth1, (char*) NULL);
+		if (err) {
+			ERROR("failed to run script '%s' for interface '%s'",
+				      netdev->script, veth1);
+			goto out_delete;
+		}
+	}
+
 	DEBUG("instanciated veth '%s/%s', index is '%d'",
 	      veth1, veth2, netdev->ifindex);
 
@@ -1277,7 +1335,7 @@ out_delete:
 	return -1;
 }
 
-static int instanciate_macvlan(struct lxc_netdev *netdev)
+static int instanciate_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
 {
 	char peerbuf[IFNAMSIZ], *peer;
 	int err;
@@ -1317,7 +1375,7 @@ static int instanciate_macvlan(struct lxc_netdev *netdev)
 }
 
 /* XXX: merge with instanciate_macvlan */
-static int instanciate_vlan(struct lxc_netdev *netdev)
+static int instanciate_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
 {
 	char peer[IFNAMSIZ];
 	int err;
@@ -1349,7 +1407,7 @@ static int instanciate_vlan(struct lxc_netdev *netdev)
 	return 0;
 }
 
-static int instanciate_phys(struct lxc_netdev *netdev)
+static int instanciate_phys(struct lxc_handler *handler, struct lxc_netdev *netdev)
 {
 	if (!netdev->link) {
 		ERROR("no link specified for the physical interface");
@@ -1365,17 +1423,20 @@ static int instanciate_phys(struct lxc_netdev *netdev)
 	return 0;
 }
 
-static int instanciate_empty(struct lxc_netdev *netdev)
+static int instanciate_empty(struct lxc_handler *handler, struct lxc_netdev *netdev)
 {
 	netdev->ifindex = 0;
 	return 0;
 }
 
-int lxc_create_network(struct lxc_list *network)
+int lxc_create_network(struct lxc_handler *handler)
 {
+	struct lxc_list *network = &handler->conf->network;
 	struct lxc_list *iterator;
 	struct lxc_netdev *netdev;
 
+	char *cname = handler->name;
+
 	lxc_list_for_each(iterator, network) {
 
 		netdev = iterator->elem;
@@ -1386,10 +1447,26 @@ int lxc_create_network(struct lxc_list *network)
 			return -1;
 		}
 
-		if (netdev_conf[netdev->type](netdev)) {
+		/* run pre conf hook */
+		if (netdev->prescript) {
+			if (!run_script(cname, "net", netdev->prescript, (char*) NULL)) {
+				ERROR("failed to run network pre-conf script '%s'", netdev->prescript);
+				return -1;
+			}
+		}
+
+		if (netdev_conf[netdev->type](handler, netdev)) {
 			ERROR("failed to create netdev");
 			return -1;
 		}
+
+		if (netdev->postscript) {
+			if (!run_script(cname, "net", netdev->postscript, (char*) NULL)) {
+				ERROR("failed to run network post-conf script '%s'", netdev->postscript);
+				return -1;
+			}
+		}
+
 	}
 
 	return 0;
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index b12a346..90f5496 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -28,6 +28,8 @@
 
 #include <lxc/list.h>
 
+#include <start.h> /* for lxc_handler */
+
 enum {
 	LXC_NET_EMPTY,
 	LXC_NET_VETH,
@@ -94,11 +96,14 @@ union netdev_p {
 
 /*
  * Defines a structure to configure a network device
- * @link   : lxc.network.link, name of bridge or host iface to attach if any
- * @name   : lxc.network.name, name of iface on the container side
- * @flags  : flag of the network device (IFF_UP, ... )
- * @ipv4   : a list of ipv4 addresses to be set on the network device
- * @ipv6   : a list of ipv6 addresses to be set on the network device
+ * @link       : lxc.network.link, name of bridge or host iface to attach if any
+ * @name       : lxc.network.name, name of iface on the container side
+ * @flags      : flag of the network device (IFF_UP, ... )
+ * @ipv4       : a list of ipv4 addresses to be set on the network device
+ * @ipv6       : a list of ipv6 addresses to be set on the network device
+ * @script     : a script filename to be executed during configuration
+ * @prescript  : a script filename to be executed before configuration
+ * @postscript : a script filename to be executed after configuration
  */
 struct lxc_netdev {
 	int type;
@@ -111,6 +116,9 @@ struct lxc_netdev {
 	union netdev_p priv;
 	struct lxc_list ipv4;
 	struct lxc_list ipv6;
+	char *script;
+	char *prescript;
+	char *postscript;
 };
 
 /*
@@ -210,7 +218,7 @@ struct lxc_conf {
  */
 extern struct lxc_conf *lxc_conf_init(void);
 
-extern int lxc_create_network(struct lxc_list *networks);
+extern int lxc_create_network(struct lxc_handler *handler);
 extern void lxc_delete_network(struct lxc_list *networks);
 extern int lxc_assign_network(struct lxc_list *networks, pid_t pid);
 
@@ -221,8 +229,5 @@ extern void lxc_delete_tty(struct lxc_tty_info *tty_info);
  * Configure the container from inside
  */
 
-struct lxc_handler;
-
 extern int lxc_setup(const char *name, struct lxc_conf *lxc_conf);
-
 #endif
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index 610ca15..b150fa9 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -63,6 +63,7 @@ static int config_network_hwaddr(const char *, char *, struct lxc_conf *);
 static int config_network_vlan_id(const char *, char *, struct lxc_conf *);
 static int config_network_mtu(const char *, char *, struct lxc_conf *);
 static int config_network_ipv4(const char *, char *, struct lxc_conf *);
+static int config_network_script(const char *, char *, struct lxc_conf *);
 static int config_network_ipv6(const char *, char *, struct lxc_conf *);
 static int config_cap_drop(const char *, char *, struct lxc_conf *);
 static int config_console(const char *, char *, struct lxc_conf *);
@@ -91,6 +92,9 @@ static struct config config[] = {
 	{ "lxc.network.name",         config_network_name         },
 	{ "lxc.network.macvlan.mode", config_network_macvlan_mode },
 	{ "lxc.network.veth.pair",    config_network_veth_pair    },
+	{ "lxc.network.script",       config_network_script       },
+	{ "lxc.network.script.pre",   config_network_script       },
+	{ "lxc.network.script.post",  config_network_script       },
 	{ "lxc.network.hwaddr",       config_network_hwaddr       },
 	{ "lxc.network.mtu",          config_network_mtu          },
 	{ "lxc.network.vlan.id",      config_network_vlan_id      },
@@ -478,6 +482,37 @@ static int config_network_ipv6(const char *key, char *value,
 	return 0;
 }
 
+static int config_network_script(const char *key, char *value,
+				 struct lxc_conf *lxc_conf)
+{
+	struct lxc_netdev *netdev;
+
+	netdev = network_netdev(key, value, &lxc_conf->network);
+	if (!netdev)
+	return -1;
+
+	char *copy = strdup(value);
+	if (!copy) {
+		SYSERROR("failed to dup string '%s'", value);
+		return -1;
+	}
+	if (strcmp(key, "lxc.network.script") == 0) {
+		netdev->script = copy;
+		return 0;
+	}
+	if (strcmp(key, "lxc.network.script.pre") == 0) {
+		netdev->prescript = copy;
+		return 0;
+	}
+	if (strcmp(key, "lxc.network.script.post") == 0) {
+		netdev->postscript = copy;
+		return 0;
+	}
+	SYSERROR("Unknown key: %s", key);
+	free(copy);
+	return 1;
+}
+
 static int config_personality(const char *key, char *value,
 			      struct lxc_conf *lxc_conf)
 {
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 1d4087c..b963b85 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -487,7 +487,7 @@ int lxc_spawn(struct lxc_handler *handler)
 		/* that should be done before the clone because we will
 		 * fill the netdev index and use them in the child
 		 */
-		if (lxc_create_network(&handler->conf->network)) {
+		if (lxc_create_network(handler)) {
 			ERROR("failed to create the network");
 			lxc_sync_fini(handler);
 			return -1;
-- 
1.7.1




More information about the lxc-devel mailing list