From b486346aa2fad7de06c491755b9b50c58c27a017 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Thu, 9 Aug 2012 17:54:48 -0500 Subject: [PATCH 001/171] lxc-wait: Add timeout option Allow to specify a timeout for waiting on state changes via lxc-wait. Helpful for scripts that need to handle errors or excessive delays in state changing procedures. Signed-off-by: Jan Kiszka Signed-off-by: Serge Hallyn --- doc/lxc-wait.sgml.in | 11 +++++++++++ src/lxc/arguments.h | 1 + src/lxc/lxc_wait.c | 16 +++++++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/doc/lxc-wait.sgml.in b/doc/lxc-wait.sgml.in index 4160e3812..61bff8bdf 100644 --- a/doc/lxc-wait.sgml.in +++ b/doc/lxc-wait.sgml.in @@ -79,6 +79,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + + + + Wait timeout seconds for desired state to be reached. + + + + diff --git a/src/lxc/arguments.h b/src/lxc/arguments.h index 40f0d6c1b..3c9d28fd7 100644 --- a/src/lxc/arguments.h +++ b/src/lxc/arguments.h @@ -57,6 +57,7 @@ struct lxc_arguments { /* for lxc-wait */ char *states; + long timeout; /* close fds from parent? */ int close_all_fds; diff --git a/src/lxc/lxc_wait.c b/src/lxc/lxc_wait.c index a58e0c87c..d7a69bcf9 100644 --- a/src/lxc/lxc_wait.c +++ b/src/lxc/lxc_wait.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include @@ -46,12 +48,14 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg) { switch (c) { case 's': args->states = optarg; break; + case 't': args->timeout = atol(optarg); break; } return 0; } static const struct option my_longopts[] = { {"state", required_argument, 0, 's'}, + {"timeout", required_argument, 0, 't'}, LXC_COMMON_OPTIONS }; @@ -66,7 +70,8 @@ Options :\n\ -n, --name=NAME NAME for name of the container\n\ -s, --state=STATE ORed states to wait for\n\ STOPPED, STARTING, RUNNING, STOPPING,\n\ - ABORTING, FREEZING, FROZEN\n", + ABORTING, FREEZING, FROZEN\n\ + -t, --timeout=TMO Seconds to wait for state changes\n", .options = my_longopts, .parser = my_parser, .checker = my_checker, @@ -91,6 +96,11 @@ static int fillwaitedstates(char *strstates, int *states) return 0; } +static void timeout_handler(int signal) +{ + exit(-1); +} + int main(int argc, char *argv[]) { struct lxc_msg msg; @@ -124,6 +134,9 @@ int main(int argc, char *argv[]) goto out_close; } + signal(SIGALRM, timeout_handler); + alarm(my_args.timeout); + for (;;) { if (lxc_monitor_read(fd, &msg) < 0) goto out_close; @@ -140,6 +153,7 @@ int main(int argc, char *argv[]) } if (s[msg.value]) { + alarm(0); ret = 0; goto out_close; } From 8f2c3a702aae3545b631a2b4c6db99f87528553a Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 27 Jul 2012 21:13:53 -0500 Subject: [PATCH 002/171] Introduce support for seccomp. Hi, This patch is so far just a proof of concept. The libseccomp api will be changing soon so it probably wouldn't be worth pulling this until it is updated for the new API. This patch introduces support for seccomp to lxc. Seccomp lets a program restrict its own (and its children's) future access to system calls. It uses a simple whitelist system call policy file. It would probably be better to switch to something more symbolic (i.e specifying 'open' rather than the syscall #, especially given container arch flexibility). I just wanted to get this out there as a first step. You can also get source for an ubuntu package based on this patch at https://code.launchpad.net/~serge-hallyn/ubuntu/quantal/lxc/lxc-seccomp Signed-off-by: Serge Hallyn --- README | 24 +++++++++ configure.ac | 10 ++++ src/lxc/Makefile.am | 9 +++- src/lxc/conf.h | 1 + src/lxc/confile.c | 22 ++++++++ src/lxc/lxc-clone.in | 2 +- src/lxc/lxcseccomp.h | 41 +++++++++++++++ src/lxc/seccomp.c | 121 +++++++++++++++++++++++++++++++++++++++++++ src/lxc/start.c | 8 +++ 9 files changed, 235 insertions(+), 3 deletions(-) create mode 100644 src/lxc/lxcseccomp.h create mode 100644 src/lxc/seccomp.c diff --git a/README b/README index 74fa6ff44..0cf024629 100644 --- a/README +++ b/README @@ -52,3 +52,27 @@ Portability: AUTHOR Daniel Lezcano + +Seccomp with LXC +---------------- + +To restrict a container with seccomp, you must specify a profile which is +basically a whitelist of system calls it may execute. In the container +config file, add a line like + +lxc.seccomp = /var/lib/lxc/q1/seccomp.full + +I created a usable (but basically worthless) seccomp.full file using + +cat > seccomp.full << EOF +1 +whitelist +EOF +for i in `seq 0 300`; do + echo $i >> secomp.full +done +for i in `seq 1024 1079`; do + echo $i >> seccomp.full +done + + -- Serge Hallyn Fri, 27 Jul 2012 15:47:02 +0600 diff --git a/configure.ac b/configure.ac index 900e1e799..a3b961663 100644 --- a/configure.ac +++ b/configure.ac @@ -23,6 +23,11 @@ AC_ARG_ENABLE([apparmor], [], [enable_apparmor=yes]) AM_CONDITIONAL([ENABLE_APPARMOR], [test "x$enable_apparmor" = "xyes"]) +AC_ARG_ENABLE([seccomp], + [AC_HELP_STRING([--enable-seccomp], [enable seccomp])], + [], [enable_seccomp=yes]) +AM_CONDITIONAL([ENABLE_SECCOMP], [test "x$enable_seccomp" = "xyes"]) + AC_ARG_ENABLE([doc], [AC_HELP_STRING([--enable-doc], [make mans (require docbook2man installed) [default=auto]])], [], [enable_doc=auto]) @@ -39,6 +44,11 @@ AM_COND_IF([ENABLE_APPARMOR], AC_CHECK_LIB([apparmor], [aa_change_profile],[],[AC_MSG_ERROR([You must install the AppArmor development package in order to compile lxc])]) AC_SUBST([APPARMOR_LIBS], [-lapparmor])]) +AM_COND_IF([ENABLE_SECCOMP], + [AC_CHECK_HEADER([seccomp.h],[],[AC_MSG_ERROR([You must install the seccomp development package in order to compile lxc])]) + AC_CHECK_LIB([seccomp], [seccomp_init],[],[AC_MSG_ERROR([You must install the seccomp development package in order to compile lxc])]) + AC_SUBST([SECCOMP_LIBS], [-lseccomp])]) + AM_CONDITIONAL([ENABLE_DOCBOOK], [test "x$have_docbook" = "xyes"]) AC_ARG_ENABLE([examples], diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 50e67bbf2..29dfa00d3 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -50,6 +50,7 @@ liblxc_so_SOURCES = \ genl.c genl.h \ \ caps.c caps.h \ + seccomp.c seccomp.h \ mainloop.c mainloop.h \ af_unix.c af_unix.h \ \ @@ -65,13 +66,17 @@ if ENABLE_APPARMOR AM_CFLAGS += -DHAVE_APPARMOR endif +if ENABLE_SECCOMP +AM_CFLAGS += -DHAVE_SECCOMP +endif + liblxc_so_CFLAGS = -fPIC -DPIC $(AM_CFLAGS) liblxc_so_LDFLAGS = \ -shared \ -Wl,-soname,liblxc.so.$(firstword $(subst ., ,$(VERSION))) -liblxc_so_LDADD = -lutil $(CAP_LIBS) $(APPARMOR_LIBS) +liblxc_so_LDADD = -lutil $(CAP_LIBS) $(APPARMOR_LIBS) $(SECCOMP_LIBS) bin_SCRIPTS = \ lxc-ps \ @@ -110,7 +115,7 @@ AM_LDFLAGS = -Wl,-E if ENABLE_RPATH AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir) endif -LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@ +LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@ @SECCOMP_LIBS@ lxc_attach_SOURCES = lxc_attach.c lxc_cgroup_SOURCES = lxc_cgroup.c diff --git a/src/lxc/conf.h b/src/lxc/conf.h index eee12b7b5..ad48042af 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -229,6 +229,7 @@ struct lxc_conf { #if HAVE_APPARMOR /* || HAVE_SELINUX || HAVE_SMACK */ int lsm_umount_proc; #endif + char *seccomp; // filename with the seccomp rules }; int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf); diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 4f816f55b..367a92cd0 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -75,6 +75,7 @@ static int config_network_ipv6(const char *, char *, struct lxc_conf *); static int config_network_ipv6_gateway(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 *); +static int config_seccomp(const char *, char *, struct lxc_conf *); typedef int (*config_cb)(const char *, char *, struct lxc_conf *); @@ -118,6 +119,7 @@ static struct config config[] = { { "lxc.network.ipv6", config_network_ipv6 }, { "lxc.cap.drop", config_cap_drop }, { "lxc.console", config_console }, + { "lxc.seccomp", config_seccomp }, }; static const size_t config_size = sizeof(config)/sizeof(struct config); @@ -610,6 +612,26 @@ static int add_hook(struct lxc_conf *lxc_conf, int which, char *hook) return 0; } +static int config_seccomp(const char *key, char *value, + struct lxc_conf *lxc_conf) +{ + char *path; + + if (lxc_conf->seccomp) { + ERROR("seccomp already defined"); + return -1; + } + path = strdup(value); + if (!path) { + SYSERROR("failed to strdup '%s': %m", value); + return -1; + } + + lxc_conf->seccomp = path; + + return 0; +} + static int config_hook(const char *key, char *value, struct lxc_conf *lxc_conf) { diff --git a/src/lxc/lxc-clone.in b/src/lxc/lxc-clone.in index 04ef20bb2..05d993df2 100644 --- a/src/lxc/lxc-clone.in +++ b/src/lxc/lxc-clone.in @@ -175,7 +175,7 @@ cp $lxc_path/$lxc_orig/config $lxc_path/$lxc_new/config sed -i '/lxc.utsname/d' $lxc_path/$lxc_new/config echo "lxc.utsname = $hostname" >> $lxc_path/$lxc_new/config -grep "lxc.mount[ \t]" $lxc_path/$lxc_new/config >/dev/null 2>&1 && { sed -i '/lxc.mount[ \t]/d' $lxc_path/$lxc_new/config; echo "lxc.mount = $lxc_path/$lxc_new/fstab" >> $lxc_path/$lxc_new/config; } +grep "lxc.mount =" $lxc_path/$lxc_new/config >/dev/null 2>&1 && { sed -i '/lxc.mount =/d' $lxc_path/$lxc_new/config; echo "lxc.mount = $lxc_path/$lxc_new/fstab" >> $lxc_path/$lxc_new/config; } if [ -e $lxc_path/$lxc_orig/fstab ];then cp $lxc_path/$lxc_orig/fstab $lxc_path/$lxc_new/fstab diff --git a/src/lxc/lxcseccomp.h b/src/lxc/lxcseccomp.h new file mode 100644 index 000000000..00262a52c --- /dev/null +++ b/src/lxc/lxcseccomp.h @@ -0,0 +1,41 @@ +/* + * lxc: linux Container library + * + * (C) Copyright Canonical, Inc. 2012 + * + * Authors: + * Serge Hallyn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _lxc_seccomp_h + +#include "conf.h" + +#ifdef HAVE_SECCOMP +int lxc_seccomp_load(struct lxc_conf *conf); +int lxc_read_seccomp_config(struct lxc_conf *conf); +#else +static inline int lxc_seccomp_load(struct lxc_conf *conf) { + return 0; +} + +static inline int lxc_read_seccomp_config(struct lxc_conf *conf) { + return 0; +} +#endif + +#endif diff --git a/src/lxc/seccomp.c b/src/lxc/seccomp.c new file mode 100644 index 000000000..f2c5d00e5 --- /dev/null +++ b/src/lxc/seccomp.c @@ -0,0 +1,121 @@ +/* + * lxc: linux Container library + * + * (C) Copyright Canonical, Inc. 2012 + * + * Authors: + * Serge Hallyn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include "lxcseccomp.h" + +#include "log.h" + +lxc_log_define(lxc_seccomp, lxc); + +/* + * The first line of the config file has a policy language version + * the second line has some directives + * then comes policy subject to the directives + * right now version must be '1' + * the directives must include 'whitelist' (only type of policy currently + * supported) and can include 'debug' (though debug is not yet supported). + */ +static int parse_config(FILE *f, struct lxc_conf *conf) +{ + char line[1024]; + int ret, version; + + ret = fscanf(f, "%d\n", &version); + if (ret != 1 || version != 1) { + ERROR("invalid version"); + return -1; + } + if (!fgets(line, 1024, f)) { + ERROR("invalid config file"); + return -1; + } + if (!strstr(line, "whitelist")) { + ERROR("only whitelist policy is supported"); + return -1; + } + if (strstr(line, "debug")) { + ERROR("debug not yet implemented"); + return -1; + } + /* now read in the whitelist entries one per line */ + while (fgets(line, 1024, f)) { + int nr; + ret = sscanf(line, "%d", &nr); + if (ret != 1) + return -1; + ret = seccomp_rule_add(SCMP_ACT_ALLOW, nr, 0); + if (ret < 0) { + ERROR("failed loading allow rule for %d\n", nr); + return ret; + } + } + return 0; +} + +int lxc_read_seccomp_config(struct lxc_conf *conf) +{ + FILE *f; + int ret; + + if (seccomp_init(SCMP_ACT_ERRNO(31)) < 0) { /* for debug, pass in SCMP_ACT_TRAP */ + ERROR("failed initializing seccomp"); + return -1; + } + if (!conf->seccomp) + return 0; + + /* turn of no-new-privs. We don't want it in lxc, and it breaks + * with apparmor */ + if (seccomp_attr_set(SCMP_FLTATR_CTL_NNP, 0)) { + ERROR("failed to turn off n-new-privs\n"); + return -1; + } + + f = fopen(conf->seccomp, "r"); + if (!f) { + SYSERROR("failed to open seccomp policy file %s\n", conf->seccomp); + return -1; + } + ret = parse_config(f, conf); + fclose(f); + return ret; +} + +int lxc_seccomp_load(struct lxc_conf *conf) +{ + int ret; + if (!conf->seccomp) + return 0; + ret = seccomp_load(); + if (ret < 0) { + ERROR("Error loading the seccomp policy"); + return -1; + } + return 0; +} diff --git a/src/lxc/start.c b/src/lxc/start.c index 19815fcab..23ce1d94e 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -353,6 +353,11 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf) goto out_free; } + if (lxc_read_seccomp_config(conf) != 0) { + ERROR("failed loading seccomp policy"); + goto out_free_name; + } + /* Begin the set the state to STARTING*/ if (lxc_set_state(name, handler, STARTING)) { ERROR("failed to set state '%s'", lxc_state2str(STARTING)); @@ -530,6 +535,9 @@ static int do_start(void *data) if (apparmor_load(handler) < 0) goto out_warn_father; + if (lxc_seccomp_load(handler->conf) != 0) + goto out_warn_father; + if (run_lxc_hooks(handler->name, "start", handler->conf)) { ERROR("failed to run start hooks for container '%s'.", handler->name); goto out_warn_father; From 09ad624693cb0ef41cf246e2876b5f356783b34d Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Thu, 9 Aug 2012 18:00:58 -0500 Subject: [PATCH 003/171] confile: support 'lxc.include' option to include other config files For instance lxc.include = /var/lib/lxc/commonopts in /var/lib/lxc/q1/config would cause the configuration in /var/lib/lxc/commonopts to be loaded when container q1 starts. Signed-off-by: Serge Hallyn --- src/lxc/confile.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 367a92cd0..2b5e73c91 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -76,6 +76,7 @@ static int config_network_ipv6_gateway(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 *); static int config_seccomp(const char *, char *, struct lxc_conf *); +static int config_includefile(const char *, char *, struct lxc_conf *); typedef int (*config_cb)(const char *, char *, struct lxc_conf *); @@ -120,6 +121,7 @@ static struct config config[] = { { "lxc.cap.drop", config_cap_drop }, { "lxc.console", config_console }, { "lxc.seccomp", config_seccomp }, + { "lxc.include", config_includefile }, }; static const size_t config_size = sizeof(config)/sizeof(struct config); @@ -894,6 +896,12 @@ static int config_console(const char *key, char *value, return 0; } +static int config_includefile(const char *key, char *value, + struct lxc_conf *lxc_conf) +{ + return lxc_config_read(value, lxc_conf); +} + static int config_rootfs(const char *key, char *value, struct lxc_conf *lxc_conf) { if (strlen(value) >= MAXPATHLEN) { From 0d0527a929bf9f35b4d99c100dfc76449d67a906 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Thu, 16 Aug 2012 15:07:51 -0500 Subject: [PATCH 004/171] seccomp: include lxcseccomp.h in start.c Signed-off-by: Serge Hallyn --- src/lxc/start.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lxc/start.c b/src/lxc/start.c index 23ce1d94e..60ac1096b 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -127,6 +127,7 @@ int signalfd(int fd, const sigset_t *mask, int flags) #include "sync.h" #include "namespace.h" #include "apparmor.h" +#include "lxcseccomp.h" lxc_log_define(lxc_start, lxc); From e767dd55995d20151987ca38eb00c8d43465ca41 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 17 Aug 2012 13:04:34 -0500 Subject: [PATCH 005/171] fix configure.ac for seccomp and apparmor Use --enable-XXX=check when not specified to get reasonable defaults. Signed-off-by: Serge Hallyn --- configure.ac | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/configure.ac b/configure.ac index a3b961663..066f56642 100644 --- a/configure.ac +++ b/configure.ac @@ -18,16 +18,6 @@ AC_ARG_ENABLE([rpath], AM_CONDITIONAL([ENABLE_RPATH], [test "x$enable_rpath" = "xyes"]) -AC_ARG_ENABLE([apparmor], - [AC_HELP_STRING([--enable-apparmor], [enable apparmor])], - [], [enable_apparmor=yes]) -AM_CONDITIONAL([ENABLE_APPARMOR], [test "x$enable_apparmor" = "xyes"]) - -AC_ARG_ENABLE([seccomp], - [AC_HELP_STRING([--enable-seccomp], [enable seccomp])], - [], [enable_seccomp=yes]) -AM_CONDITIONAL([ENABLE_SECCOMP], [test "x$enable_seccomp" = "xyes"]) - AC_ARG_ENABLE([doc], [AC_HELP_STRING([--enable-doc], [make mans (require docbook2man installed) [default=auto]])], [], [enable_doc=auto]) @@ -39,11 +29,31 @@ if test "x$enable_doc" = "xyes" -o "x$enable_doc" = "xauto"; then AC_MSG_ERROR([docbook2man required by man request, but not found]) fi +AC_ARG_ENABLE([apparmor], + [AC_HELP_STRING([--enable-apparmor], [enable apparmor])], + [], [enable_apparmor=check]) + +if test "$enable_apparmor" = "check" ; then + AC_CHECK_LIB([apparmor],[aa_change_profile],[enable_apparmor=yes], [enable_apparmor=no]) +fi + +AM_CONDITIONAL([ENABLE_APPARMOR], [test "x$enable_apparmor" = "xyes"]) + AM_COND_IF([ENABLE_APPARMOR], [AC_CHECK_HEADER([sys/apparmor.h],[],[AC_MSG_ERROR([You must install the AppArmor development package in order to compile lxc])]) AC_CHECK_LIB([apparmor], [aa_change_profile],[],[AC_MSG_ERROR([You must install the AppArmor development package in order to compile lxc])]) AC_SUBST([APPARMOR_LIBS], [-lapparmor])]) +AC_ARG_ENABLE([seccomp], + [AC_HELP_STRING([--enable-seccomp], [enable seccomp])], + [], [enable_seccomp=check]) + +if test "$enable_seccomp" = "check" ; then + AC_CHECK_LIB([seccomp],[seccomp_init],[enable_seccomp=yes],[enable_seccomp=no]) +fi + +AM_CONDITIONAL([ENABLE_SECCOMP], [test "x$enable_seccomp" = "xyes"]) + AM_COND_IF([ENABLE_SECCOMP], [AC_CHECK_HEADER([seccomp.h],[],[AC_MSG_ERROR([You must install the seccomp development package in order to compile lxc])]) AC_CHECK_LIB([seccomp], [seccomp_init],[],[AC_MSG_ERROR([You must install the seccomp development package in order to compile lxc])]) From c8dee0f165d19271bb0a83069dcbb65fbc02e73f Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 17 Aug 2012 14:06:34 -0500 Subject: [PATCH 006/171] Makefile.am: use right .h file name for seccomp Signed-off-by: Serge Hallyn --- src/lxc/Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 29dfa00d3..a4b63fa64 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -50,7 +50,7 @@ liblxc_so_SOURCES = \ genl.c genl.h \ \ caps.c caps.h \ - seccomp.c seccomp.h \ + lxcseccomp.h \ mainloop.c mainloop.h \ af_unix.c af_unix.h \ \ @@ -68,6 +68,7 @@ endif if ENABLE_SECCOMP AM_CFLAGS += -DHAVE_SECCOMP +liblxc_so_SOURCES += seccomp.c endif liblxc_so_CFLAGS = -fPIC -DPIC $(AM_CFLAGS) From 74a2b5864f2ece87bf522d1c1cbd590dc24c0c53 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 9 Jul 2012 19:15:48 +0200 Subject: [PATCH 007/171] Add network-down script Analogously to lxc.network.script.up, add the ability to register a down script. It is called before the guest network is finally destroyed, allowing to clean up resources that are not reset/destroyed automatically. Parameters of the down script are identical to the up script except for the execution context "down". Signed-off-by: Jan Kiszka Signed-off-by: Serge Hallyn --- doc/lxc.conf.sgml.in | 20 ++++++++++ src/lxc/conf.c | 94 +++++++++++++++++++++++++++++++++++++++++--- src/lxc/conf.h | 6 ++- src/lxc/confile.c | 5 +++ src/lxc/start.c | 7 +++- 5 files changed, 124 insertions(+), 8 deletions(-) diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in index 05d6bc7f7..80c4335e3 100644 --- a/doc/lxc.conf.sgml.in +++ b/doc/lxc.conf.sgml.in @@ -374,6 +374,26 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + + + + + add a configuration option to specify a script to be + executed before destroying the network used from the + host side. The following arguments are passed to the + script: container name and config section name (net) + Additional arguments depend on the config section + employing a script hook; the following are used by the + network system: execution context (down), network type + (empty/veth/macvlan/phys), Depending on the network + type, other arguments may be passed: + veth/macvlan/phys. And finally (host-sided) device name. + + + diff --git a/src/lxc/conf.c b/src/lxc/conf.c index b437654a3..79fe4ad5a 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -138,6 +138,20 @@ static instanciate_cb netdev_conf[LXC_NET_MAXCONFTYPE + 1] = { [LXC_NET_EMPTY] = instanciate_empty, }; +static int shutdown_veth(struct lxc_handler *, struct lxc_netdev *); +static int shutdown_macvlan(struct lxc_handler *, struct lxc_netdev *); +static int shutdown_vlan(struct lxc_handler *, struct lxc_netdev *); +static int shutdown_phys(struct lxc_handler *, struct lxc_netdev *); +static int shutdown_empty(struct lxc_handler *, struct lxc_netdev *); + +static instanciate_cb netdev_deconf[LXC_NET_MAXCONFTYPE + 1] = { + [LXC_NET_VETH] = shutdown_veth, + [LXC_NET_MACVLAN] = shutdown_macvlan, + [LXC_NET_VLAN] = shutdown_vlan, + [LXC_NET_PHYS] = shutdown_phys, + [LXC_NET_EMPTY] = shutdown_empty, +}; + static struct mount_opt mount_opt[] = { { "defaults", 0, 0 }, { "ro", 0, MS_RDONLY }, @@ -1765,6 +1779,8 @@ static int instanciate_veth(struct lxc_handler *handler, struct lxc_netdev *netd return -1; } veth1 = mktemp(veth1buf); + /* store away for deconf */ + memcpy(netdev->priv.veth_attr.veth1, veth1, IFNAMSIZ); } snprintf(veth2buf, sizeof(veth2buf), "vethXXXXXX"); @@ -1841,6 +1857,25 @@ out_delete: return -1; } +static int shutdown_veth(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + char *veth1; + int err; + + if (netdev->priv.veth_attr.pair) + veth1 = netdev->priv.veth_attr.pair; + else + veth1 = netdev->priv.veth_attr.veth1; + + if (netdev->downscript) { + err = run_script(handler->name, "net", netdev->downscript, + "down", "veth", veth1, (char*) NULL); + if (err) + return -1; + } + return 0; +} + static int instanciate_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev) { char peerbuf[IFNAMSIZ], *peer; @@ -1889,6 +1924,20 @@ static int instanciate_macvlan(struct lxc_handler *handler, struct lxc_netdev *n return 0; } +static int shutdown_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + int err; + + if (netdev->downscript) { + err = run_script(handler->name, "net", netdev->downscript, + "down", "macvlan", netdev->link, + (char*) NULL); + if (err) + return -1; + } + return 0; +} + /* XXX: merge with instanciate_macvlan */ static int instanciate_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev) { @@ -1926,6 +1975,11 @@ static int instanciate_vlan(struct lxc_handler *handler, struct lxc_netdev *netd return 0; } +static int shutdown_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + return 0; +} + static int instanciate_phys(struct lxc_handler *handler, struct lxc_netdev *netdev) { if (!netdev->link) { @@ -1950,6 +2004,19 @@ static int instanciate_phys(struct lxc_handler *handler, struct lxc_netdev *netd return 0; } +static int shutdown_phys(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + int err; + + if (netdev->downscript) { + err = run_script(handler->name, "net", netdev->downscript, + "down", "phys", netdev->link, (char*) NULL); + if (err) + return -1; + } + return 0; +} + static int instanciate_empty(struct lxc_handler *handler, struct lxc_netdev *netdev) { netdev->ifindex = 0; @@ -1963,6 +2030,19 @@ static int instanciate_empty(struct lxc_handler *handler, struct lxc_netdev *net return 0; } +static int shutdown_empty(struct lxc_handler *handler, struct lxc_netdev *netdev) +{ + int err; + + if (netdev->downscript) { + err = run_script(handler->name, "net", netdev->downscript, + "down", "empty", (char*) NULL); + if (err) + return -1; + } + return 0; +} + int lxc_create_network(struct lxc_handler *handler) { struct lxc_list *network = &handler->conf->network; @@ -1989,28 +2069,32 @@ int lxc_create_network(struct lxc_handler *handler) return 0; } -void lxc_delete_network(struct lxc_list *network) +void lxc_delete_network(struct lxc_handler *handler) { + struct lxc_list *network = &handler->conf->network; struct lxc_list *iterator; struct lxc_netdev *netdev; lxc_list_for_each(iterator, network) { netdev = iterator->elem; - if (netdev->ifindex == 0) - continue; - if (netdev->type == LXC_NET_PHYS) { + if (netdev->ifindex != 0 && netdev->type == LXC_NET_PHYS) { if (lxc_netdev_rename_by_index(netdev->ifindex, netdev->link)) WARN("failed to rename to the initial name the " \ "netdev '%s'", netdev->link); continue; } + if (netdev_deconf[netdev->type](handler, netdev)) { + WARN("failed to destroy netdev"); + } + /* Recent kernel remove the virtual interfaces when the network * namespace is destroyed but in case we did not moved the * interface to the network namespace, we have to destroy it */ - if (lxc_netdev_delete_by_index(netdev->ifindex)) + if (netdev->ifindex != 0 && + lxc_netdev_delete_by_index(netdev->ifindex)) WARN("failed to remove interface '%s'", netdev->name); } } diff --git a/src/lxc/conf.h b/src/lxc/conf.h index ad48042af..92efc585e 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -24,6 +24,7 @@ #define _conf_h #include +#include #include #include @@ -76,6 +77,7 @@ struct lxc_route6 { struct ifla_veth { char *pair; /* pair name */ + char veth1[IFNAMSIZ]; /* needed for deconf */ }; struct ifla_vlan { @@ -103,6 +105,7 @@ union netdev_p { * @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 * @upscript : a script filename to be executed during interface configuration + * @downscript : a script filename to be executed during interface destruction */ struct lxc_netdev { int type; @@ -120,6 +123,7 @@ struct lxc_netdev { struct in6_addr *ipv6_gateway; bool ipv6_gateway_auto; char *upscript; + char *downscript; }; /* @@ -242,7 +246,7 @@ extern struct lxc_conf *lxc_conf_init(void); extern int pin_rootfs(const char *rootfs); extern int lxc_create_network(struct lxc_handler *handler); -extern void lxc_delete_network(struct lxc_list *networks); +extern void lxc_delete_network(struct lxc_handler *handler); extern int lxc_assign_network(struct lxc_list *networks, pid_t pid); extern int lxc_find_gateway_addresses(struct lxc_handler *handler); diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 2b5e73c91..9e51a6392 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -111,6 +111,7 @@ static struct config config[] = { { "lxc.network.macvlan.mode", config_network_macvlan_mode }, { "lxc.network.veth.pair", config_network_veth_pair }, { "lxc.network.script.up", config_network_script }, + { "lxc.network.script.down", config_network_script }, { "lxc.network.hwaddr", config_network_hwaddr }, { "lxc.network.mtu", config_network_mtu }, { "lxc.network.vlan.id", config_network_vlan_id }, @@ -595,6 +596,10 @@ static int config_network_script(const char *key, char *value, netdev->upscript = copy; return 0; } + if (strcmp(key, "lxc.network.script.down") == 0) { + netdev->downscript = copy; + return 0; + } SYSERROR("Unknown key: %s", key); free(copy); return -1; diff --git a/src/lxc/start.c b/src/lxc/start.c index 60ac1096b..77755b979 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -652,7 +652,7 @@ int lxc_spawn(struct lxc_handler *handler) out_delete_net: if (clone_flags & CLONE_NEWNET) - lxc_delete_network(&handler->conf->network); + lxc_delete_network(handler); out_abort: lxc_abort(name, handler); lxc_sync_fini(handler); @@ -684,7 +684,7 @@ int __lxc_start(const char *name, struct lxc_conf *conf, err = lxc_spawn(handler); if (err) { ERROR("failed to spawn '%s'", name); - goto out_fini; + goto out_fini_nonet; } err = lxc_poll(name, handler); @@ -719,6 +719,9 @@ int __lxc_start(const char *name, struct lxc_conf *conf, err = lxc_error_set_and_log(handler->pid, status); out_fini: + lxc_delete_network(handler); + +out_fini_nonet: lxc_cgroup_destroy(name); lxc_fini(name, handler); return err; From 1881820ae4ff9004beef1bf7f04553580840441d Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 21 Aug 2012 10:11:23 -0500 Subject: [PATCH 008/171] lxc-create: Make location of container rootfs configurable Make 'dir' an explicit backing store type, which accepts '--dir rootfs' as an option to specify a custom location for the container rootfs. Also update lxc-destroy to now remove the rootfs separately, as removing @LXCPATH@/$name may not hit it. Signed-off-by: Serge Hallyn --- doc/lxc-create.sgml.in | 6 +++++- src/lxc/lxc-create.in | 26 +++++++++++++++++++++++--- src/lxc/lxc-destroy.in | 3 ++- templates/lxc-altlinux.in | 7 ++++++- templates/lxc-archlinux.in | 6 +++++- templates/lxc-busybox.in | 18 ++++++++++++++++-- templates/lxc-debian.in | 11 +++++++++-- templates/lxc-fedora.in | 2 +- templates/lxc-lenny.in | 10 ++++++++-- templates/lxc-opensuse.in | 10 ++++++++-- templates/lxc-sshd.in | 10 ++++++++-- templates/lxc-ubuntu-cloud.in | 10 ++++++++-- templates/lxc-ubuntu.in | 10 ++++++++-- 13 files changed, 107 insertions(+), 22 deletions(-) diff --git a/doc/lxc-create.sgml.in b/doc/lxc-create.sgml.in index 0ffb21c24..f4f4e592f 100644 --- a/doc/lxc-create.sgml.in +++ b/doc/lxc-create.sgml.in @@ -123,9 +123,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - 'backingstore' is one of 'none', 'lvm', or 'btrfs'. The + 'backingstore' is one of 'none', 'dir', 'lvm', or 'btrfs'. The default is 'none', meaning that the container root filesystem will be a directory under @LXCPATH@/container/rootfs. + 'dir' has the same meaning as 'none', but also allows the optional + --dir ROOTFS to be specified, meaning + that the container rootfs should be placed under the specified path, + rather than the default. The option 'btrfs' need not be specified as it will be used automatically if the @LXCPATH@ filesystem is found to be btrfs. If backingstore is 'lvm', then an lvm block device will be diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in index b21cdc301..79c67d627 100644 --- a/src/lxc/lxc-create.in +++ b/src/lxc/lxc-create.in @@ -26,6 +26,7 @@ usage() { echo >&2 echo "where FS_OPTIONS is one of:" >&2 echo " -B none" >&2 + echo " -B dir [--dir rootfs_dir]" >&2 echo " -B lvm [--lvname LV_NAME] [--vgname VG_NAME] [--fstype FS_TYPE]" >&2 echo " [--fssize FS_SIZE]" >&2 echo " -B btrfs" >&2 @@ -43,6 +44,7 @@ help() { echo " -B BACKING_STORE alter the container backing store (default: none)" >&2 echo " --lvname LV_NAME specify the LVM logical volume name" >&2 echo " (default: container name)" >&2 + echo " --dir ROOTFS_DIR specify path for custom rootfs directory location" >&2 echo " --vgname VG_NAME specify the LVM volume group name (default: lxc)" >&2 echo " --fstype FS_TYPE specify the filesystem type (default: ext4)" >&2 echo " --fssize FS_SIZE specify the filesystem size (default: 500M)" >&2 @@ -61,7 +63,7 @@ help() { } shortoptions='hn:f:t:B:' -longoptions='help,name:,config:,template:,backingstore:,fstype:,lvname:,vgname:,fssize:' +longoptions='help,name:,config:,template:,backingstore:,fstype:,dir:,lvname:,vgname:,fssize:' lxc_path=@LXCPATH@ bindir=@BINDIR@ templatedir=@LXCTEMPLATEDIR@ @@ -69,6 +71,7 @@ backingstore=_unset fstype=ext4 fssize=500M vgname=lxc +custom_rootfs="" getopt=$(getopt -o $shortoptions --longoptions $longoptions -- "$@") if [ $? != 0 ]; then @@ -104,6 +107,11 @@ while true; do backingstore=$1 shift ;; + --dir) + shift + custom_rootfs=$1 + shift + ;; --lvname) shift lvname=$1 @@ -171,9 +179,13 @@ if [ "$(id -u)" != "0" ]; then exit 1 fi +if [ -n "$custom_rootfs" -a "$backingstore" != "dir" ]; then + echo "--dir is only valid with -B dir" +fi + case "$backingstore" in - lvm|none|btrfs|_unset) :;; - *) echo "$(basename $0): '$backingstore' is not known (try 'none', 'lvm', 'btrfs')" >&2 + dir|lvm|none|btrfs|_unset) :;; + *) echo "$(basename $0): '$backingstore' is not known (try 'none', 'dir', 'lvm', 'btrfs')" >&2 usage exit 1 ;; @@ -258,6 +270,14 @@ else cp $lxc_config $lxc_path/$lxc_name/config fi +if [ -n "$custom_rootfs" ]; then + if grep -q "lxc.rootfs" $lxc_path/$lxc_name/config ; then + echo "configuration file already specifies a lxc.rootfs" + exit 1 + fi + echo "lxc.rootfs = $custom_rootfs" >> $lxc_path/$lxc_name/config +fi + # Create the fs as needed if [ $backingstore = "lvm" ]; then [ -d "$rootfs" ] || mkdir $rootfs diff --git a/src/lxc/lxc-destroy.in b/src/lxc/lxc-destroy.in index 846266cad..ecff08325 100644 --- a/src/lxc/lxc-destroy.in +++ b/src/lxc/lxc-destroy.in @@ -110,7 +110,7 @@ fi # else, ignore it. We'll support deletion of others later. rootdev=`grep lxc.rootfs $lxc_path/$lxc_name/config 2>/dev/null | sed -e 's/^[^/]*/\//'` if [ -n "$rootdev" ]; then - if [ -b "$rootdev" ]; then + if [ -b "$rootdev" -o -h "$rootdev" ]; then lvdisplay $rootdev > /dev/null 2>&1 if [ $? -eq 0 ]; then echo "removing backing store: $rootdev" @@ -126,5 +126,6 @@ if [ -n "$rootdev" ]; then fi fi fi + # recursively remove the container to remove old container configuration rm -rf --one-file-system --preserve-root $lxc_path/$lxc_name diff --git a/templates/lxc-altlinux.in b/templates/lxc-altlinux.in index 2d2274a33..0bf973541 100644 --- a/templates/lxc-altlinux.in +++ b/templates/lxc-altlinux.in @@ -239,11 +239,11 @@ copy_configuration() { mkdir -p $config_path + grep -q "^lxc.rootfs" $config_path/config 2>/dev/null || echo "lxc.rootfs = $rootfs_path" >> $config_path/config cat <> $config_path/config lxc.utsname = $name lxc.tty = 4 lxc.pts = 1024 -lxc.rootfs = $rootfs_path lxc.mount = $config_path/fstab # When using LXC with apparmor, uncomment the next line to run unconfined: @@ -433,6 +433,11 @@ if [ -f $config_path/config ]; then exit 1 fi +# check for 'lxc.rootfs' passed in through default config by lxc-create +if grep -q '^lxc.rootfs' $path/config 2>/dev/null ; then + rootfs_path=`grep 'lxc.rootfs =' $path/config | awk -F= '{ print $2 }'` +fi + install_altlinux if [ $? -ne 0 ]; then echo "failed to install altlinux" diff --git a/templates/lxc-archlinux.in b/templates/lxc-archlinux.in index 18ec0642d..639d81a38 100644 --- a/templates/lxc-archlinux.in +++ b/templates/lxc-archlinux.in @@ -218,11 +218,11 @@ EOF # write container configuration files function copy_configuration { mkdir -p "${config_path}" + grep -q "^lxc.rootfs" "${config_path}/config" 2>/dev/null || echo "lxc.rootfs=${rootfs_path}" >> "${config_path}/config" cat > "${config_path}/config" << EOF lxc.utsname=${name} lxc.tty=4 lxc.pts=1024 -lxc.rootfs=${rootfs_path} lxc.mount=${config_path}/fstab # When using LXC with apparmor, uncomment the next line to run unconfined: @@ -423,6 +423,10 @@ if [ "${EUID}" != "0" ]; then fi rootfs_path="${path}/rootfs" +# check for 'lxc.rootfs' passed in through default config by lxc-create +if grep -q '^lxc.rootfs' $path/config 2>/dev/null ; then + rootfs_path=`grep 'lxc.rootfs =' $path/config | awk -F= '{ print $2 }'` +fi config_path="${default_path}/${name}" revert() diff --git a/templates/lxc-busybox.in b/templates/lxc-busybox.in index 06d2f8916..f6e8b5a9a 100644 --- a/templates/lxc-busybox.in +++ b/templates/lxc-busybox.in @@ -228,11 +228,19 @@ copy_configuration() rootfs=$2 name=$3 +grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config cat <> $path/config lxc.utsname = $name lxc.tty = 1 lxc.pts = 1 -lxc.rootfs = $rootfs +EOF + +if [ -d "$rootfs/lib" ]; then +cat <> $path/config +lxc.mount.entry=/lib $rootfs/lib none ro,bind 0 0 +lxc.mount.entry=/usr/lib $rootfs/usr/lib none ro,bind 0 0 +EOF +fi # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined @@ -287,7 +295,13 @@ if [ -z "$path" ]; then exit 1 fi -rootfs=$path/rootfs +# detect rootfs +config="$path/config" +if grep -q '^lxc.rootfs' $config 2>/dev/null ; then + rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'` +else + rootfs=$path/rootfs +fi install_busybox $rootfs $name if [ $? -ne 0 ]; then diff --git a/templates/lxc-debian.in b/templates/lxc-debian.in index 0c4d2c504..03a4c6aa2 100644 --- a/templates/lxc-debian.in +++ b/templates/lxc-debian.in @@ -202,10 +202,10 @@ copy_configuration() rootfs=$2 hostname=$3 + grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config cat <> $path/config lxc.tty = 4 lxc.pts = 1024 -lxc.rootfs = $rootfs lxc.utsname = $hostname # When using LXC with apparmor, uncomment the next line to run unconfined: @@ -312,7 +312,14 @@ if [ "$(id -u)" != "0" ]; then exit 1 fi -rootfs=$path/rootfs +# detect rootfs +config="$path/config" +if grep -q '^lxc.rootfs' $config 2>/dev/null ; then + rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'` +else + rootfs=$path/rootfs +fi + install_debian $rootfs if [ $? -ne 0 ]; then diff --git a/templates/lxc-fedora.in b/templates/lxc-fedora.in index c2d3143d1..edd4b9dc9 100644 --- a/templates/lxc-fedora.in +++ b/templates/lxc-fedora.in @@ -243,11 +243,11 @@ copy_configuration() { mkdir -p $config_path + grep -q "^lxc.rootfs" $config_path/config 2>/dev/null || echo "lxc.rootfs = $rootfs_path" >> $config_path/config cat <> $config_path/config lxc.utsname = $name lxc.tty = 4 lxc.pts = 1024 -lxc.rootfs = $rootfs_path lxc.mount = $config_path/fstab # When using LXC with apparmor, uncomment the next line to run unconfined: diff --git a/templates/lxc-lenny.in b/templates/lxc-lenny.in index de03f01d8..46de1eaeb 100644 --- a/templates/lxc-lenny.in +++ b/templates/lxc-lenny.in @@ -178,10 +178,10 @@ copy_configuration() rootfs=$2 name=$3 + grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config cat <> $path/config lxc.tty = 4 lxc.pts = 1024 -lxc.rootfs = $rootfs lxc.cgroup.devices.deny = a # When using LXC with apparmor, uncomment the next line to run unconfined: @@ -287,7 +287,13 @@ if [ "$(id -u)" != "0" ]; then exit 1 fi -rootfs=$path/rootfs +# detect rootfs +config="$path/config" +if grep -q '^lxc.rootfs' $config 2>/dev/null ; then + rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'` +else + rootfs=$path/rootfs +fi install_debian $rootfs if [ $? -ne 0 ]; then diff --git a/templates/lxc-opensuse.in b/templates/lxc-opensuse.in index d728af312..32424515d 100644 --- a/templates/lxc-opensuse.in +++ b/templates/lxc-opensuse.in @@ -254,12 +254,12 @@ copy_configuration() rootfs=$2 name=$3 + grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config cat <> $path/config lxc.utsname = $name lxc.tty = 4 lxc.pts = 1024 -lxc.rootfs = $rootfs lxc.mount = $path/fstab # When using LXC with apparmor, uncomment the next line to run unconfined: @@ -367,7 +367,13 @@ if [ "$(id -u)" != "0" ]; then exit 1 fi -rootfs=$path/rootfs +# detect rootfs +config="$path/config" +if grep -q '^lxc.rootfs' $config 2>/dev/null ; then + rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'` +else + rootfs=$path/rootfs +fi install_opensuse $rootfs if [ $? -ne 0 ]; then diff --git a/templates/lxc-sshd.in b/templates/lxc-sshd.in index d456fbbb8..4f8d50407 100644 --- a/templates/lxc-sshd.in +++ b/templates/lxc-sshd.in @@ -108,10 +108,10 @@ copy_configuration() rootfs=$2 name=$3 + grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config cat <> $path/config lxc.utsname = $name lxc.pts = 1024 -lxc.rootfs = $rootfs # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined @@ -207,7 +207,13 @@ if [ -z "$path" ]; then exit 1 fi -rootfs=$path/rootfs +# detect rootfs +config="$path/config" +if grep -q '^lxc.rootfs' $config 2>/dev/null ; then + rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'` +else + rootfs=$path/rootfs +fi install_sshd $rootfs if [ $? -ne 0 ]; then diff --git a/templates/lxc-ubuntu-cloud.in b/templates/lxc-ubuntu-cloud.in index 349830999..8df05da11 100644 --- a/templates/lxc-ubuntu-cloud.in +++ b/templates/lxc-ubuntu-cloud.in @@ -46,12 +46,12 @@ lxc.network.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$// EOF fi + grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config cat <> $path/config lxc.utsname = $name lxc.tty = 4 lxc.pts = 1024 -lxc.rootfs = $rootfs lxc.mount = $path/fstab lxc.arch = $arch lxc.cap.drop = sys_module mac_admin @@ -249,7 +249,13 @@ if [ "$(id -u)" != "0" ]; then exit 1 fi -rootfs=$path/rootfs +# detect rootfs +config="$path/config" +if grep -q '^lxc.rootfs' $config 2>/dev/null ; then + rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'` +else + rootfs=$path/rootfs +fi type ubuntu-cloudimg-query type wget diff --git a/templates/lxc-ubuntu.in b/templates/lxc-ubuntu.in index aca211843..90021fa8e 100644 --- a/templates/lxc-ubuntu.in +++ b/templates/lxc-ubuntu.in @@ -303,13 +303,13 @@ lxc.network.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$// EOF fi + grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config cat <> $path/config lxc.utsname = $name lxc.devttydir =$ttydir lxc.tty = 4 lxc.pts = 1024 -lxc.rootfs = $rootfs lxc.mount = $path/fstab lxc.arch = $arch lxc.cap.drop = sys_module mac_admin mac_override @@ -670,7 +670,13 @@ if [ "$(id -u)" != "0" ]; then exit 1 fi -rootfs=$path/rootfs +# detect rootfs +config="$path/config" +if grep -q '^lxc.rootfs' $config 2>/dev/null ; then + rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'` +else + rootfs=$path/rootfs +fi install_ubuntu $rootfs $release $flushcache if [ $? -ne 0 ]; then From d5088cf2d3c077444aa28194340db2f23b94035a Mon Sep 17 00:00:00 2001 From: Christian Seiler Date: Wed, 22 Aug 2012 00:03:11 +0200 Subject: [PATCH 009/171] lxc-start: Add command to retrieve the clone flags used to start the container. Add the LXC_COMMAND_CLONE_FLAGS that retrieves the flags passed to clone(2) when the container was started. This allows external programs to determine which namespaces the container was unshared from. Signed-off-by: Christian Seiler Cc: Daniel Lezcano Acked-by: Serge Hallyn --- src/lxc/commands.c | 30 ++++++++++++++++++++++++++---- src/lxc/commands.h | 2 ++ src/lxc/start.c | 34 ++++++++++++++++++++++++++++------ src/lxc/start.h | 1 + 4 files changed, 57 insertions(+), 10 deletions(-) diff --git a/src/lxc/commands.c b/src/lxc/commands.c index cce24db0f..dc9381531 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -154,11 +154,32 @@ pid_t get_init_pid(const char *name) return command.answer.pid; } +int lxc_get_clone_flags(const char *name) +{ + struct lxc_command command = { + .request = { .type = LXC_COMMAND_CLONE_FLAGS }, + }; + + int ret, stopped = 0; + + ret = lxc_command(name, &command, &stopped); + if (ret < 0 && stopped) + return -1; + + if (ret < 0) { + ERROR("failed to send command"); + return -1; + } + + return command.answer.ret; +} + extern void lxc_console_remove_fd(int, struct lxc_tty_info *); extern int lxc_console_callback(int, struct lxc_request *, struct lxc_handler *); extern int lxc_stop_callback(int, struct lxc_request *, struct lxc_handler *); extern int lxc_state_callback(int, struct lxc_request *, struct lxc_handler *); extern int lxc_pid_callback(int, struct lxc_request *, struct lxc_handler *); +extern int lxc_clone_flags_callback(int, struct lxc_request *, struct lxc_handler *); static int trigger_command(int fd, struct lxc_request *request, struct lxc_handler *handler) @@ -166,10 +187,11 @@ static int trigger_command(int fd, struct lxc_request *request, typedef int (*callback)(int, struct lxc_request *, struct lxc_handler *); callback cb[LXC_COMMAND_MAX] = { - [LXC_COMMAND_TTY] = lxc_console_callback, - [LXC_COMMAND_STOP] = lxc_stop_callback, - [LXC_COMMAND_STATE] = lxc_state_callback, - [LXC_COMMAND_PID] = lxc_pid_callback, + [LXC_COMMAND_TTY] = lxc_console_callback, + [LXC_COMMAND_STOP] = lxc_stop_callback, + [LXC_COMMAND_STATE] = lxc_state_callback, + [LXC_COMMAND_PID] = lxc_pid_callback, + [LXC_COMMAND_CLONE_FLAGS] = lxc_clone_flags_callback, }; if (request->type < 0 || request->type >= LXC_COMMAND_MAX) diff --git a/src/lxc/commands.h b/src/lxc/commands.h index d5c013fc7..3b0ac9a65 100644 --- a/src/lxc/commands.h +++ b/src/lxc/commands.h @@ -28,6 +28,7 @@ enum { LXC_COMMAND_STOP, LXC_COMMAND_STATE, LXC_COMMAND_PID, + LXC_COMMAND_CLONE_FLAGS, LXC_COMMAND_MAX, }; @@ -48,6 +49,7 @@ struct lxc_command { }; extern pid_t get_init_pid(const char *name); +extern int lxc_get_clone_flags(const char *name); extern int lxc_command(const char *name, struct lxc_command *command, int *stopped); diff --git a/src/lxc/start.c b/src/lxc/start.c index 77755b979..3e26b27be 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -279,6 +279,29 @@ int lxc_pid_callback(int fd, struct lxc_request *request, return 0; } +int lxc_clone_flags_callback(int fd, struct lxc_request *request, + struct lxc_handler *handler) +{ + struct lxc_answer answer; + int ret; + + answer.pid = 0; + answer.ret = handler->clone_flags; + + ret = send(fd, &answer, sizeof(answer), 0); + if (ret < 0) { + WARN("failed to send answer to the peer"); + return -1; + } + + if (ret != sizeof(answer)) { + ERROR("partial answer sent"); + return -1; + } + + return 0; +} + int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_state_t state) { handler->state = state; @@ -558,7 +581,6 @@ out_warn_father: int lxc_spawn(struct lxc_handler *handler) { - int clone_flags; int failed_before_rename = 0; const char *name = handler->name; int pinfd; @@ -566,10 +588,10 @@ int lxc_spawn(struct lxc_handler *handler) if (lxc_sync_init(handler)) return -1; - clone_flags = CLONE_NEWUTS|CLONE_NEWPID|CLONE_NEWIPC|CLONE_NEWNS; + handler->clone_flags = CLONE_NEWUTS|CLONE_NEWPID|CLONE_NEWIPC|CLONE_NEWNS; if (!lxc_list_empty(&handler->conf->network)) { - clone_flags |= CLONE_NEWNET; + handler->clone_flags |= CLONE_NEWNET; /* Find gateway addresses from the link device, which is * no longer accessible inside the container. Do this @@ -603,7 +625,7 @@ int lxc_spawn(struct lxc_handler *handler) } /* Create a process in a new set of namespaces */ - handler->pid = lxc_clone(do_start, handler, clone_flags); + handler->pid = lxc_clone(do_start, handler, handler->clone_flags); if (handler->pid < 0) { SYSERROR("failed to fork into a new namespace"); goto out_delete_net; @@ -621,7 +643,7 @@ int lxc_spawn(struct lxc_handler *handler) goto out_delete_net; /* Create the network configuration */ - if (clone_flags & CLONE_NEWNET) { + if (handler->clone_flags & CLONE_NEWNET) { if (lxc_assign_network(&handler->conf->network, handler->pid)) { ERROR("failed to create the configured network"); goto out_delete_net; @@ -651,7 +673,7 @@ int lxc_spawn(struct lxc_handler *handler) return 0; out_delete_net: - if (clone_flags & CLONE_NEWNET) + if (handler->clone_flags & CLONE_NEWNET) lxc_delete_network(handler); out_abort: lxc_abort(name, handler); diff --git a/src/lxc/start.h b/src/lxc/start.h index 0e12abaed..4b2e2b54e 100644 --- a/src/lxc/start.h +++ b/src/lxc/start.h @@ -39,6 +39,7 @@ struct lxc_handler { pid_t pid; char *name; lxc_state_t state; + int clone_flags; int sigfd; sigset_t oldmask; struct lxc_conf *conf; From c8f7c5630e8312cab7e17539381f05dffe9193fa Mon Sep 17 00:00:00 2001 From: Christian Seiler Date: Wed, 22 Aug 2012 00:03:12 +0200 Subject: [PATCH 010/171] lxc-attach: Remodel cgroup attach logic and attach to namespaces again in parent process With the introduction of lxc-attach's functionality to attach to cgroups, the setns() calls were put in the child process after the fork() and not the parent process before the fork() so the parent process remained outside the namespaces and could add the child to the correct cgroup. Unfortunately, the pid namespace really affects only children of the current process and not the process itself, which has several drawbacks: The attached program does not have a pid inside the container and the context that is used when remounting /proc from that process is wrong. Thus, the previous logic of first setting the namespaces and then forking so the child process (which then exec()s to the desired program) is a real member of the container. However, inside the container, there is no guarantee that the cgroup filesystem is still be mounted and that we are allowed to write to it (which is why the setns() was moved in the first place). To work around both problems, we separate the cgroup attach functionality into two parts: Preparing the attach process, which just opens the tasks files of all cgroups and keeps the file descriptors open and the writing to those fds part. This allows us to open all the tasks files in lxc_attach, then call setns(), then fork, in the child process close them completely and in the parent process just write the pid of the child process to all those fds. Signed-off-by: Christian Seiler Cc: Daniel Lezcano Acked-by: Serge Hallyn --- src/lxc/cgroup.c | 153 ++++++++++++++++++++++++++++++++++++++----- src/lxc/cgroup.h | 3 + src/lxc/lxc_attach.c | 62 +++++++++++++----- 3 files changed, 187 insertions(+), 31 deletions(-) diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c index 69ba4e5d1..a02ebc2fa 100644 --- a/src/lxc/cgroup.c +++ b/src/lxc/cgroup.c @@ -254,13 +254,38 @@ static int cgroup_enable_clone_children(const char *path) return ret; } -static int lxc_one_cgroup_attach(const char *name, - struct mntent *mntent, pid_t pid) +static int lxc_one_cgroup_finish_attach(int fd, pid_t pid) { - FILE *f; + char buf[32]; + int ret; + + snprintf(buf, 32, "%ld", (long)pid); + + ret = write(fd, buf, strlen(buf)); + if (ret <= 0) { + SYSERROR("failed to write pid '%ld' to fd '%d'", (long)pid, fd); + ret = -1; + } else { + ret = 0; + } + + close(fd); + return ret; +} + +static int lxc_one_cgroup_dispose_attach(int fd) +{ + close(fd); + return 0; +} + +static int lxc_one_cgroup_prepare_attach(const char *name, + struct mntent *mntent) +{ + int fd; char tasks[MAXPATHLEN], initcgroup[MAXPATHLEN]; char *cgmnt = mntent->mnt_dir; - int flags, ret = 0; + int flags; int rc; flags = get_cgroup_flags(mntent); @@ -274,31 +299,83 @@ static int lxc_one_cgroup_attach(const char *name, return -1; } - f = fopen(tasks, "w"); - if (!f) { + fd = open(tasks, O_WRONLY); + if (fd < 0) { SYSERROR("failed to open '%s'", tasks); return -1; } - if (fprintf(f, "%d", pid) <= 0) { - SYSERROR("failed to write pid '%d' to '%s'", pid, tasks); - ret = -1; + return fd; +} + +static int lxc_one_cgroup_attach(const char *name, struct mntent *mntent, pid_t pid) +{ + int fd; + + fd = lxc_one_cgroup_prepare_attach(name, mntent); + if (fd < 0) { + return -1; } - fclose(f); + return lxc_one_cgroup_finish_attach(fd, pid); +} + +int lxc_cgroup_dispose_attach(void *data) +{ + int *fds = data; + int ret, err; + + if (!fds) { + return 0; + } + + ret = 0; + + for (; *fds >= 0; fds++) { + err = lxc_one_cgroup_dispose_attach(*fds); + if (err) { + ret = err; + } + } + + free(data); return ret; } -/* - * for each mounted cgroup, attach a pid to the cgroup for the container - */ -int lxc_cgroup_attach(const char *name, pid_t pid) +int lxc_cgroup_finish_attach(void *data, pid_t pid) +{ + int *fds = data; + int err; + + if (!fds) { + return 0; + } + + for (; *fds >= 0; fds++) { + err = lxc_one_cgroup_finish_attach(*fds, pid); + if (err) { + /* get rid of the rest of them */ + lxc_cgroup_dispose_attach(data); + return -1; + } + *fds = -1; + } + + free(data); + + return 0; +} + +int lxc_cgroup_prepare_attach(const char *name, void **data) { struct mntent *mntent; FILE *file = NULL; int err = -1; int found = 0; + int *fds; + int i; + static const int MAXFDS = 256; file = setmntent(MTAB, "r"); if (!file) { @@ -306,7 +383,29 @@ int lxc_cgroup_attach(const char *name, pid_t pid) return -1; } + /* create a large enough buffer for all practical + * use cases + */ + fds = malloc(sizeof(int) * MAXFDS); + if (!fds) { + err = -1; + goto out; + } + for (i = 0; i < MAXFDS; i++) { + fds[i] = -1; + } + + err = 0; + i = 0; while ((mntent = getmntent(file))) { + if (i >= MAXFDS - 1) { + ERROR("too many cgroups to attach to, aborting"); + lxc_cgroup_dispose_attach(fds); + errno = ENOMEM; + err = -1; + goto out; + } + DEBUG("checking '%s' (%s)", mntent->mnt_dir, mntent->mnt_type); if (strcmp(mntent->mnt_type, "cgroup")) @@ -317,19 +416,41 @@ int lxc_cgroup_attach(const char *name, pid_t pid) INFO("[%d] found cgroup mounted at '%s',opts='%s'", ++found, mntent->mnt_dir, mntent->mnt_opts); - err = lxc_one_cgroup_attach(name, mntent, pid); - if (err) + fds[i] = lxc_one_cgroup_prepare_attach(name, mntent); + if (fds[i] < 0) { + err = fds[i]; + lxc_cgroup_dispose_attach(fds); goto out; + } + i++; }; if (!found) ERROR("No cgroup mounted on the system"); + *data = fds; + out: endmntent(file); return err; } +/* + * for each mounted cgroup, attach a pid to the cgroup for the container + */ +int lxc_cgroup_attach(const char *name, pid_t pid) +{ + void *data = NULL; + int ret; + + ret = lxc_cgroup_prepare_attach(name, &data); + if (ret < 0) { + return ret; + } + + return lxc_cgroup_finish_attach(data, pid); +} + /* * rename cgname, which is under cgparent, to a new name starting * with 'cgparent/dead'. That way cgname can be reused. Return diff --git a/src/lxc/cgroup.h b/src/lxc/cgroup.h index 3c90696dc..8167f3920 100644 --- a/src/lxc/cgroup.h +++ b/src/lxc/cgroup.h @@ -31,5 +31,8 @@ extern int lxc_cgroup_destroy(const char *name); extern int lxc_cgroup_path_get(char **path, const char *subsystem, const char *name); extern int lxc_cgroup_nrtasks(const char *name); extern int lxc_cgroup_attach(const char *name, pid_t pid); +extern int lxc_cgroup_prepare_attach(const char *name, void **data); +extern int lxc_cgroup_finish_attach(void *data, pid_t pid); +extern int lxc_cgroup_dispose_attach(void *data); extern int lxc_ns_is_mounted(void); #endif diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c index 955e9f445..e4f604ba3 100644 --- a/src/lxc/lxc_attach.c +++ b/src/lxc/lxc_attach.c @@ -96,6 +96,7 @@ int main(int argc, char *argv[]) struct passwd *passwd; struct lxc_proc_context_info *init_ctx; struct lxc_handler *handler; + void *cgroup_data = NULL; uid_t uid; char *curdir; @@ -124,6 +125,35 @@ int main(int argc, char *argv[]) return -1; } + if (!elevated_privileges) { + /* we have to do this now since /sys/fs/cgroup may not + * be available inside the container or we may not have + * the required permissions anymore + */ + ret = lxc_cgroup_prepare_attach(my_args.name, &cgroup_data); + if (ret < 0) { + ERROR("failed to prepare attaching to cgroup"); + return -1; + } + } + + curdir = get_current_dir_name(); + + /* we need to attach before we fork since certain namespaces + * (such as pid namespaces) only really affect children of the + * current process and not the process itself + */ + ret = lxc_attach_to_ns(init_pid); + if (ret < 0) { + ERROR("failed to enter the namespace"); + return -1; + } + + if (curdir && chdir(curdir)) + WARN("could not change directory to '%s'", curdir); + + free(curdir); + /* hack: we need sync.h infrastructure - and that needs a handler */ handler = calloc(1, sizeof(*handler)); @@ -150,8 +180,22 @@ int main(int argc, char *argv[]) if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE)) return -1; - if (!elevated_privileges && lxc_cgroup_attach(my_args.name, pid)) - return -1; + /* now that we are done with all privileged operations, + * we can add ourselves to the cgroup. Since we smuggled in + * the fds earlier, we still have write permission + */ + if (!elevated_privileges) { + /* since setns() for pid namespaces only really + * affects child processes, the pid we have is + * still valid outside the container, so this is + * fine + */ + ret = lxc_cgroup_finish_attach(cgroup_data, pid); + if (ret < 0) { + ERROR("failed to attach process to cgroup"); + return -1; + } + } /* tell the child we are done initializing */ if (lxc_sync_wake_child(handler, LXC_SYNC_POST_CONFIGURE)) @@ -175,19 +219,7 @@ int main(int argc, char *argv[]) if (!pid) { lxc_sync_fini_parent(handler); - - curdir = get_current_dir_name(); - - ret = lxc_attach_to_ns(init_pid); - if (ret < 0) { - ERROR("failed to enter the namespace"); - return -1; - } - - if (curdir && chdir(curdir)) - WARN("could not change directory to '%s'", curdir); - - free(curdir); + lxc_cgroup_dispose_attach(cgroup_data); if (new_personality < 0) new_personality = init_ctx->personality; From fc763ab77d48ecd191a402a11f778c46e114e79b Mon Sep 17 00:00:00 2001 From: Christian Seiler Date: Wed, 22 Aug 2012 00:03:13 +0200 Subject: [PATCH 011/171] lxc-attach: Detect which namespaces to attach to dynamically Use the command interface to contact lxc-start to receive the set of flags passed to clone() when starting the container. This allows lxc-attach to determine which namespaces were used for the container and select only those to attach to. Signed-off-by: Christian Seiler Cc: Daniel Lezcano Acked-by: Serge Hallyn --- src/lxc/attach.c | 42 +++++++++++++++++++++++++++++++++++++----- src/lxc/attach.h | 2 +- src/lxc/lxc_attach.c | 16 +++++++++++++++- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/lxc/attach.c b/src/lxc/attach.c index a95b3d3cc..37e667fe1 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -121,13 +121,22 @@ out_error: return NULL; } -int lxc_attach_to_ns(pid_t pid) +int lxc_attach_to_ns(pid_t pid, int which) { char path[MAXPATHLEN]; - char *ns[] = { "pid", "mnt", "net", "ipc", "uts" }; - const int size = sizeof(ns) / sizeof(char *); + /* according to , + * the file for user namepsaces in /proc/$pid/ns will be called + * 'user' once the kernel supports it + */ + static char *ns[] = { "mnt", "pid", "uts", "ipc", "user", "net" }; + static int flags[] = { + CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWIPC, + CLONE_NEWUSER, CLONE_NEWNET + }; + static const int size = sizeof(ns) / sizeof(char *); int fd[size]; - int i; + int i, j, saved_errno; + snprintf(path, MAXPATHLEN, "/proc/%d/ns", pid); if (access(path, X_OK)) { @@ -136,16 +145,39 @@ int lxc_attach_to_ns(pid_t pid) } for (i = 0; i < size; i++) { + /* ignore if we are not supposed to attach to that + * namespace + */ + if (which != -1 && !(which & flags[i])) { + fd[i] = -1; + continue; + } + snprintf(path, MAXPATHLEN, "/proc/%d/ns/%s", pid, ns[i]); fd[i] = open(path, O_RDONLY); if (fd[i] < 0) { + saved_errno = errno; + + /* close all already opened file descriptors before + * we return an error, so we don't leak them + */ + for (j = 0; j < i; j++) + close(fd[j]); + + errno = saved_errno; SYSERROR("failed to open '%s'", path); return -1; } } for (i = 0; i < size; i++) { - if (setns(fd[i], 0)) { + if (fd[i] >= 0 && setns(fd[i], 0) != 0) { + saved_errno = errno; + + for (j = i; j < size; j++) + close(fd[j]); + + errno = saved_errno; SYSERROR("failed to set namespace '%s'", ns[i]); return -1; } diff --git a/src/lxc/attach.h b/src/lxc/attach.h index 2d46c83dc..d96fdae42 100644 --- a/src/lxc/attach.h +++ b/src/lxc/attach.h @@ -33,7 +33,7 @@ struct lxc_proc_context_info { extern struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid); -extern int lxc_attach_to_ns(pid_t other_pid); +extern int lxc_attach_to_ns(pid_t other_pid, int which); extern int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx); #endif diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c index e4f604ba3..10d4a64ad 100644 --- a/src/lxc/lxc_attach.c +++ b/src/lxc/lxc_attach.c @@ -51,6 +51,7 @@ static const struct option my_longopts[] = { static int elevated_privileges = 0; static signed long new_personality = -1; +static int namespace_flags = -1; static int my_parser(struct lxc_arguments* args, int c, char* arg) { @@ -139,11 +140,24 @@ int main(int argc, char *argv[]) curdir = get_current_dir_name(); + /* determine which namespaces the container was created with + * by asking lxc-start + */ + if (namespace_flags == -1) { + namespace_flags = lxc_get_clone_flags(my_args.name); + /* call failed */ + if (namespace_flags == -1) { + ERROR("failed to automatically determine the " + "namespaces which the container unshared"); + return -1; + } + } + /* we need to attach before we fork since certain namespaces * (such as pid namespaces) only really affect children of the * current process and not the process itself */ - ret = lxc_attach_to_ns(init_pid); + ret = lxc_attach_to_ns(init_pid, namespace_flags); if (ret < 0) { ERROR("failed to enter the namespace"); return -1; From 39a5d5feeeebbffa7187ef318fb6a11fdb063fbe Mon Sep 17 00:00:00 2001 From: Christian Seiler Date: Wed, 22 Aug 2012 00:03:14 +0200 Subject: [PATCH 012/171] lxc-unshare: Move functions to determine clone flags from command line options to namespace.c In order to be able to reuse code in lxc-attach, the functions lxc_namespace_2_cloneflag and lxc_fill_namespace_flags are moved from lxc_unshare.c to namespace.c. Signed-off-by: Christian Seiler Cc: Daniel Lezcano Acked-by: Serge Hallyn --- src/lxc/lxc_unshare.c | 45 ------------------------------------------- src/lxc/namespace.c | 45 +++++++++++++++++++++++++++++++++++++++++++ src/lxc/namespace.h | 3 +++ 3 files changed, 48 insertions(+), 45 deletions(-) diff --git a/src/lxc/lxc_unshare.c b/src/lxc/lxc_unshare.c index 498d6e0e6..3a848b23d 100644 --- a/src/lxc/lxc_unshare.c +++ b/src/lxc/lxc_unshare.c @@ -84,51 +84,6 @@ static uid_t lookup_user(const char *optarg) return uid; } -static char *namespaces_list[] = { - "MOUNT", "PID", "UTSNAME", "IPC", - "USER", "NETWORK" -}; -static int cloneflags_list[] = { - CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWIPC, - CLONE_NEWUSER, CLONE_NEWNET -}; - -static int lxc_namespace_2_cloneflag(char *namespace) -{ - int i, len; - len = sizeof(namespaces_list)/sizeof(namespaces_list[0]); - for (i = 0; i < len; i++) - if (!strcmp(namespaces_list[i], namespace)) - return cloneflags_list[i]; - - ERROR("invalid namespace name %s", namespace); - return -1; -} - -static int lxc_fill_namespace_flags(char *flaglist, int *flags) -{ - char *token, *saveptr = NULL; - int aflag; - - if (!flaglist) { - ERROR("need at least one namespace to unshare"); - return -1; - } - - token = strtok_r(flaglist, "|", &saveptr); - while (token) { - - aflag = lxc_namespace_2_cloneflag(token); - if (aflag < 0) - return -1; - - *flags |= aflag; - - token = strtok_r(NULL, "|", &saveptr); - } - return 0; -} - struct start_arg { char ***args; diff --git a/src/lxc/namespace.c b/src/lxc/namespace.c index 3e6fc3aad..3fa027b53 100644 --- a/src/lxc/namespace.c +++ b/src/lxc/namespace.c @@ -69,3 +69,48 @@ pid_t lxc_clone(int (*fn)(void *), void *arg, int flags) return ret; } + +static char *namespaces_list[] = { + "MOUNT", "PID", "UTSNAME", "IPC", + "USER", "NETWORK" +}; +static int cloneflags_list[] = { + CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWIPC, + CLONE_NEWUSER, CLONE_NEWNET +}; + +int lxc_namespace_2_cloneflag(char *namespace) +{ + int i, len; + len = sizeof(namespaces_list)/sizeof(namespaces_list[0]); + for (i = 0; i < len; i++) + if (!strcmp(namespaces_list[i], namespace)) + return cloneflags_list[i]; + + ERROR("invalid namespace name %s", namespace); + return -1; +} + +int lxc_fill_namespace_flags(char *flaglist, int *flags) +{ + char *token, *saveptr = NULL; + int aflag; + + if (!flaglist) { + ERROR("need at least one namespace to unshare"); + return -1; + } + + token = strtok_r(flaglist, "|", &saveptr); + while (token) { + + aflag = lxc_namespace_2_cloneflag(token); + if (aflag < 0) + return -1; + + *flags |= aflag; + + token = strtok_r(NULL, "|", &saveptr); + } + return 0; +} diff --git a/src/lxc/namespace.h b/src/lxc/namespace.h index f839f4510..715dffa0e 100644 --- a/src/lxc/namespace.h +++ b/src/lxc/namespace.h @@ -54,4 +54,7 @@ int clone(int (*fn)(void *), void *child_stack, extern pid_t lxc_clone(int (*fn)(void *), void *arg, int flags); +extern int lxc_namespace_2_cloneflag(char *namespace); +extern int lxc_fill_namespace_flags(char *flaglist, int *flags); + #endif From e13eeea2db3743bf8d3fe2833e069a80e2c4102c Mon Sep 17 00:00:00 2001 From: Christian Seiler Date: Wed, 22 Aug 2012 00:03:15 +0200 Subject: [PATCH 013/171] lxc-attach: Add -s option to select namespaces to attach to This patch allows the user to select any list of namespaces (network, pid, mount, uts, ipc, user) that lxc-attach should use when attaching to the container; all other namespaces will not be attached to. This allows the user to for example attach to just the network namespace and use the host's (and not the container's) network tools to reconfigure the network of the container. Signed-off-by: Christian Seiler Cc: Daniel Lezcano Acked-by: Serge Hallyn --- doc/lxc-attach.sgml.in | 96 ++++++++++++++++++++++++++++++++++++++++-- src/lxc/lxc_attach.c | 20 ++++++++- 2 files changed, 111 insertions(+), 5 deletions(-) diff --git a/doc/lxc-attach.sgml.in b/doc/lxc-attach.sgml.in index 5d6263f13..d5154fd18 100644 --- a/doc/lxc-attach.sgml.in +++ b/doc/lxc-attach.sgml.in @@ -52,6 +52,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -n name -a arch -e + -s namespaces -- command @@ -125,6 +126,29 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + + + + Specify the namespaces to attach to, as a pipe-separated liste, + e.g. NETWORK|IPC. Allowed values are + MOUNT, PID, + UTSNAME, IPC, + USER and + NETWORK. This allows one to change + the context of the process to e.g. the network namespace of the + container while retaining the other namespaces as those of the + host. + + + Important: This option implies + . + + + + @@ -147,19 +171,83 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA To deactivate the network link eth1 of a running container that - does not have the NET_ADMIN capability, use the - option to use increased capabilities: + does not have the NET_ADMIN capability, use either the + option to use increased capabilities, + assuming the ip tool is installed: lxc-attach -n container -e -- /sbin/ip link delete eth1 + Or, alternatively, use the to use the + tools installed on the host outside the container: + + lxc-attach -n container -s NETWORK -- /sbin/ip link delete eth1 + + + Compatibility + + Attaching completely (including the pid and mount namespaces) to a + container requires a patched kernel, please see the lxc website for + details. lxc-attach will fail in that case if + used with an unpatched kernel. + + + Nevertheless, it will succeed on an unpatched kernel of version 3.0 + or higher if the option is used to restrict the + namespaces that the process is to be attached to to one or more of + NETWORK, IPC + and UTSNAME. + + + Attaching to user namespaces is currently completely unsupported + by the kernel. lxc-attach should however be able + to do this once once future kernel versions implement this. + + + + + Notes + + The Linux /proc and + /sys filesystems contain information + about some quantities that are affected by namespaces, such as + the directories named after process ids in + /proc or the network interface infromation + in /sys/class/net. The namespace of the + process mounting the pseudo-filesystems determines what information + is shown, not the namespace of the process + accessing /proc or + /sys. + + + If one uses the option to only attach to + the pid namespace of a container, but not its mount namespace + (which will contain the /proc of the + container and not the host), the contents of + will reflect that of the host and not the container. Analogously, + the same issue occurs when reading the contents of + /sys/class/net and attaching to just + the network namespace. + + + A workaround is to use lxc-unshare to unshare + the mount namespace after using lxc-attach with + -s PID and/or -s + NETWORK and then unmount and then mount again both + pseudo-filesystems within that new mount namespace, before + executing a program/script that relies on this information to be + correct. + + + Security - The should be used with care, as it may break - the isolation of the containers if used improperly. + The and options should + be used with care, as it may break the isolation of the containers + if used improperly. diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c index 10d4a64ad..4f2275207 100644 --- a/src/lxc/lxc_attach.c +++ b/src/lxc/lxc_attach.c @@ -40,12 +40,14 @@ #include "start.h" #include "sync.h" #include "log.h" +#include "namespace.h" lxc_log_define(lxc_attach_ui, lxc); static const struct option my_longopts[] = { {"elevated-privileges", no_argument, 0, 'e'}, {"arch", required_argument, 0, 'a'}, + {"namespaces", required_argument, 0, 's'}, LXC_COMMON_OPTIONS }; @@ -55,6 +57,8 @@ static int namespace_flags = -1; static int my_parser(struct lxc_arguments* args, int c, char* arg) { + int ret; + switch (c) { case 'e': elevated_privileges = 1; break; case 'a': @@ -64,6 +68,14 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg) return -1; } break; + case 's': + namespace_flags = 0; + ret = lxc_fill_namespace_flags(arg, &namespace_flags); + if (ret) + return -1; + /* -s implies -e */ + elevated_privileges = 1; + break; } return 0; @@ -84,7 +96,13 @@ Options :\n\ WARNING: This may leak privleges into the container.\n\ Use with care.\n\ -a, --arch=ARCH Use ARCH for program instead of container's own\n\ - architecture.\n", + architecture.\n\ + -s, --namespaces=FLAGS\n\ + Don't attach to all the namespaces of the container\n\ + but just to the following OR'd list of flags:\n\ + MOUNT, PID, UTSNAME, IPC, USER or NETWORK\n\ + WARNING: Using -s implies -e, it may therefore\n\ + leak privileges into the container. Use with care.\n", .options = my_longopts, .parser = my_parser, .checker = NULL, From 7a0b0b5672a33c190eefb4b2d3e3693241c130f2 Mon Sep 17 00:00:00 2001 From: Christian Seiler Date: Wed, 22 Aug 2012 00:03:16 +0200 Subject: [PATCH 014/171] lxc-attach: Add -R option to remount /sys and /proc when only partially attaching When attaching to only some namespaces of the container but not the mount namespace, the contents of /sys and /proc of the host system do not properly reflect the context of the container's pid and/or network namespaces, and possibly others. The introduced -R option adds the possibility to additionally unshare the mount namespace (when it is not being attached) and remount /sys and /proc in order for those filesystems to properly reflect the container's context even when only attaching to some of the namespaces. Signed-off-by: Christian Seiler Acked-by: Serge Hallyn Cc: Daniel Lezcano --- doc/lxc-attach.sgml.in | 43 +++++++++++++++++++++++++++++++++-------- src/lxc/attach.c | 44 ++++++++++++++++++++++++++++++++++++++++++ src/lxc/attach.h | 1 + src/lxc/lxc_attach.c | 22 ++++++++++++++++++++- 4 files changed, 101 insertions(+), 9 deletions(-) diff --git a/doc/lxc-attach.sgml.in b/doc/lxc-attach.sgml.in index d5154fd18..a9a29021b 100644 --- a/doc/lxc-attach.sgml.in +++ b/doc/lxc-attach.sgml.in @@ -53,6 +53,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -a arch -e -s namespaces + -R -- command @@ -149,7 +150,30 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - + + + + + + + When using and the mount namespace is not + included, this flag will cause lxc-attach + to remount /proc and + /sys to reflect the current other + namespace contexts. + + + Please see the Notes section for more + details. + + + This option will be ignored if one tries to attach to the + mount namespace anyway. + + + + + @@ -232,13 +256,16 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA the network namespace. - A workaround is to use lxc-unshare to unshare - the mount namespace after using lxc-attach with - -s PID and/or -s - NETWORK and then unmount and then mount again both - pseudo-filesystems within that new mount namespace, before - executing a program/script that relies on this information to be - correct. + To work around this problem, the flag provides + the option to remount /proc and + /sys in order for them to reflect the + network/pid namespace context of the attached process. In order + not to interfere with the host's actual filesystem, the mount + namespace will be unshared (like lxc-unshare + does) before this is done, esentially giving the process a new + mount namespace, which is identical to the hosts's mount namespace + except for the /proc and + /sys filesystems. diff --git a/src/lxc/attach.c b/src/lxc/attach.c index 37e667fe1..ec0e08300 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #if !HAVE_DECL_PR_CAPBSET_DROP @@ -188,6 +189,49 @@ int lxc_attach_to_ns(pid_t pid, int which) return 0; } +int lxc_attach_remount_sys_proc() +{ + int ret; + + ret = unshare(CLONE_NEWNS); + if (ret < 0) { + SYSERROR("failed to unshare mount namespace"); + return -1; + } + + /* assume /proc is always mounted, so remount it */ + ret = umount2("/proc", MNT_DETACH); + if (ret < 0) { + SYSERROR("failed to unmount /proc"); + return -1; + } + + ret = mount("none", "/proc", "proc", 0, NULL); + if (ret < 0) { + SYSERROR("failed to remount /proc"); + return -1; + } + + /* try to umount /sys - if it's not a mount point, + * we'll get EINVAL, then we ignore it because it + * may not have been mounted in the first place + */ + ret = umount2("/sys", MNT_DETACH); + if (ret < 0 && errno != EINVAL) { + SYSERROR("failed to unmount /sys"); + return -1; + } else if (ret == 0) { + /* remount it */ + ret = mount("none", "/sys", "sysfs", 0, NULL); + if (ret < 0) { + SYSERROR("failed to remount /sys"); + return -1; + } + } + + return 0; +} + int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx) { int last_cap = lxc_caps_last_cap(); diff --git a/src/lxc/attach.h b/src/lxc/attach.h index d96fdae42..aab47e33f 100644 --- a/src/lxc/attach.h +++ b/src/lxc/attach.h @@ -34,6 +34,7 @@ struct lxc_proc_context_info { extern struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid); extern int lxc_attach_to_ns(pid_t other_pid, int which); +extern int lxc_attach_remount_sys_proc(); extern int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx); #endif diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c index 4f2275207..e292bc4be 100644 --- a/src/lxc/lxc_attach.c +++ b/src/lxc/lxc_attach.c @@ -48,12 +48,14 @@ static const struct option my_longopts[] = { {"elevated-privileges", no_argument, 0, 'e'}, {"arch", required_argument, 0, 'a'}, {"namespaces", required_argument, 0, 's'}, + {"remount-sys-proc", no_argument, 0, 'R'}, LXC_COMMON_OPTIONS }; static int elevated_privileges = 0; static signed long new_personality = -1; static int namespace_flags = -1; +static int remount_sys_proc = 0; static int my_parser(struct lxc_arguments* args, int c, char* arg) { @@ -61,6 +63,7 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg) switch (c) { case 'e': elevated_privileges = 1; break; + case 'R': remount_sys_proc = 1; break; case 'a': new_personality = lxc_config_parse_arch(arg); if (new_personality < 0) { @@ -102,7 +105,12 @@ Options :\n\ but just to the following OR'd list of flags:\n\ MOUNT, PID, UTSNAME, IPC, USER or NETWORK\n\ WARNING: Using -s implies -e, it may therefore\n\ - leak privileges into the container. Use with care.\n", + leak privileges into the container. Use with care.\n\ + -R, --remount-sys-proc\n\ + Remount /sys and /proc if not attaching to the\n\ + mount namespace when using -s in order to properly\n\ + reflect the correct namespace context. See the\n\ + lxc-attach(1) manual page for details.\n", .options = my_longopts, .parser = my_parser, .checker = NULL, @@ -253,6 +261,18 @@ int main(int argc, char *argv[]) lxc_sync_fini_parent(handler); lxc_cgroup_dispose_attach(cgroup_data); + /* A description of the purpose of this functionality is + * provided in the lxc-attach(1) manual page. We have to + * remount here and not in the parent process, otherwise + * /proc may not properly reflect the new pid namespace. + */ + if (!(namespace_flags & CLONE_NEWNS) && remount_sys_proc) { + ret = lxc_attach_remount_sys_proc(); + if (ret < 0) { + return -1; + } + } + if (new_personality < 0) new_personality = init_ctx->personality; From 72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 27 Aug 2012 18:53:00 -0400 Subject: [PATCH 015/171] Merge the liblxc API work by Serge Hallyn. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This turns liblxc into a public library implementing a container structure. The container structure is meant to cover most LXC commands and can easily be used to write bindings in other programming languages. More information on the new functions can be found in src/lxc/lxccontainer.h Test programs using the API can also be found in src/tests/ Signed-off-by: Serge Hallyn Acked-by: Stéphane Graber --- configure.ac | 2 + doc/rootfs/Makefile.am | 2 +- lxc/runapitests.bash | 32 ++ runapitests.bash | 32 ++ src/Makefile.am | 2 +- src/lxc/Makefile.am | 15 +- src/lxc/commands.c | 1 + src/lxc/conf.c | 197 +++++++- src/lxc/conf.h | 10 + src/lxc/confile.c | 604 ++++++++++++++++++++++++- src/lxc/confile.h | 12 + src/lxc/lxc.h | 25 + src/lxc/lxc_wait.c | 73 +-- src/lxc/lxccontainer.c | 905 +++++++++++++++++++++++++++++++++++++ src/lxc/lxccontainer.h | 71 +++ src/lxc/lxclock.c | 105 +++++ src/lxc/lxclock.h | 61 +++ src/lxc/monitor.c | 24 +- src/lxc/network.c | 16 + src/lxc/network.h | 1 + src/lxc/state.c | 107 +++++ src/lxc/state.h | 1 + src/tests/Makefile.am | 17 + src/tests/containertests.c | 257 +++++++++++ src/tests/createtest.c | 92 ++++ src/tests/destroytest.c | 104 +++++ src/tests/get_item.c | 308 +++++++++++++ src/tests/getkeys.c | 71 +++ src/tests/locktests.c | 239 ++++++++++ src/tests/saveconfig.c | 106 +++++ src/tests/shutdowntest.c | 93 ++++ src/tests/startone.c | 202 +++++++++ 32 files changed, 3693 insertions(+), 94 deletions(-) create mode 100644 lxc/runapitests.bash create mode 100644 runapitests.bash create mode 100644 src/lxc/lxccontainer.c create mode 100644 src/lxc/lxccontainer.h create mode 100644 src/lxc/lxclock.c create mode 100644 src/lxc/lxclock.h create mode 100644 src/tests/Makefile.am create mode 100644 src/tests/containertests.c create mode 100644 src/tests/createtest.c create mode 100644 src/tests/destroytest.c create mode 100644 src/tests/get_item.c create mode 100644 src/tests/getkeys.c create mode 100644 src/tests/locktests.c create mode 100644 src/tests/saveconfig.c create mode 100644 src/tests/shutdowntest.c create mode 100644 src/tests/startone.c diff --git a/configure.ac b/configure.ac index 066f56642..dbaf48ba9 100644 --- a/configure.ac +++ b/configure.ac @@ -192,6 +192,8 @@ AC_CONFIG_FILES([ src/lxc/lxc-shutdown src/lxc/lxc-destroy + src/tests/Makefile + ]) AC_CONFIG_COMMANDS([default],[[]],[[]]) AC_OUTPUT diff --git a/doc/rootfs/Makefile.am b/doc/rootfs/Makefile.am index 98fb0e07a..c9bb45db1 100644 --- a/doc/rootfs/Makefile.am +++ b/doc/rootfs/Makefile.am @@ -1,3 +1,3 @@ READMEdir=@LXCROOTFSMOUNT@ -README_DATA=README \ No newline at end of file +README_DATA= diff --git a/lxc/runapitests.bash b/lxc/runapitests.bash new file mode 100644 index 000000000..2b1b3baec --- /dev/null +++ b/lxc/runapitests.bash @@ -0,0 +1,32 @@ +#!/bin/bash + +cleanup() { + rm -f /etc/lxc/test-busybox.conf + rm -f liblxc.so.0 +} + +if [ `id -u` -ne 0 ]; then + echo "Run as root" + exit 1 +fi + +cat > /etc/lxc/test-busybox.conf << EOF +lxc.network.type=veth +lxc.network.link=lxcbr0 +lxc.network.flags=up +EOF + +[ -f liblxc.so.0 ] || ln -s src/lxc/liblxc.so ./liblxc.so.0 +export LD_LIBRARY_PATH=. +TESTS="containertests locktests startone" +for curtest in $TESTS; do + echo "running $curtest" + ./src/tests/$curtest + if [ $? -ne 0 ]; then + echo "Test $curtest failed. Stopping" + cleanup + exit 1 + fi +done +echo "All tests passed" +cleanup diff --git a/runapitests.bash b/runapitests.bash new file mode 100644 index 000000000..2b1b3baec --- /dev/null +++ b/runapitests.bash @@ -0,0 +1,32 @@ +#!/bin/bash + +cleanup() { + rm -f /etc/lxc/test-busybox.conf + rm -f liblxc.so.0 +} + +if [ `id -u` -ne 0 ]; then + echo "Run as root" + exit 1 +fi + +cat > /etc/lxc/test-busybox.conf << EOF +lxc.network.type=veth +lxc.network.link=lxcbr0 +lxc.network.flags=up +EOF + +[ -f liblxc.so.0 ] || ln -s src/lxc/liblxc.so ./liblxc.so.0 +export LD_LIBRARY_PATH=. +TESTS="containertests locktests startone" +for curtest in $TESTS; do + echo "running $curtest" + ./src/tests/$curtest + if [ $? -ne 0 ]; then + echo "Test $curtest failed. Stopping" + cleanup + exit 1 + fi +done +echo "All tests passed" +cleanup diff --git a/src/Makefile.am b/src/Makefile.am index 7dd344abc..ca3b09203 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1 +1 @@ -SUBDIRS = lxc +SUBDIRS = lxc tests diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index a4b63fa64..b2f2128c4 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -13,7 +13,9 @@ pkginclude_HEADERS = \ list.h \ log.h \ state.h \ - attach.h + attach.h \ + lxccontainer.h \ + lxclock.h sodir=$(libdir) # use PROGRAMS to avoid complains from automake @@ -55,12 +57,15 @@ liblxc_so_SOURCES = \ af_unix.c af_unix.h \ \ utmp.c utmp.h \ - apparmor.c apparmor.h + apparmor.c apparmor.h \ + lxclock.h lxclock.c \ + lxccontainer.c lxccontainer.h AM_CFLAGS=-I$(top_srcdir)/src \ -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -DLXCPATH=\"$(LXCPATH)\" \ - -DLXCINITDIR=\"$(LXCINITDIR)\" + -DLXCINITDIR=\"$(LXCINITDIR)\" \ + -DLXCTEMPLATEDIR=\"$(LXCTEMPLATEDIR)\" if ENABLE_APPARMOR AM_CFLAGS += -DHAVE_APPARMOR @@ -77,7 +82,7 @@ liblxc_so_LDFLAGS = \ -shared \ -Wl,-soname,liblxc.so.$(firstword $(subst ., ,$(VERSION))) -liblxc_so_LDADD = -lutil $(CAP_LIBS) $(APPARMOR_LIBS) $(SECCOMP_LIBS) +liblxc_so_LDADD = -lutil $(CAP_LIBS) $(APPARMOR_LIBS) $(SECCOMP_LIBS) -lrt bin_SCRIPTS = \ lxc-ps \ @@ -116,7 +121,7 @@ AM_LDFLAGS = -Wl,-E if ENABLE_RPATH AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir) endif -LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@ @SECCOMP_LIBS@ +LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@ @SECCOMP_LIBS@ -lrt lxc_attach_SOURCES = lxc_attach.c lxc_cgroup_SOURCES = lxc_cgroup.c diff --git a/src/lxc/commands.c b/src/lxc/commands.c index dc9381531..737540d3d 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -327,5 +327,6 @@ extern int lxc_command_mainloop_add(const char *name, close(fd); } + handler->conf->maincmd_fd = fd; return ret; } diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 79fe4ad5a..34788e649 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -109,6 +109,9 @@ lxc_log_define(lxc_conf, lxc); #define PR_CAPBSET_DROP 24 #endif +char *lxchook_names[NUM_LXC_HOOKS] = { + "pre-start", "mount", "start", "post-stop" }; + extern int pivot_root(const char * new_root, const char * put_old); typedef int (*instanciate_cb)(struct lxc_handler *, struct lxc_netdev *); @@ -1661,7 +1664,7 @@ static int setup_netdev(struct lxc_netdev *netdev) ifname, strerror(-err)); if (netdev->ipv6_gateway_auto) { char buf[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET, netdev->ipv6_gateway, buf, sizeof(buf)); + inet_ntop(AF_INET6, netdev->ipv6_gateway, buf, sizeof(buf)); ERROR("tried to set autodetected ipv6 gateway '%s'", buf); } return -1; @@ -2354,3 +2357,195 @@ int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf) } return 0; } + +static int lxc_remove_nic(struct lxc_list *it) +{ + struct lxc_netdev *netdev = it->elem; + struct lxc_list *it2; + + lxc_list_del(it); + + if (netdev->link) + free(netdev->link); + if (netdev->name) + free(netdev->name); + if (netdev->upscript) + free(netdev->upscript); + if (netdev->hwaddr) + free(netdev->hwaddr); + if (netdev->mtu) + free(netdev->mtu); + if (netdev->ipv4_gateway) + free(netdev->ipv4_gateway); + if (netdev->ipv6_gateway) + free(netdev->ipv6_gateway); + lxc_list_for_each(it2, &netdev->ipv4) { + lxc_list_del(it2); + free(it2->elem); + free(it2); + } + lxc_list_for_each(it2, &netdev->ipv6) { + lxc_list_del(it2); + free(it2->elem); + free(it2); + } + free(it); +} + +/* we get passed in something like '0', '0.ipv4' or '1.ipv6' */ +int lxc_clear_nic(struct lxc_conf *c, char *key) +{ + char *p1; + int ret, idx, i; + struct lxc_list *it; + struct lxc_netdev *netdev; + + p1 = index(key, '.'); + if (!p1 || *(p1+1) == '\0') + p1 = NULL; + + ret = sscanf(key, "%d", &idx); + if (ret != 1) return -1; + if (idx < 0) + return -1; + + i = 0; + lxc_list_for_each(it, &c->network) { + if (i == idx) + break; + i++; + } + if (i < idx) // we don't have that many nics defined + return -1; + + if (!it || !it->elem) + return -1; + + netdev = it->elem; + + if (!p1) { + lxc_remove_nic(it); + } else if (strcmp(p1, "ipv4") == 0) { + struct lxc_list *it2; + lxc_list_for_each(it2, &netdev->ipv4) { + lxc_list_del(it2); + free(it2->elem); + free(it2); + } + } else if (strcmp(p1, "ipv6") == 0) { + struct lxc_list *it2; + lxc_list_for_each(it2, &netdev->ipv6) { + lxc_list_del(it2); + free(it2->elem); + free(it2); + } + } else if (strcmp(p1, "link") == 0) { + if (netdev->link) { + free(netdev->link); + netdev->link = NULL; + } + } else if (strcmp(p1, "name") == 0) { + if (netdev->name) { + free(netdev->name); + netdev->name = NULL; + } + } else if (strcmp(p1, "script.up") == 0) { + if (netdev->upscript) { + free(netdev->upscript); + netdev->upscript = NULL; + } + } else if (strcmp(p1, "hwaddr") == 0) { + if (netdev->hwaddr) { + free(netdev->hwaddr); + netdev->hwaddr = NULL; + } + } else if (strcmp(p1, "mtu") == 0) { + if (netdev->mtu) { + free(netdev->mtu); + netdev->mtu = NULL; + } + } else if (strcmp(p1, "ipv4_gateway") == 0) { + if (netdev->ipv4_gateway) { + free(netdev->ipv4_gateway); + netdev->ipv4_gateway = NULL; + } + } else if (strcmp(p1, "ipv6_gateway") == 0) { + if (netdev->ipv6_gateway) { + free(netdev->ipv6_gateway); + netdev->ipv6_gateway = NULL; + } + } + else return -1; + + return 0; +} + +int lxc_clear_config_network(struct lxc_conf *c) +{ + struct lxc_list *it; + lxc_list_for_each(it, &c->network) { + lxc_remove_nic(it); + } + return 0; +} + +int lxc_clear_config_caps(struct lxc_conf *c) +{ + struct lxc_list *it; + + lxc_list_for_each(it, &c->caps) { + lxc_list_del(it); + free(it->elem); + free(it); + } + return 0; +} + +int lxc_clear_cgroups(struct lxc_conf *c, char *key) +{ + struct lxc_list *it; + bool all = false; + char *k = key + 11; + + if (strcmp(key, "lxc.cgroup") == 0) + all = true; + + lxc_list_for_each(it, &c->cgroup) { + struct lxc_cgroup *cg = it->elem; + if (!all && strcmp(cg->subsystem, k) != 0) + continue; + lxc_list_del(it); + free(cg->subsystem); + free(cg->value); + free(cg); + free(it); + } + return 0; +} + +int lxc_clear_mount_entries(struct lxc_conf *c) +{ + struct lxc_list *it; + + lxc_list_for_each(it, &c->mount_list) { + lxc_list_del(it); + free(it->elem); + free(it); + } + return 0; +} + +int lxc_clear_hooks(struct lxc_conf *c) +{ + struct lxc_list *it; + int i; + + for (i=0; ihooks[i]) { + lxc_list_del(it); + free(it->elem); + free(it); + } + } + return 0; +} diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 92efc585e..f75b43061 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -209,6 +209,8 @@ struct lxc_rootfs { enum lxchooks { LXCHOOK_PRESTART, LXCHOOK_MOUNT, LXCHOOK_START, LXCHOOK_POSTSTOP, NUM_LXC_HOOKS}; +extern char *lxchook_names[NUM_LXC_HOOKS]; + struct lxc_conf { char *fstab; int tty; @@ -234,6 +236,7 @@ struct lxc_conf { int lsm_umount_proc; #endif char *seccomp; // filename with the seccomp rules + int maincmd_fd; }; int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf); @@ -253,6 +256,13 @@ extern int lxc_find_gateway_addresses(struct lxc_handler *handler); extern int lxc_create_tty(const char *name, struct lxc_conf *conf); extern void lxc_delete_tty(struct lxc_tty_info *tty_info); +extern int lxc_clear_config_network(struct lxc_conf *c); +extern int lxc_clear_nic(struct lxc_conf *c, char *key); +extern int lxc_clear_config_caps(struct lxc_conf *c); +extern int lxc_clear_cgroups(struct lxc_conf *c, char *key); +extern int lxc_clear_mount_entries(struct lxc_conf *c); +extern int lxc_clear_hooks(struct lxc_conf *c); + /* * Configure the container from inside */ diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 9e51a6392..1b2a8f096 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -42,6 +42,7 @@ #include #include +#include "network.h" lxc_log_define(lxc_confile, lxc); @@ -77,15 +78,11 @@ static int config_cap_drop(const char *, char *, struct lxc_conf *); static int config_console(const char *, char *, struct lxc_conf *); static int config_seccomp(const char *, char *, struct lxc_conf *); static int config_includefile(const char *, char *, struct lxc_conf *); +static int config_network_nic(const char *, char *, struct lxc_conf *); typedef int (*config_cb)(const char *, char *, struct lxc_conf *); -struct config { - char *name; - config_cb cb; -}; - -static struct config config[] = { +static struct lxc_config_t config[] = { { "lxc.arch", config_personality }, { "lxc.pts", config_pts }, @@ -119,15 +116,17 @@ static struct config config[] = { { "lxc.network.ipv4", config_network_ipv4 }, { "lxc.network.ipv6.gateway", config_network_ipv6_gateway }, { "lxc.network.ipv6", config_network_ipv6 }, + /* config_network_nic must come after all other 'lxc.network.*' entries */ + { "lxc.network.", config_network_nic }, { "lxc.cap.drop", config_cap_drop }, { "lxc.console", config_console }, { "lxc.seccomp", config_seccomp }, { "lxc.include", config_includefile }, }; -static const size_t config_size = sizeof(config)/sizeof(struct config); +static const size_t config_size = sizeof(config)/sizeof(struct lxc_config_t); -static struct config *getconfig(const char *key) +extern struct lxc_config_t *lxc_getconfig(const char *key) { int i; @@ -138,6 +137,76 @@ static struct config *getconfig(const char *key) return NULL; } +#define strprint(str, inlen, ...) \ + do { \ + len = snprintf(str, inlen, ##__VA_ARGS__); \ + if (len < 0) { SYSERROR("snprintf"); return -1; }; \ + fulllen += len; \ + if (inlen > 0) { \ + if (str) str += len; \ + inlen -= len; \ + if (inlen < 0) inlen = 0; \ + } \ + } while (0); + +int lxc_listconfigs(char *retv, int inlen) +{ + int i, fulllen = 0, len; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + for (i = 0; i < config_size; i++) { + char *s = config[i].name; + if (s[strlen(s)-1] == '.') + continue; + strprint(retv, inlen, "%s\n", s); + } + return fulllen; +} + +/* + * config entry is something like "lxc.network.0.ipv4" + * the key 'lxc.network.' was found. So we make sure next + * comes an integer, find the right callback (by rewriting + * the key), and call it. + */ +static int config_network_nic(const char *key, char *value, + struct lxc_conf *lxc_conf) +{ + char *copy = strdup(key), *p; + int ret = -1; + struct lxc_config_t *config; + + if (!copy) { + SYSERROR("failed to allocate memory"); + return -1; + } + /* + * ok we know that to get here we've got "lxc.network." + * and it isn't any of the other network entries. So + * after the second . should come an integer (# of defined + * nic) followed by a valid entry. + */ + if (*(key+12) < '0' || *(key+12) > '9') + goto out; + p = index(key+12, '.'); + if (!p) + goto out; + strcpy(copy+12, p+1); + config = lxc_getconfig(copy); + if (!config) { + ERROR("unknown key %s", key); + goto out; + } + ret = config->cb(key, value, lxc_conf); + +out: + free(copy); + return ret; +} + static int config_network_type(const char *key, char *value, struct lxc_conf *lxc_conf) { @@ -195,10 +264,89 @@ static int config_ip_prefix(struct in_addr *addr) return 0; } +/* + * if you have p="lxc.network.0.link", pass it p+12 + * to get back '0' (the index of the nic) + */ +static int get_network_netdev_idx(const char *key) +{ + int ret, idx; + + if (*key < '0' || *key > '9') + return -1; + ret = sscanf(key, "%d", &idx); + if (ret != 1) + return -1; + return idx; +} + +/* + * if you have p="lxc.network.0", pass this p+12 and it will return + * the netdev of the first configured nic + */ +static struct lxc_netdev *get_netdev_from_key(const char *key, + struct lxc_list *network) +{ + int i = 0, idx = get_network_netdev_idx(key); + struct lxc_netdev *netdev = NULL; + struct lxc_list *it; + if (idx == -1) + return NULL; + lxc_list_for_each(it, network) { + if (idx == i++) { + netdev = it->elem; + break; + } + } + return netdev; +} + +extern int lxc_list_nicconfigs(struct lxc_conf *c, char *key, char *retv, int inlen) +{ + struct lxc_netdev *netdev; + int fulllen = 0, len; + + netdev = get_netdev_from_key(key+12, &c->network); + if (!netdev) + return -1; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + strprint(retv, inlen, "script.up\n"); + if (netdev->type != LXC_NET_EMPTY) { + strprint(retv, inlen, "flags\n"); + strprint(retv, inlen, "link\n"); + strprint(retv, inlen, "name\n"); + strprint(retv, inlen, "hwaddr\n"); + strprint(retv, inlen, "mtu\n"); + strprint(retv, inlen, "ipv6\n"); + strprint(retv, inlen, "ipv6_gateway\n"); + strprint(retv, inlen, "ipv4\n"); + strprint(retv, inlen, "ipv4_gateway\n"); + } + switch(netdev->type) { + case LXC_NET_VETH: + strprint(retv, inlen, "veth.pair\n"); + break; + case LXC_NET_MACVLAN: + strprint(retv, inlen, "macvlan.mode\n"); + break; + case LXC_NET_VLAN: + strprint(retv, inlen, "vlan.id\n"); + break; + case LXC_NET_PHYS: + break; + } + return fulllen; +} + static struct lxc_netdev *network_netdev(const char *key, const char *value, struct lxc_list *network) { - struct lxc_netdev *netdev; + struct lxc_netdev *netdev = NULL; if (lxc_list_empty(network)) { ERROR("network is not created for '%s' = '%s' option", @@ -206,7 +354,11 @@ static struct lxc_netdev *network_netdev(const char *key, const char *value, return NULL; } - netdev = lxc_list_last_elem(network); + if (get_network_netdev_idx(key+12) == -1) + netdev = lxc_list_last_elem(network); + else + netdev = get_netdev_from_key(key+12, network); + if (!netdev) { ERROR("no network device defined for '%s' = '%s' option", key, value); @@ -592,7 +744,7 @@ static int config_network_script(const char *key, char *value, SYSERROR("failed to dup string '%s'", value); return -1; } - if (strcmp(key, "lxc.network.script.up") == 0) { + if (strstr(key, "script.up") != NULL) { netdev->upscript = copy; return 0; } @@ -870,6 +1022,10 @@ static int config_cap_drop(const char *key, char *value, break; } + /* note - i do believe we're losing memory here, + note that token is already pointing into dropcaps + which is strdup'd. we never free that bit. + */ droplist->elem = strdup(token); if (!droplist->elem) { SYSERROR("failed to dup '%s'", token); @@ -979,7 +1135,7 @@ static int config_utsname(const char *key, char *value, struct lxc_conf *lxc_con static int parse_line(char *buffer, void *data) { - struct config *config; + struct lxc_config_t *config; char *line, *linep; char *dot; char *key; @@ -1024,7 +1180,7 @@ static int parse_line(char *buffer, void *data) value += lxc_char_left_gc(value, strlen(value)); value[lxc_char_right_gc(value, strlen(value))] = '\0'; - config = getconfig(key); + config = lxc_getconfig(key); if (!config) { ERROR("unknown key %s", key); goto out; @@ -1101,3 +1257,425 @@ signed long lxc_config_parse_arch(const char *arch) return -1; } + +static int lxc_get_conf_int(struct lxc_conf *c, char *retv, int inlen, int v) +{ + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + return snprintf(retv, inlen, "%d", v); +} + +static int lxc_get_arch_entry(struct lxc_conf *c, char *retv, int inlen) +{ + int len, fulllen = 0; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + switch(c->personality) { + case PER_LINUX32: strprint(retv, inlen, "x86"); break; + case PER_LINUX: strprint(retv, inlen, "x86_64"); break; + default: break; + } + + return fulllen; +} + +/* + * If you ask for a specific cgroup value, i.e. lxc.cgroup.devices.list, + * then just the value(s) will be printed. Since there still could be + * more than one, it is newline-separated. + * (Maybe that's ambigous, since some values, i.e. devices.list, will + * already have newlines?) + * If you ask for 'lxc.cgroup", then all cgroup entries will be printed, + * in 'lxc.cgroup.subsystem.key = value' format. + */ +static int lxc_get_cgroup_entry(struct lxc_conf *c, char *retv, int inlen, char *key) +{ + int fulllen = 0, len; + int all = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (strcmp(key, "all") == 0) + all = 1; + + lxc_list_for_each(it, &c->cgroup) { + struct lxc_cgroup *cg = it->elem; + if (all) { + strprint(retv, inlen, "lxc.cgroup.%s = %s\n", cg->subsystem, cg->value); + } else if (strcmp(cg->subsystem, key) == 0) { + strprint(retv, inlen, "%s\n", cg->value); + } + } + return fulllen; +} + +static int lxc_get_item_hooks(struct lxc_conf *c, char *retv, int inlen, char *key) +{ + char *subkey; + int len, fulllen = 0, found = -1; + struct lxc_list *it; + int i; + + /* "lxc.hook.mount" */ + subkey = index(key, '.'); + if (subkey) subkey = index(subkey+1, '.'); + if (!subkey) + return -1; + subkey++; + if (!*subkey) + return -1; + for (i=0; ihooks[found]) { + strprint(retv, inlen, "%s\n", (char *)it->elem); + } + return fulllen; +} + +static int lxc_get_item_cap_drop(struct lxc_conf *c, char *retv, int inlen) +{ + int len, fulllen = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->caps) { + strprint(retv, inlen, "%s\n", (char *)it->elem); + } + return fulllen; +} + +static int lxc_get_mount_entries(struct lxc_conf *c, char *retv, int inlen) +{ + int len, fulllen = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->mount_list) { + strprint(retv, inlen, "%s\n", (char *)it->elem); + } + return fulllen; +} + +/* + * lxc.network.0.XXX, where XXX can be: name, type, link, flags, type, + * macvlan.mode, veth.pair, vlan, ipv4, ipv6, upscript, hwaddr, mtu, + * ipv4_gateway, ipv6_gateway. ipvX_gateway can return 'auto' instead + * of an address. ipv4 and ipv6 return lists (newline-separated). + * things like veth.pair return '' if invalid (i.e. if called for vlan + * type). + */ +static int lxc_get_item_nic(struct lxc_conf *c, char *retv, int inlen, char *key) +{ + char *p1; + int i, len, fulllen = 0; + struct lxc_netdev *netdev; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + p1 = index(key, '.'); + if (!p1 || *(p1+1) == '\0') return -1; + p1++; + + netdev = get_netdev_from_key(key, &c->network); + if (!netdev) + return -1; + if (strcmp(p1, "name") == 0) { + if (netdev->name) + strprint(retv, inlen, "%s", netdev->name); + } else if (strcmp(p1, "type") == 0) { + strprint(retv, inlen, "%s", lxc_net_type_to_str(netdev->type)); + } else if (strcmp(p1, "link") == 0) { + if (netdev->link) + strprint(retv, inlen, "%s", netdev->link); + } else if (strcmp(p1, "flags") == 0) { + if (netdev->flags & IFF_UP) + strprint(retv, inlen, "up"); + } else if (strcmp(p1, "upscript") == 0) { + if (netdev->upscript) + strprint(retv, inlen, "%s", netdev->upscript); + } else if (strcmp(p1, "hwaddr") == 0) { + if (netdev->hwaddr) + strprint(retv, inlen, "%s", netdev->hwaddr); + } else if (strcmp(p1, "mtu") == 0) { + if (netdev->mtu) + strprint(retv, inlen, "%s", netdev->mtu); + } else if (strcmp(p1, "macvlan.mode") == 0) { + if (netdev->type == LXC_NET_MACVLAN) { + const char *mode; + switch (netdev->priv.macvlan_attr.mode) { + case MACVLAN_MODE_PRIVATE: mode = "private"; break; + case MACVLAN_MODE_VEPA: mode = "vepa"; break; + case MACVLAN_MODE_BRIDGE: mode = "bridge"; break; + default: mode = "(invalid)"; break; + } + strprint(retv, inlen, "%s", mode); + } + } else if (strcmp(p1, "veth.pair") == 0) { + if (netdev->type == LXC_NET_VETH && netdev->priv.veth_attr.pair) + strprint(retv, inlen, "%s", netdev->priv.veth_attr.pair); + } else if (strcmp(p1, "vlan") == 0) { + if (netdev->type == LXC_NET_VLAN) { + strprint(retv, inlen, "%d", netdev->priv.vlan_attr.vid); + } + } else if (strcmp(p1, "ipv4_gateway") == 0) { + if (netdev->ipv4_gateway_auto) { + strprint(retv, inlen, "auto"); + } else if (netdev->ipv4_gateway) { + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, netdev->ipv4_gateway, buf, sizeof(buf)); + strprint(retv, inlen, "%s", buf); + } + } else if (strcmp(p1, "ipv4") == 0) { + struct lxc_list *it2; + lxc_list_for_each(it2, &netdev->ipv4) { + struct lxc_inetdev *i = it2->elem; + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &i->addr, buf, sizeof(buf)); + strprint(retv, inlen, "%s\n", buf); + } + } else if (strcmp(p1, "ipv6_gateway") == 0) { + if (netdev->ipv6_gateway_auto) { + strprint(retv, inlen, "auto"); + } else if (netdev->ipv6_gateway) { + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, netdev->ipv6_gateway, buf, sizeof(buf)); + strprint(retv, inlen, "%s", buf); + } + } else if (strcmp(p1, "ipv6") == 0) { + struct lxc_list *it2; + lxc_list_for_each(it2, &netdev->ipv6) { + struct lxc_inetdev *i = it2->elem; + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET6, &i->addr, buf, sizeof(buf)); + strprint(retv, inlen, "%s\n", buf); + } + } + return fulllen; +} + +static int lxc_get_item_network(struct lxc_conf *c, char *retv, int inlen) +{ + int len, fulllen = 0; + struct lxc_list *it; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->network) { + struct lxc_netdev *n = it->elem; + const char *t = lxc_net_type_to_str(n->type); + strprint(retv, inlen, "%s\n", t ? t : "(invalid)"); + } + return fulllen; +} + +int lxc_get_config_item(struct lxc_conf *c, char *key, char *retv, int inlen) +{ + char *v = NULL; + + if (strcmp(key, "lxc.mount.entry") == 0) + return lxc_get_mount_entries(c, retv, inlen); + else if (strcmp(key, "lxc.mount") == 0) + v = c->fstab; + else if (strcmp(key, "lxc.tty") == 0) + return lxc_get_conf_int(c, retv, inlen, c->tty); + else if (strcmp(key, "lxc.pts") == 0) + return lxc_get_conf_int(c, retv, inlen, c->pts); + else if (strcmp(key, "lxc.devttydir") == 0) + v = c->ttydir; + else if (strcmp(key, "lxc.arch") == 0) + return lxc_get_arch_entry(c, retv, inlen); + else if (strcmp(key, "lxc.aa_profile") == 0) + v = c->aa_profile; + else if (strcmp(key, "lxc.cgroup") == 0) // all cgroup info + return lxc_get_cgroup_entry(c, retv, inlen, "all"); + else if (strncmp(key, "lxc.cgroup.", 11) == 0) // specific cgroup info + return lxc_get_cgroup_entry(c, retv, inlen, key + 11); + else if (strcmp(key, "lxc.utsname") == 0) + v = c->utsname->nodename; + else if (strcmp(key, "lxc.console") == 0) + v = c->console.path; + else if (strcmp(key, "lxc.rootfs.mount") == 0) + v = c->rootfs.mount; + else if (strcmp(key, "lxc.rootfs") == 0) + v = c->rootfs.path; + else if (strcmp(key, "lxc.pivotdir") == 0) + v = c->rootfs.pivot; + else if (strcmp(key, "lxc.cap.drop") == 0) + return lxc_get_item_cap_drop(c, retv, inlen); + else if (strncmp(key, "lxc.hook", 8) == 0) + return lxc_get_item_hooks(c, retv, inlen, key); + else if (strcmp(key, "lxc.network") == 0) + return lxc_get_item_network(c, retv, inlen); + else if (strncmp(key, "lxc.network.", 12) == 0) + return lxc_get_item_nic(c, retv, inlen, key + 12); + else return -1; + + if (!v) + return 0; + if (retv && inlen >= strlen(v) + 1) + strncpy(retv, v, strlen(v)+1); + return strlen(v); +} + +int lxc_clear_config_item(struct lxc_conf *c, char *key) +{ + if (strcmp(key, "lxc.network") == 0) + return lxc_clear_config_network(c); + else if (strncmp(key, "lxc.network.", 12) == 0) + return lxc_clear_nic(c, key + 12); + else if (strcmp(key, "lxc.cap.drop") == 0) + return lxc_clear_config_caps(c); + else if (strncmp(key, "lxc.cgroup", 10) == 0) + return lxc_clear_cgroups(c, key); + else if (strcmp(key, "lxc.mount.entries") == 0) + return lxc_clear_mount_entries(c); + else if (strcmp(key, "lxc.hook") == 0) + return lxc_clear_hooks(c); + + return -1; +} + +/* + * writing out a confile. + */ +void write_config(FILE *fout, struct lxc_conf *c) +{ + struct lxc_list *it; + int i; + + if (c->fstab) + fprintf(fout, "lxc.mount = %s\n", c->fstab); + lxc_list_for_each(it, &c->mount_list) { + fprintf(fout, "lxc.mount.entry = %s\n", (char *)it->elem); + } + if (c->tty) + fprintf(fout, "lxc.tty = %d\n", c->tty); + if (c->pts) + fprintf(fout, "lxc.pts = %d\n", c->pts); + if (c->ttydir) + fprintf(fout, "lxc.devttydir = %s\n", c->ttydir); + switch(c->personality) { + case PER_LINUX32: fprintf(fout, "lxc.arch = x86\n"); break; + case PER_LINUX: fprintf(fout, "lxc.arch = x86_64\n"); break; + default: break; + } + if (c->aa_profile) + fprintf(fout, "lxc.aa_profile = %s\n", c->aa_profile); + lxc_list_for_each(it, &c->cgroup) { + struct lxc_cgroup *cg = it->elem; + fprintf(fout, "lxc.cgroup.%s = %s\n", cg->subsystem, cg->value); + } + if (c->utsname) + fprintf(fout, "lxc.utsname = %s\n", c->utsname->nodename); + lxc_list_for_each(it, &c->network) { + struct lxc_netdev *n = it->elem; + const char *t = lxc_net_type_to_str(n->type); + struct lxc_list *it2; + fprintf(fout, "lxc.network.type = %s\n", t ? t : "(invalid)"); + if (n->flags & IFF_UP) + fprintf(fout, "lxc.network.flags = up\n"); + if (n->link) + fprintf(fout, "lxc.network.link = %s\n", n->link); + if (n->name) + fprintf(fout, "lxc.network.name = %s\n", n->name); + if (n->type == LXC_NET_MACVLAN) { + const char *mode; + switch (n->priv.macvlan_attr.mode) { + case MACVLAN_MODE_PRIVATE: mode = "private"; break; + case MACVLAN_MODE_VEPA: mode = "vepa"; break; + case MACVLAN_MODE_BRIDGE: mode = "bridge"; break; + default: mode = "(invalid)"; break; + } + fprintf(fout, "lxc.network.macvlan.mode = %s\n", mode); + } else if (n->type == LXC_NET_VETH) { + if (n->priv.veth_attr.pair) + fprintf(fout, "lxc.network.veth.pair = %s\n", + n->priv.veth_attr.pair); + } else if (n->type == LXC_NET_VLAN) { + fprintf(fout, "lxc.network.vlan.id = %d\n", n->priv.vlan_attr.vid); + } + if (n->upscript) + fprintf(fout, "lxc.network.script.up = %s\n", n->upscript); + if (n->hwaddr) + fprintf(fout, "lxc.network.hwaddr = %s\n", n->hwaddr); + if (n->mtu) + fprintf(fout, "lxc.network.mtu = %s\n", n->mtu); + if (n->ipv4_gateway_auto) + fprintf(fout, "lxc.network.ipv4.gateway = auto\n"); + else if (n->ipv4_gateway) { + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, n->ipv4_gateway, buf, sizeof(buf)); + fprintf(fout, "lxc.network.ipv4.gateway = %s\n", buf); + } + lxc_list_for_each(it2, &n->ipv4) { + struct lxc_inetdev *i = it2->elem; + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &i->addr, buf, sizeof(buf)); + fprintf(fout, "lxc.network.ipv4 = %s\n", buf); + } + if (n->ipv6_gateway_auto) + fprintf(fout, "lxc.network.ipv6.gateway = auto\n"); + else if (n->ipv6_gateway) { + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, n->ipv6_gateway, buf, sizeof(buf)); + fprintf(fout, "lxc.network.ipv6.gateway = %s\n", buf); + } + lxc_list_for_each(it2, &n->ipv6) { + struct lxc_inet6dev *i = it2->elem; + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET, &i->addr, buf, sizeof(buf)); + fprintf(fout, "lxc.network.ipv6 = %s\n", buf); + } + } + lxc_list_for_each(it, &c->caps) + fprintf(fout, "lxc.cap.drop = %s\n", (char *)it->elem); + for (i=0; ihooks[i]) + fprintf(fout, "lxc.hook.%s = %s\n", + lxchook_names[i], (char *)it->elem); + } + if (c->console.path) + fprintf(fout, "lxc.console = %s\n", c->console.path); + if (c->rootfs.path) + fprintf(fout, "lxc.rootfs = %s\n", c->rootfs.path); + if (c->rootfs.mount && strcmp(c->rootfs.mount, LXCROOTFSMOUNT) != 0) + fprintf(fout, "lxc.rootfs.mount = %s\n", c->rootfs.mount); + if (c->rootfs.pivot) + fprintf(fout, "lxc.pivotdir = %s\n", c->rootfs.pivot); +} diff --git a/src/lxc/confile.h b/src/lxc/confile.h index d2faa7502..7c5244748 100644 --- a/src/lxc/confile.h +++ b/src/lxc/confile.h @@ -27,6 +27,15 @@ struct lxc_conf; struct lxc_list; +typedef int (*config_cb)(const char *, char *, struct lxc_conf *); +struct lxc_config_t { + char *name; + config_cb cb; +}; + +extern struct lxc_config_t *lxc_getconfig(const char *key); +extern int lxc_list_nicconfigs(struct lxc_conf *c, char *key, char *retv, int inlen); +extern int lxc_listconfigs(char *retv, int inlen); extern int lxc_config_read(const char *file, struct lxc_conf *conf); extern int lxc_config_readline(char *buffer, struct lxc_conf *conf); @@ -37,4 +46,7 @@ extern int lxc_config_define_load(struct lxc_list *defines, /* needed for lxc-attach */ extern signed long lxc_config_parse_arch(const char *arch); +extern int lxc_get_config_item(struct lxc_conf *c, char *key, char *retv, int inlen); +extern int lxc_clear_config_item(struct lxc_conf *c, char *key); +extern void write_config(FILE *fout, struct lxc_conf *c); #endif diff --git a/src/lxc/lxc.h b/src/lxc/lxc.h index ae8a3f725..807a0cecb 100644 --- a/src/lxc/lxc.h +++ b/src/lxc/lxc.h @@ -84,6 +84,7 @@ extern int lxc_monitor_open(void); * data was readen, < 0 otherwise */ extern int lxc_monitor_read(int fd, struct lxc_msg *msg); +extern int lxc_monitor_read_timeout(int fd, struct lxc_msg *msg, int timeout); /* * Close the fd associated with the monitoring @@ -178,6 +179,30 @@ extern int lxc_restart(const char *, int, struct lxc_conf *, int); */ extern const char const *lxc_version(void); +/* + * Create and return a new lxccontainer struct. + */ +extern struct lxc_container *lxc_container_new(char *name); + +/* + * Returns 1 on success, 0 on failure. + */ +extern int lxc_container_get(struct lxc_container *c); + +/* + * Put a lxccontainer struct reference. + * Return -1 on error. + * Return 0 if this was not the last reference. + * If it is the last reference, free the lxccontainer and return 1. + */ +extern int lxc_container_put(struct lxc_container *c); + +/* + * Get a list of valid wait states. + * If states is NULL, simply return the number of states + */ +extern int lxc_get_wait_states(char **states); + #ifdef __cplusplus } #endif diff --git a/src/lxc/lxc_wait.c b/src/lxc/lxc_wait.c index d7a69bcf9..efb0a6984 100644 --- a/src/lxc/lxc_wait.c +++ b/src/lxc/lxc_wait.c @@ -77,25 +77,6 @@ Options :\n\ .checker = my_checker, }; -static int fillwaitedstates(char *strstates, int *states) -{ - char *token, *saveptr = NULL; - int state; - - token = strtok_r(strstates, "|", &saveptr); - while (token) { - - state = lxc_str2state(token); - if (state < 0) - return -1; - - states[state] = 1; - - token = strtok_r(NULL, "|", &saveptr); - } - return 0; -} - static void timeout_handler(int signal) { exit(-1); @@ -114,57 +95,5 @@ int main(int argc, char *argv[]) my_args.progname, my_args.quiet)) return -1; - if (fillwaitedstates(my_args.states, s)) - return -1; - - fd = lxc_monitor_open(); - if (fd < 0) - return -1; - - /* - * if container present, - * then check if already in requested state - */ - ret = -1; - state = lxc_getstate(my_args.name); - if (state < 0) { - goto out_close; - } else if ((state >= 0) && (s[state])) { - ret = 0; - goto out_close; - } - - signal(SIGALRM, timeout_handler); - alarm(my_args.timeout); - - for (;;) { - if (lxc_monitor_read(fd, &msg) < 0) - goto out_close; - - if (strcmp(my_args.name, msg.name)) - continue; - - switch (msg.type) { - case lxc_msg_state: - if (msg.value < 0 || msg.value >= MAX_STATE) { - ERROR("Receive an invalid state number '%d'", - msg.value); - goto out_close; - } - - if (s[msg.value]) { - alarm(0); - ret = 0; - goto out_close; - } - break; - default: - /* just ignore garbage */ - break; - } - } - -out_close: - lxc_monitor_close(fd); - return ret; + return lxc_wait(my_args.name, my_args.states, my_args.timeout); } diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c new file mode 100644 index 000000000..f61ba0060 --- /dev/null +++ b/src/lxc/lxccontainer.c @@ -0,0 +1,905 @@ +/* liblxcapi + * + * Copyright © 2012 Serge Hallyn . + * Copyright © 2012 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "lxc.h" +#include "state.h" +#include "lxccontainer.h" +#include "conf.h" +#include "config.h" +#include "confile.h" +#include "cgroup.h" +#include "commands.h" +#include "log.h" +#include +#include +#include +#include + +lxc_log_define(lxc_container, lxc); + +/* LOCKING + * c->privlock protects the struct lxc_container from multiple threads. + * c->slock protects the on-disk container data + * NOTHING mutexes two independent programs with their own struct + * lxc_container for the same c->name, between API calls. For instance, + * c->config_read(); c->start(); Between those calls, data on disk + * could change (which shouldn't bother the caller unless for instance + * the rootfs get moved). c->config_read(); update; c->config_write(); + * Two such updaters could race. The callers should therefore check their + * results. Trying to prevent that would necessarily expose us to deadlocks + * due to hung callers. So I prefer to keep the locks only within our own + * functions, not across functions. + * + * If you're going to fork while holding a lxccontainer, increment + * c->numthreads (under privlock) before forking. When deleting, + * decrement numthreads under privlock, then if it hits 0 you can delete. + * Do not ever use a lxccontainer whose numthreads you did not bump. + */ + +static void lxc_container_free(struct lxc_container *c) +{ + if (!c) + return; + + if (c->configfile) { + free(c->configfile); + c->configfile = NULL; + } + if (c->error_string) { + free(c->error_string); + c->error_string = NULL; + } + if (c->privlock) { + sem_destroy(c->privlock); + free(c->privlock); + c->privlock = NULL; + } + if (c->name) { + free(c->name); + c->name = NULL; + } + /* + * XXX TODO + * note, c->lxc_conf is going to have to be freed, but the fn + * to do that hasn't been written yet near as I can tell + */ + free(c); +} + +int lxc_container_get(struct lxc_container *c) +{ + if (!c) + return 0; + + if (lxclock(c->privlock, 0)) + return 0; + if (c->numthreads < 1) { + // bail without trying to unlock, bc the privlock is now probably + // in freed memory + return 0; + } + c->numthreads++; + lxcunlock(c->privlock); + return 1; +} + +int lxc_container_put(struct lxc_container *c) +{ + if (!c) + return -1; + if (lxclock(c->privlock, 0)) + return -1; + if (--c->numthreads < 1) { + lxcunlock(c->privlock); + lxc_container_free(c); + return 1; + } + lxcunlock(c->privlock); + return 0; +} + +static bool file_exists(char *f) +{ + struct stat statbuf; + + return stat(f, &statbuf) == 0; +} + +static bool lxcapi_is_defined(struct lxc_container *c) +{ + struct stat statbuf; + bool ret = false; + int statret; + + if (!c) + return false; + + if (lxclock(c->privlock, 0)) + return false; + if (!c->configfile) + goto out; + statret = stat(c->configfile, &statbuf); + if (statret != 0) + goto out; + ret = true; + +out: + lxcunlock(c->privlock); + return ret; +} + +static const char *lxcapi_state(struct lxc_container *c) +{ + const char *ret; + lxc_state_t s; + + if (!c) + return NULL; + if (lxclock(c->slock, 0)) + return NULL; + s = lxc_getstate(c->name); + ret = lxc_state2str(s); + lxcunlock(c->slock); + + return ret; +} + +static bool lxcapi_is_running(struct lxc_container *c) +{ + const char *s; + + if (!c) + return false; + s = lxcapi_state(c); + if (!s || strcmp(s, "STOPPED") == 0) + return false; + return true; +} + +static bool lxcapi_freeze(struct lxc_container *c) +{ + int ret; + if (!c) + return false; + + if (lxclock(c->slock, 0)) + return false; + ret = lxc_freeze(c->name); + lxcunlock(c->slock); + if (ret) + return false; + return true; +} + +static bool lxcapi_unfreeze(struct lxc_container *c) +{ + int ret; + if (!c) + return false; + + if (lxclock(c->slock, 0)) + return false; + ret = lxc_unfreeze(c->name); + lxcunlock(c->slock); + if (ret) + return false; + return true; +} + +static pid_t lxcapi_init_pid(struct lxc_container *c) +{ + pid_t ret; + if (!c) + return -1; + + if (lxclock(c->slock, 0)) + return -1; + ret = get_init_pid(c->name); + lxcunlock(c->slock); + return ret; +} + +static bool lxcapi_load_config(struct lxc_container *c, char *alt_file) +{ + bool ret = false; + char *fname; + if (!c) + return false; + + fname = c->configfile; + if (alt_file) + fname = alt_file; + if (!fname) + return false; + if (lxclock(c->slock, 0)) + return false; + if (!c->lxc_conf) + c->lxc_conf = lxc_conf_init(); + if (c->lxc_conf && !lxc_config_read(fname, c->lxc_conf)) + ret = true; + lxcunlock(c->slock); + return ret; +} + +static void lxcapi_want_daemonize(struct lxc_container *c) +{ + if (!c) + return; + c->daemonize = 1; +} + +/* + * I can't decide if it'd be more convenient for callers if we accept '...', + * or a null-terminated array (i.e. execl vs execv) + */ +static bool lxcapi_start(struct lxc_container *c, int useinit, char ** argv) +{ + int ret; + struct lxc_conf *conf; + int daemonize = 0; + char *default_args[] = { + "/sbin/init", + '\0', + }; + + /* container exists */ + if (!c) + return false; + /* container has been setup */ + if (!c->lxc_conf) + return false; + + /* is this app meant to be run through lxcinit, as in lxc-execute? */ + if (useinit && !argv) + return false; + + if (lxclock(c->privlock, 0)) + return false; + conf = c->lxc_conf; + daemonize = c->daemonize; + lxcunlock(c->privlock); + + if (useinit) { + ret = lxc_execute(c->name, argv, 1, conf); + return ret == 0 ? true : false; + } + + if (!argv) + argv = default_args; + + /* + * say, I'm not sure - what locks do we want here? Any? + * Is liblxc's locking enough here to protect the on disk + * container? We don't want to exclude things like lxc_info + * while container is running... + */ + if (daemonize) { + if (!lxc_container_get(c)) + return false; + pid_t pid = fork(); + if (pid < 0) { + lxc_container_put(c); + return false; + } + if (pid != 0) + return true; + /* like daemon(), chdir to / and redirect 0,1,2 to /dev/null */ + chdir("/"); + close(0); + close(1); + close(2); + open("/dev/null", O_RDONLY); + open("/dev/null", O_RDWR); + open("/dev/null", O_RDWR); + setsid(); + } + + if (putenv("container=lxc")) { + fprintf(stderr, "failed to set environment variable"); + if (daemonize) { + lxc_container_put(c); + exit(1); + } else { + return false; + } + } + +reboot: + conf->reboot = 0; + ret = lxc_start(c->name, argv, conf); + + if (conf->reboot) { + INFO("container requested reboot"); + conf->reboot = 0; + if (conf->maincmd_fd) + close(conf->maincmd_fd); + conf->maincmd_fd = 0; + goto reboot; + } + + if (daemonize) { + lxc_container_put(c); + exit (ret == 0 ? true : false); + } else { + return (ret == 0 ? true : false); + } +} + +/* + * note there MUST be an ending NULL + */ +static bool lxcapi_startl(struct lxc_container *c, int useinit, ...) +{ + va_list ap; + char **inargs = NULL, **temp; + int n_inargs = 0; + bool bret = false; + + /* container exists */ + if (!c) + return false; + + /* build array of arguments if any */ + va_start(ap, useinit); + while (1) { + char *arg; + arg = va_arg(ap, char *); + if (!arg) + break; + n_inargs++; + temp = realloc(inargs, n_inargs * sizeof(*inargs)); + if (!temp) + goto out; + inargs = temp; + inargs[n_inargs - 1] = strdup(arg); // not sure if it's safe not to copy + } + va_end(ap); + + /* add trailing NULL */ + if (n_inargs) { + n_inargs++; + temp = realloc(inargs, n_inargs * sizeof(*inargs)); + if (!temp) + goto out; + inargs = temp; + inargs[n_inargs - 1] = NULL; + } + + bret = lxcapi_start(c, useinit, inargs); + +out: + if (inargs) { + int i; + for (i = 0; i < n_inargs; i++) { + if (inargs[i]) + free(inargs[i]); + } + free(inargs); + } + + return bret; +} + +static bool lxcapi_stop(struct lxc_container *c) +{ + int ret; + + if (!c) + return false; + + ret = lxc_stop(c->name); + + return ret == 0; +} + +static bool lxcapi_wait(struct lxc_container *c, char *state, int timeout) +{ + int ret; + + if (!c) + return false; + + ret = lxc_wait(c->name, state, timeout); + return ret == 0; +} + +static bool valid_template(char *t) +{ + struct stat statbuf; + int statret; + + statret = stat(t, &statbuf); + if (statret == 0) + return true; + return false; +} + +/* + * create the standard expected container dir + */ +static bool create_container_dir(struct lxc_container *c) +{ + char *s; + int len, ret; + + len = strlen(LXCPATH) + strlen(c->name) + 2; + s = malloc(len); + if (!s) + return false; + ret = snprintf(s, len, "%s/%s", LXCPATH, c->name); + if (ret < 0 || ret >= len) { + free(s); + return false; + } + ret = mkdir(s, 0755); + if (ret) { + if (errno == EEXIST) + ret = 0; + else + SYSERROR("failed to create container path for %s\n", c->name); + } + free(s); + return ret == 0; +} + +/* + * backing stores not (yet) supported + * for ->create, argv contains the arguments to pass to the template, + * terminated by NULL. If no arguments, you can just pass NULL. + */ +static bool lxcapi_create(struct lxc_container *c, char *t, char **argv) +{ + bool bret = false; + pid_t pid; + int ret, status; + char *tpath = NULL; + int len, nargs = 0; + char **newargv; + + if (!c) + return false; + + len = strlen(LXCTEMPLATEDIR) + strlen(t) + strlen("/lxc-") + 1; + tpath = malloc(len); + if (!tpath) + return false; + ret = snprintf(tpath, len, "%s/lxc-%s", LXCTEMPLATEDIR, t); + if (ret < 0 || ret >= len) + goto out; + if (!valid_template(tpath)) { + ERROR("bad template: %s\n", t); + goto out; + } + + if (!create_container_dir(c)) + goto out; + + if (!c->save_config(c, NULL)) { + ERROR("failed to save starting configuration for %s\n", c->name); + goto out; + } + + /* we're going to fork. but since we'll wait for our child, we + don't need to lxc_container_get */ + + if (lxclock(c->slock, 0)) { + ERROR("failed to grab global container lock for %s\n", c->name); + goto out; + } + + pid = fork(); + if (pid < 0) { + SYSERROR("failed to fork task for container creation template\n"); + goto out_unlock; + } + + if (pid == 0) { // child + char *patharg, *namearg; + int i; + + close(0); + close(1); + close(2); + open("/dev/null", O_RDONLY); + open("/dev/null", O_RDWR); + open("/dev/null", O_RDWR); + + /* + * create our new array, pre-pend the template name and + * base args + */ + if (argv) + for (; argv[nargs]; nargs++) ; + nargs += 3; // template, path and name args + newargv = malloc(nargs * sizeof(*newargv)); + if (!newargv) + exit(1); + newargv[0] = t; + + len = strlen(LXCPATH) + strlen(c->name) + strlen("--path=") + 2; + patharg = malloc(len); + if (!patharg) + exit(1); + ret = snprintf(patharg, len, "--path=%s/%s", LXCPATH, c->name); + if (ret < 0 || ret >= len) + exit(1); + newargv[1] = patharg; + len = strlen("--name=") + strlen(c->name) + 1; + namearg = malloc(len); + if (!namearg) + exit(1); + ret = snprintf(namearg, len, "--name=%s", c->name); + if (ret < 0 || ret >= len) + exit(1); + newargv[2] = namearg; + + /* add passed-in args */ + if (argv) + for (i = 3; i < nargs; i++) + newargv[i] = argv[i-3]; + + /* add trailing NULL */ + nargs++; + newargv = realloc(newargv, nargs * sizeof(*newargv)); + if (!newargv) + exit(1); + newargv[nargs - 1] = NULL; + + /* execute */ + ret = execv(tpath, newargv); + SYSERROR("failed to execute template %s", tpath); + exit(1); + } + +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == -EINTR) + goto again; + SYSERROR("waitpid failed"); + goto out_unlock; + } + if (ret != pid) + goto again; + if (!WIFEXITED(status)) { // did not exit normally + // we could set an error code and string inside the + // container_struct here if we like + ERROR("container creation template exited abnormally\n"); + goto out_unlock; + } + + if (WEXITSTATUS(status) != 0) + ERROR("container creation template for %s exited with %d\n", + c->name, WEXITSTATUS(status)); + else + bret = true; + +out_unlock: + lxcunlock(c->slock); +out: + if (tpath) + free(tpath); + return bret; +} + +static bool lxcapi_shutdown(struct lxc_container *c, int timeout) +{ + bool retv; + pid_t pid; + + if (!c) + return false; + + if (!timeout) + timeout = -1; + if (!c->is_running(c)) + return true; + pid = c->init_pid(c); + if (pid <= 0) + return true; + kill(pid, SIGPWR); + retv = c->wait(c, "STOPPED", timeout); + if (timeout > 0) { + c->stop(c); + retv = c->wait(c, "STOPPED", 0); // 0 means don't wait + } + return retv; +} + +static bool lxcapi_createl(struct lxc_container *c, char *t, ...) +{ + bool bret = false; + char **args = NULL, **temp; + va_list ap; + int nargs = 0; + + if (!c) + return false; + + /* + * since we're going to wait for create to finish, I don't think we + * need to get a copy of the arguments. + */ + va_start(ap, t); + while (1) { + char *arg; + arg = va_arg(ap, char *); + if (!arg) + break; + nargs++; + temp = realloc(args, nargs * sizeof(*args)); + if (!temp) + goto out; + args = temp; + args[nargs - 1] = arg; + } + va_end(ap); + + bret = c->create(c, t, args); + +out: + if (args) + free(args); + return bret; +} + +static bool lxcapi_clear_config_item(struct lxc_container *c, char *key) +{ + int ret; + + if (!c || !c->lxc_conf) + return false; + if (lxclock(c->privlock, 0)) { + return false; + } + ret = lxc_clear_config_item(c->lxc_conf, key); + lxcunlock(c->privlock); + return ret == 0; +} + +static int lxcapi_get_config_item(struct lxc_container *c, char *key, char *retv, int inlen) +{ + int ret; + + if (!c || !c->lxc_conf) + return -1; + if (lxclock(c->privlock, 0)) { + return -1; + } + ret = lxc_get_config_item(c->lxc_conf, key, retv, inlen); + lxcunlock(c->privlock); + return ret; +} + +static int lxcapi_get_keys(struct lxc_container *c, char *key, char *retv, int inlen) +{ + if (!key) + return lxc_listconfigs(retv, inlen); + /* + * Support 'lxc.network.', i.e. 'lxc.network.0' + * This is an intelligent result to show which keys are valid given + * the type of nic it is + */ + if (!c || !c->lxc_conf) + return -1; + if (lxclock(c->privlock, 0)) + return -1; + int ret = -1; + if (strncmp(key, "lxc.network.", 12) == 0) + ret = lxc_list_nicconfigs(c->lxc_conf, key, retv, inlen); + lxcunlock(c->privlock); + return ret; +} + + +/* default config file - should probably come through autoconf */ +#define LXC_DEFAULT_CONFIG "/etc/lxc/lxc.conf" +static bool lxcapi_save_config(struct lxc_container *c, char *alt_file) +{ + if (!alt_file) + alt_file = c->configfile; + if (!alt_file) + return false; // should we write to stdout if no file is specified? + if (!c->lxc_conf) + if (!c->load_config(c, LXC_DEFAULT_CONFIG)) { + ERROR("Error loading default configuration file %s while saving %s\n", LXC_DEFAULT_CONFIG, c->name); + return false; + } + + FILE *fout = fopen(alt_file, "w"); + if (!fout) + return false; + if (lxclock(c->privlock, 0)) { + fclose(fout); + return false; + } + write_config(fout, c->lxc_conf); + fclose(fout); + lxcunlock(c->privlock); + return true; +} + +static bool lxcapi_destroy(struct lxc_container *c) +{ + pid_t pid; + int ret, status; + + if (!c) + return false; + + pid = fork(); + if (pid < 0) + return false; + if (pid == 0) { // child + ret = execlp("lxc-destroy", "lxc-destroy", "-n", c->name, NULL); + perror("execl"); + exit(1); + } + +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == -EINTR) + goto again; + perror("waitpid"); + return false; + } + if (ret != pid) + goto again; + if (!WIFEXITED(status)) { // did not exit normally + // we could set an error code and string inside the + // container_struct here if we like + return false; + } + + return WEXITSTATUS(status) == 0; +} + +static bool lxcapi_set_config_item(struct lxc_container *c, char *key, char *v) +{ + int ret; + bool b = false; + struct lxc_config_t *config; + + if (!c) + return false; + + if (lxclock(c->privlock, 0)) + return false; + + if (!c->lxc_conf) + c->lxc_conf = lxc_conf_init(); + if (!c->lxc_conf) + goto err; + config = lxc_getconfig(key); + if (!config) + goto err; + ret = config->cb(key, v, c->lxc_conf); + if (!ret) + b = true; + +err: + lxcunlock(c->privlock); + return b; +} + +static char *lxcapi_config_file_name(struct lxc_container *c) +{ + if (!c || !c->configfile) + return NULL; + return strdup(c->configfile); +} + +struct lxc_container *lxc_container_new(char *name) +{ + struct lxc_container *c; + int ret, len; + + c = malloc(sizeof(*c)); + if (!c) { + fprintf(stderr, "failed to malloc lxc_container\n"); + return NULL; + } + memset(c, 0, sizeof(*c)); + + c->name = malloc(strlen(name)+1); + if (!c->name) { + fprintf(stderr, "Error allocating lxc_container name\n"); + goto err; + } + strcpy(c->name, name); + + c->numthreads = 1; + c->slock = lxc_newlock(name); + if (!c->slock) { + fprintf(stderr, "failed to create lock\n"); + goto err; + } + + c->privlock = lxc_newlock(NULL); + if (!c->privlock) { + fprintf(stderr, "failed to alloc privlock\n"); + goto err; + } + + len = strlen(LXCDIR)+strlen(c->name)+strlen("/config")+2; + c->configfile = malloc(len); + if (!c->configfile) { + fprintf(stderr, "Error allocating config file pathname\n"); + goto err; + } + ret = snprintf(c->configfile, len, "%s/%s/config", LXCDIR, c->name); + if (ret < 0 || ret >= len) { + fprintf(stderr, "Error printing out config file name\n"); + goto err; + } + + if (file_exists(c->configfile)) + lxcapi_load_config(c, NULL); + + // assign the member functions + c->is_defined = lxcapi_is_defined; + c->state = lxcapi_state; + c->is_running = lxcapi_is_running; + c->freeze = lxcapi_freeze; + c->unfreeze = lxcapi_unfreeze; + c->init_pid = lxcapi_init_pid; + c->load_config = lxcapi_load_config; + c->want_daemonize = lxcapi_want_daemonize; + c->start = lxcapi_start; + c->startl = lxcapi_startl; + c->stop = lxcapi_stop; + c->config_file_name = lxcapi_config_file_name; + c->wait = lxcapi_wait; + c->set_config_item = lxcapi_set_config_item; + c->destroy = lxcapi_destroy; + c->save_config = lxcapi_save_config; + c->get_keys = lxcapi_get_keys; + c->create = lxcapi_create; + c->createl = lxcapi_createl; + c->shutdown = lxcapi_shutdown; + c->clear_config_item = lxcapi_clear_config_item; + c->get_config_item = lxcapi_get_config_item; + + /* we'll allow the caller to update these later */ + if (lxc_log_init("/var/log/lxccontainer.log", "trace", "lxc_container", 0)) { + fprintf(stderr, "failed to open log\n"); + goto err; + } + + /* + * default configuration file is $LXCDIR/$NAME/config + */ + + return c; + +err: + lxc_container_free(c); + return NULL; +} + +int lxc_get_wait_states(char **states) +{ + int i; + + if (states) + for (i=0; i +#include + +#include + +struct lxc_container { + // private fields + char *name; + char *configfile; + sem_t *slock; + sem_t *privlock; + int numthreads; /* protected by privlock. */ + struct lxc_conf *lxc_conf; // maybe we'll just want the whole lxc_handler? + + // public fields + char *error_string; + int error_num; + int daemonize; + +#define LXCDIR "/var/lib/lxc" + bool (*is_defined)(struct lxc_container *c); // did /var/lib/lxc/$name/config exist + const char *(*state)(struct lxc_container *c); + bool (*is_running)(struct lxc_container *c); // true so long as defined and not stopped + bool (*freeze)(struct lxc_container *c); + bool (*unfreeze)(struct lxc_container *c); + pid_t (*init_pid)(struct lxc_container *c); + bool (*load_config)(struct lxc_container *c, char *alt_file); + /* The '...' is the command line. If provided, it must be ended with a NULL */ + bool (*start)(struct lxc_container *c, int useinit, char ** argv); + bool (*startl)(struct lxc_container *c, int useinit, ...); + bool (*stop)(struct lxc_container *c); + void (*want_daemonize)(struct lxc_container *c); + // Return current config file name. The result is strdup()d, so free the result. + char *(*config_file_name)(struct lxc_container *c); + // for wait, timeout == -1 means wait forever, timeout == 0 means don't wait. + // otherwise timeout is seconds to wait. + bool (*wait)(struct lxc_container *c, char *state, int timeout); + bool (*set_config_item)(struct lxc_container *c, char *key, char *value); + bool (*destroy)(struct lxc_container *c); + bool (*save_config)(struct lxc_container *c, char *alt_file); + bool (*create)(struct lxc_container *c, char *t, char **argv); + bool (*createl)(struct lxc_container *c, char *t, ...); + /* send SIGPWR. if timeout is not 0 or -1, do a hard stop after timeout seconds */ + bool (*shutdown)(struct lxc_container *c, int timeout); + /* clear all network or capability items in the in-memory configuration */ + bool (*clear_config_item)(struct lxc_container *c, char *key); + /* print a config item to a in-memory string allocated by the caller. Return + * the length which was our would be printed. */ + int (*get_config_item)(struct lxc_container *c, char *key, char *retv, int inlen); + int (*get_keys)(struct lxc_container *c, char *key, char *retv, int inlen); + +#if 0 + bool (*commit_cgroups)(struct lxc_container *c); + bool (*reread_cgroups)(struct lxc_container *c); + // question with clone: how do we handle non-standard config file in orig? + struct lxc_container (*clone)(struct container *c); + int (*ns_attach)(struct lxc_container *c, int ns_mask); + // we'll need some plumbing to support lxc-console +#endif +}; + +struct lxc_container *lxc_container_new(char *name); +int lxc_container_get(struct lxc_container *c); +int lxc_container_put(struct lxc_container *c); +int lxc_get_wait_states(char **states); + +#if 0 +char ** lxc_get_valid_keys(); +char ** lxc_get_valid_values(char *key); +#endif diff --git a/src/lxc/lxclock.c b/src/lxc/lxclock.c new file mode 100644 index 000000000..95b447078 --- /dev/null +++ b/src/lxc/lxclock.c @@ -0,0 +1,105 @@ +/* liblxcapi + * + * Copyright © 2012 Serge Hallyn . + * Copyright © 2012 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "lxclock.h" +#include + +#define OFLAG (O_CREAT | O_RDWR) +#define SEMMODE 0660 +#define SEMVALUE 1 +#define SEMVALUE_LOCKED 0 +#define LXCLOCK_PREFIX "/lxcapi." + + +static char *lxclock_name(char *container) +{ + int ret; + int len = strlen(container) + strlen(LXCLOCK_PREFIX) + 1; + char *dest = malloc(len); + if (!dest) + return NULL; + ret = snprintf(dest, len, "%s%s", LXCLOCK_PREFIX, container); + if (ret < 0 || ret >= len) { + free(dest); + return NULL; + } + return dest; +} + +static void lxcfree_name(char *name) +{ + if (name) + free(name); +} + +static sem_t *lxc_new_unnamed_sem(void) +{ + sem_t *s; + int ret; + + s = malloc(sizeof(*s)); + if (!s) + return NULL; + ret = sem_init(s, 0, 1); + if (ret) + return NULL; + return s; +} + +sem_t *lxc_newlock(char *name) +{ + char *lname; + sem_t *lock; + + if (!name) + return lxc_new_unnamed_sem(); + + lname = lxclock_name(name); + if (!lname) + return NULL; + lock = sem_open(lname, OFLAG, SEMMODE, SEMVALUE); + lxcfree_name(lname); + if (lock == SEM_FAILED) + return NULL; + return lock; +} + +int lxclock(sem_t *sem, int timeout) +{ + int ret; + + if (!timeout) { + ret = sem_wait(sem); + } else { + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) == -1) + return -2; + ts.tv_sec += timeout; + ret = sem_timedwait(sem, &ts); + } + + return ret; +} + +int lxcunlock(sem_t *sem) +{ + if (!sem) + return -2; + return sem_post(sem); +} diff --git a/src/lxc/lxclock.h b/src/lxc/lxclock.h new file mode 100644 index 000000000..35df18256 --- /dev/null +++ b/src/lxc/lxclock.h @@ -0,0 +1,61 @@ +/* liblxcapi + * + * Copyright © 2012 Serge Hallyn . + * Copyright © 2012 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include /* For O_* constants */ +#include /* For mode constants */ +#include +#include +#include + +/* + * lxc_newlock: + * if name is not given, create an unnamed semaphore. We use these + * to protect against racing threads. + * Note that an unnamed sem was malloced by us and needs to be freed. + * + * If name is given, it is prepended with '/lxcapi.', and used as the + * name for a system-wide (well, ipcns-wide) semaphore. We use that + * to protect the containers as represented on disk. + * A named sem should not be freed. + * + * XXX TODO + * We should probably introduce a lxclock_close() which detecs the type + * of lock and calls sem_close() or sem_destroy()+free() not as appropriate. + * For now, it is up to the caller to do so. + * + * sem is initialized to value of 1 + * + * return NULL on failure, else a sem_t * which can be passed to + * lxclock() and lxcunlock(). + */ +extern sem_t *lxc_newlock(char *name); + +/* + * lxclock: take an existing lock. If timeout is 0, wait + * indefinately. Otherwise use given timeout. + * return 0 if we got the lock, -2 on failure to set timeout, or -1 + * otherwise in which case errno will be set by sem_wait()). + */ +extern int lxclock(sem_t *sem, int timeout); + +/* + * lxcunlock: unlock given sem. Return 0 on success. Otherwise returns + * -1 and sem_post will leave errno set. + */ +extern int lxcunlock(sem_t *lock); diff --git a/src/lxc/monitor.c b/src/lxc/monitor.c index e51b59aac..2eb03978f 100644 --- a/src/lxc/monitor.c +++ b/src/lxc/monitor.c @@ -98,11 +98,28 @@ int lxc_monitor_open(void) return fd; } -int lxc_monitor_read(int fd, struct lxc_msg *msg) +/* timeout of 0 means return immediately; -1 means wait forever */ +int lxc_monitor_read_timeout(int fd, struct lxc_msg *msg, int timeout) { struct sockaddr_un from; socklen_t len = sizeof(from); int ret; + fd_set rfds; + struct timeval tv; + + if (timeout != -1) { + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + tv.tv_sec = timeout; + tv.tv_usec = 0; + + ret = select(fd+1, &rfds, NULL, NULL, &tv); + if (ret == -1) + return -1; + else if (!ret) + return -2; // timed out + } ret = recvfrom(fd, msg, sizeof(*msg), 0, (struct sockaddr *)&from, &len); @@ -114,6 +131,11 @@ int lxc_monitor_read(int fd, struct lxc_msg *msg) return ret; } +int lxc_monitor_read(int fd, struct lxc_msg *msg) +{ + return lxc_monitor_read_timeout(fd, msg, -1); +} + int lxc_monitor_close(int fd) { return close(fd); diff --git a/src/lxc/network.c b/src/lxc/network.c index c8aecfb82..f97e685c0 100644 --- a/src/lxc/network.c +++ b/src/lxc/network.c @@ -47,6 +47,7 @@ #include "nl.h" #include "network.h" +#include "conf.h" #ifndef IFLA_LINKMODE # define IFLA_LINKMODE 17 @@ -1004,3 +1005,18 @@ int lxc_bridge_attach(const char *bridge, const char *ifname) return err; } + +static char* lxc_network_types[LXC_NET_MAXCONFTYPE + 1] = { + [LXC_NET_VETH] = "veth", + [LXC_NET_MACVLAN] = "macvlan", + [LXC_NET_VLAN] = "vlan", + [LXC_NET_PHYS] = "phys", + [LXC_NET_EMPTY] = "empty", +}; + +const char *lxc_net_type_to_str(int type) +{ + if (type < 0 || type > LXC_NET_MAXCONFTYPE) + return NULL; + return lxc_network_types[type]; +} diff --git a/src/lxc/network.h b/src/lxc/network.h index 0fd5e5d36..5ba059a73 100644 --- a/src/lxc/network.h +++ b/src/lxc/network.h @@ -122,4 +122,5 @@ extern int lxc_neigh_proxy_on(const char *name, int family); */ extern int lxc_neigh_proxy_off(const char *name, int family); +extern const char *lxc_net_type_to_str(int type); #endif diff --git a/src/lxc/state.c b/src/lxc/state.c index 99836562f..466dc0a04 100644 --- a/src/lxc/state.c +++ b/src/lxc/state.c @@ -31,9 +31,11 @@ #include #include +#include #include #include #include +#include #include "commands.h" #include "config.h" @@ -162,3 +164,108 @@ out: return ret; } +static int fillwaitedstates(char *strstates, int *states) +{ + char *token, *saveptr = NULL; + int state; + + token = strtok_r(strstates, "|", &saveptr); + while (token) { + + state = lxc_str2state(token); + if (state < 0) + return -1; + + states[state] = 1; + + token = strtok_r(NULL, "|", &saveptr); + } + return 0; +} + +extern int lxc_wait(char *lxcname, char *states, int timeout) +{ + struct lxc_msg msg; + int state, ret; + int s[MAX_STATE] = { }, fd; + + if (fillwaitedstates(states, s)) + return -1; + + fd = lxc_monitor_open(); + if (fd < 0) + return -1; + + /* + * if container present, + * then check if already in requested state + */ + ret = -1; + state = lxc_getstate(lxcname); + if (state < 0) { + goto out_close; + } else if ((state >= 0) && (s[state])) { + ret = 0; + goto out_close; + } + + for (;;) { + int elapsed_time, curtime; + struct timeval tv; + int stop = 0; + int retval; + + if (timeout != -1) { + retval = gettimeofday(&tv, NULL); + if (retval) + goto out_close; + curtime = tv.tv_sec; + } + if (lxc_monitor_read_timeout(fd, &msg, timeout) < 0) + goto out_close; + + if (timeout != -1) { + retval = gettimeofday(&tv, NULL); + if (retval) + goto out_close; + elapsed_time = tv.tv_sec - curtime; + if (timeout - elapsed_time <= 0) + stop = 1; + timeout -= elapsed_time; + } + + if (strcmp(lxcname, msg.name)) { + if (stop) { + ret = -2; + goto out_close; + } + continue; + } + + switch (msg.type) { + case lxc_msg_state: + if (msg.value < 0 || msg.value >= MAX_STATE) { + ERROR("Receive an invalid state number '%d'", + msg.value); + goto out_close; + } + + if (s[msg.value]) { + ret = 0; + goto out_close; + } + break; + default: + if (stop) { + ret = -2; + goto out_close; + } + /* just ignore garbage */ + break; + } + } + +out_close: + lxc_monitor_close(fd); + return ret; +} diff --git a/src/lxc/state.h b/src/lxc/state.h index f64169873..77b3424cb 100644 --- a/src/lxc/state.h +++ b/src/lxc/state.h @@ -33,5 +33,6 @@ extern lxc_state_t lxc_getstate(const char *name); extern lxc_state_t lxc_str2state(const char *state); extern const char *lxc_state2str(lxc_state_t state); +extern int lxc_wait(char *lxcname, char *states, int timeout); #endif diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am new file mode 100644 index 000000000..b880bc421 --- /dev/null +++ b/src/tests/Makefile.am @@ -0,0 +1,17 @@ +LDADD = ../lxc/liblxc.so -lrt +containertests_SOURCES = containertests.c +locktests_SOURCES = locktests.c +startone_SOURCES = startone.c +destroytest_SOURCES = destroytest.c +saveconfig_SOURCES = saveconfig.c +createtest_SOURCES = createtest.c +shutdowntest_SOURCES = shutdowntest.c +get_item_SOURCES = get_item.c +getkeys_SOURCES = getkeys.c + +AM_CFLAGS=-I$(top_srcdir)/src \ + -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ + -DLXCPATH=\"$(LXCPATH)\" \ + -DLXCINITDIR=\"$(LXCINITDIR)\" + +bin_PROGRAMS = containertests locktests startone destroytest saveconfig createtest shutdowntest get_item getkeys diff --git a/src/tests/containertests.c b/src/tests/containertests.c new file mode 100644 index 000000000..ef3cda65b --- /dev/null +++ b/src/tests/containertests.c @@ -0,0 +1,257 @@ +/* liblxcapi + * + * Copyright © 2012 Serge Hallyn . + * Copyright © 2012 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "../lxc/lxccontainer.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MYNAME "lxctest1" + +static int destroy_busybox(void) +{ + int status, ret; + pid_t pid = fork(); + + if (pid < 0) { + perror("fork"); + return -1; + } + if (pid == 0) { + ret = execlp("lxc-destroy", "lxc-destroy", "-f", "-n", MYNAME, NULL); + // Should not return + perror("execl"); + exit(1); + } +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == -EINTR) + goto again; + perror("waitpid"); + return -1; + } + if (ret != pid) + goto again; + if (!WIFEXITED(status)) { // did not exit normally + fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__); + return -1; + } + return WEXITSTATUS(status); +} + +static int create_busybox(void) +{ + int status, ret; + pid_t pid = fork(); + + if (pid < 0) { + perror("fork"); + return -1; + } + if (pid == 0) { + ret = execlp("lxc-create", "lxc-create", "-t", "busybox", "-f", "/etc/lxc/lxc.conf", "-n", MYNAME, NULL); + // Should not return + perror("execl"); + exit(1); + } +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == -EINTR) + goto again; + perror("waitpid"); + return -1; + } + if (ret != pid) + goto again; + if (!WIFEXITED(status)) { // did not exit normally + fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__); + return -1; + } + return WEXITSTATUS(status); +} + +int main(int argc, char *argv[]) +{ + struct lxc_container *c; + int ret = 0; + const char *s; + bool b; + char *str; + + ret = 1; + /* test refcounting */ + c = lxc_container_new(MYNAME); + if (!c) { + fprintf(stderr, "%d: error creating lxc_container %s\n", __LINE__, MYNAME); + goto out; + } + if (!lxc_container_get(c)) { + fprintf(stderr, "%d: error getting refcount\n", __LINE__); + goto out; + } + /* peek in, inappropriately, make sure refcount is a we'd like */ + if (c->numthreads != 2) { + fprintf(stderr, "%d: refcount is %d, not %d\n", __LINE__, c->numthreads, 2); + goto out; + } + if (strcmp(c->name, MYNAME) != 0) { + fprintf(stderr, "%d: container has wrong name (%s not %s)\n", __LINE__, c->name, MYNAME); + goto out; + } + str = c->config_file_name(c); +#define CONFIGFNAM "/var/lib/lxc/" MYNAME "/config" + if (!str || strcmp(str, CONFIGFNAM)) { + fprintf(stderr, "%d: got wrong config file name (%s, not %s)\n", __LINE__, str, CONFIGFNAM); + goto out; + } + free(str); + free(c->configfile); + c->configfile = NULL; + str = c->config_file_name(c); + if (str) { + fprintf(stderr, "%d: config file name was not NULL as it should have been\n", __LINE__); + goto out; + } + if (lxc_container_put(c) != 0) { + fprintf(stderr, "%d: c was freed on non-final put\n", __LINE__); + goto out; + } + if (c->numthreads != 1) { + fprintf(stderr, "%d: refcount is %d, not %d\n", __LINE__, c->numthreads, 1); + goto out; + } + if (lxc_container_put(c) != 1) { + fprintf(stderr, "%d: c was not freed on final put\n", __LINE__); + goto out; + } + + /* test a real container */ + c = lxc_container_new(MYNAME); + if (!c) { + fprintf(stderr, "%d: error creating lxc_container %s\n", __LINE__, MYNAME); + ret = 1; + goto out; + } + + if (c->lxc_conf != NULL) { + fprintf(stderr, "%d: lxc_conf is not NULL as it should be\n", __LINE__); + ret = 1; + goto out; + } + b = c->is_defined(c); + if (b) { + fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME); + goto out; + } + + s = c->state(c); + if (s && strcmp(s, "STOPPED") != 0) { + // liblxc says a container is STOPPED if it doesn't exist. That's because + // the container may be an application container - it's not wrong, just + // sometimes unintuitive. + fprintf(stderr, "%d: %s thinks it is in state %s\n", __LINE__, c->name, s); + goto out; + } + + // create a container + // the liblxc api does not support creation - it probably will eventually, + // but not yet. + // So we just call out to lxc-create. We'll create a busybox container. + ret = create_busybox(); + if (ret) { + fprintf(stderr, "%d: failed to create a busybox container\n", __LINE__); + goto out; + } + + b = c->is_defined(c); + if (!b) { + fprintf(stderr, "%d: %s thought it was not defined\n", __LINE__, MYNAME); + goto out; + } + + s = c->state(c); + if (!s || strcmp(s, "STOPPED")) { + fprintf(stderr, "%d: %s is in state %s, not in STOPPED.\n", __LINE__, c->name, s ? s : "undefined"); + goto out; + } + + b = c->load_config(c, NULL); + if (!b) { + fprintf(stderr, "%d: %s failed to read its config\n", __LINE__, c->name); + goto out; + } + + // test wait states + int numstates = lxc_get_wait_states(NULL); + if (numstates != MAX_STATE) { + fprintf(stderr, "%d: lxc_get_wait_states gave %d not %d\n", __LINE__, numstates, MAX_STATE); + goto out; + } + char **sstr = malloc(numstates * sizeof(char *)); + numstates = lxc_get_wait_states(sstr); + int i; + for (i=0; iwant_daemonize(c); + if (!c->startl(c, 0, NULL, NULL)) { + fprintf(stderr, "%d: %s failed to start daemonized\n", __LINE__, c->name); + goto out; + } + + if (!c->wait(c, "RUNNING", -1)) { + fprintf(stderr, "%d: failed waiting for state RUNNING\n", __LINE__); + goto out; + } + + sleep(3); + s = c->state(c); + if (!s || strcmp(s, "RUNNING")) { + fprintf(stderr, "%d: %s is in state %s, not in RUNNING.\n", __LINE__, c->name, s ? s : "undefined"); + goto out; + } + + printf("hit return to finish"); + scanf("%c", &mychar); + + fprintf(stderr, "all lxc_container tests passed for %s\n", c->name); + ret = 0; + +out: + if (c) { + c->stop(c); + destroy_busybox(); + } + lxc_container_put(c); + exit(ret); +} diff --git a/src/tests/createtest.c b/src/tests/createtest.c new file mode 100644 index 000000000..48ce922e6 --- /dev/null +++ b/src/tests/createtest.c @@ -0,0 +1,92 @@ +/* liblxcapi + * + * Copyright © 2012 Serge Hallyn . + * Copyright © 2012 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "../lxc/lxccontainer.h" + +#include +#include +#include +#include +#include +#include +#include + +#define MYNAME "lxctest1" + +int main(int argc, char *argv[]) +{ + struct lxc_container *c; + int ret = 1; + + if ((c = lxc_container_new(MYNAME)) == NULL) { + fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME); + ret = 1; + goto out; + } + + if (c->is_defined(c)) { + fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME); + goto out; + } + + if (!c->set_config_item(c, "lxc.network.type", "veth")) { + fprintf(stderr, "%d: failed to set network type\n", __LINE__); + goto out; + } + c->set_config_item(c, "lxc.network.link", "lxcbr0"); + c->set_config_item(c, "lxc.network.flags", "up"); + if (!c->createl(c, "ubuntu", "-r", "lucid", NULL)) { + fprintf(stderr, "%d: failed to create a lucid container\n", __LINE__); + goto out; + } + + if (!c->is_defined(c)) { + fprintf(stderr, "%d: %s thought it was not defined\n", __LINE__, MYNAME); + goto out; + } + + c->load_config(c, NULL); + c->want_daemonize(c); + if (!c->startl(c, 0, NULL)) { + fprintf(stderr, "%d: failed to start %s\n", __LINE__, MYNAME); + goto out; + } + fprintf(stderr, "%d: %s started, you have 60 seconds to test a console\n", __LINE__, MYNAME); + sleep(60); // wait a minute to let user connect to console + + if (!c->stop(c)) { + fprintf(stderr, "%d: failed to stop %s\n", __LINE__, MYNAME); + goto out; + } + + if (!c->destroy(c)) { + fprintf(stderr, "%d: error deleting %s\n", __LINE__, MYNAME); + goto out; + } + + if (c->is_defined(c)) { + fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME); + goto out; + } + + fprintf(stderr, "all lxc_container tests passed for %s\n", c->name); + ret = 0; +out: + lxc_container_put(c); + exit(ret); +} diff --git a/src/tests/destroytest.c b/src/tests/destroytest.c new file mode 100644 index 000000000..3fe35dfef --- /dev/null +++ b/src/tests/destroytest.c @@ -0,0 +1,104 @@ +/* liblxcapi + * + * Copyright © 2012 Serge Hallyn . + * Copyright © 2012 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "../lxc/lxccontainer.h" + +#include +#include +#include +#include +#include +#include +#include + +#define MYNAME "lxctest1" + +static int create_ubuntu(void) +{ + int status, ret; + pid_t pid = fork(); + + if (pid < 0) { + perror("fork"); + return -1; + } + if (pid == 0) { + ret = execlp("lxc-create", "lxc-create", "-t", "ubuntu", "-f", "/etc/lxc/lxc.conf", "-n", MYNAME, NULL); + // Should not return + perror("execl"); + exit(1); + } +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == -EINTR) + goto again; + perror("waitpid"); + return -1; + } + if (ret != pid) + goto again; + if (!WIFEXITED(status)) { // did not exit normally + fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__); + return -1; + } + return WEXITSTATUS(status); +} + +int main(int argc, char *argv[]) +{ + struct lxc_container *c; + int ret = 1; + + if ((c = lxc_container_new(MYNAME)) == NULL) { + fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME); + ret = 1; + goto out; + } + + if (c->is_defined(c)) { + fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME); + goto out; + } + + if (create_ubuntu()) { + fprintf(stderr, "%d: failed to create a ubuntu container\n", __LINE__); + goto out; + } + + if (!c->is_defined(c)) { + fprintf(stderr, "%d: %s thought it was not defined\n", __LINE__, MYNAME); + goto out; + } + + if (!c->destroy(c)) { + fprintf(stderr, "%d: error deleting %s\n", __LINE__, MYNAME); + goto out; + } + + if (c->is_defined(c)) { + fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME); + goto out; + } + + fprintf(stderr, "all lxc_container tests passed for %s\n", c->name); + ret = 0; +out: + lxc_container_put(c); + exit(ret); +} diff --git a/src/tests/get_item.c b/src/tests/get_item.c new file mode 100644 index 000000000..0d32eca1e --- /dev/null +++ b/src/tests/get_item.c @@ -0,0 +1,308 @@ +/* liblxcapi + * + * Copyright © 2012 Serge Hallyn . + * Copyright © 2012 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "../lxc/lxccontainer.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MYNAME "lxctest1" + +int main(int argc, char *argv[]) +{ + struct lxc_container *c; + int ret; + char v1[2], v2[256], v3[2048]; + + if ((c = lxc_container_new("testxyz")) == NULL) { + fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME); + ret = 1; + goto out; + } + + if (!c->set_config_item(c, "lxc.hook.pre-start", "hi there")) { + fprintf(stderr, "%d: failed to set hook.pre-start\n", __LINE__); + ret = 1; + goto out; + } + ret = c->get_config_item(c, "lxc.hook.pre-start", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(lxc.hook.pre-start) returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + fprintf(stderr, "lxc.hook.pre-start returned %d %s\n", ret, v2); + + ret = c->get_config_item(c, "lxc.network", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + fprintf(stderr, "%d: get_config_item(lxc.network) returned %d %s\n", __LINE__, ret, v2); + if (!c->set_config_item(c, "lxc.tty", "4")) { + fprintf(stderr, "%d: failed to set tty\n", __LINE__); + ret = 1; + goto out; + } + ret = c->get_config_item(c, "lxc.tty", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(lxc.tty) returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + fprintf(stderr, "lxc.tty returned %d %s\n", ret, v2); + + if (!c->set_config_item(c, "lxc.arch", "x86")) { + fprintf(stderr, "%d: failed to set arch\n", __LINE__); + ret = 1; + goto out; + } + ret = c->get_config_item(c, "lxc.arch", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(lxc.arch) returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("lxc.arch returned %d %s\n", ret, v2); + +#define HNAME "hostname1" + // demonstrate proper usage: + char *alloced; + if (!c->set_config_item(c, "lxc.utsname", HNAME)) { + fprintf(stderr, "%d: failed to set utsname\n", __LINE__); + ret = 1; + goto out; + } + + int len; + len = c->get_config_item(c, "lxc.utsname", NULL, 0); // query the size of the string + if (len < 0) { + fprintf(stderr, "%d: get_config_item(lxc.utsname) returned %d\n", __LINE__, len); + ret = 1; + goto out; + } + printf("lxc.utsname returned %d\n", len); + + // allocate the length of string + 1 for trailing \0 + alloced = malloc(len+1); + if (!alloced) { + fprintf(stderr, "%d: failed to allocate %d bytes for utsname\n", __LINE__, len); + ret = 1; + goto out; + } + // now pass in the malloc'd array, and pass in length of string + 1: again + // because we need room for the trailing \0 + ret = c->get_config_item(c, "lxc.utsname", alloced, len+1); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(lxc.utsname) returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + if (strcmp(alloced, HNAME) != 0 || ret != len) { + fprintf(stderr, "lxc.utsname returned wrong value: %d %s not %d %s\n", ret, alloced, len, HNAME); + ret = 1; + goto out; + } + printf("lxc.utsname returned %d %s\n", len, alloced); + free(alloced); + + if (!c->set_config_item(c, "lxc.mount.entry", "hi there")) { + fprintf(stderr, "%d: failed to set mount.entry\n", __LINE__); + ret = 1; + goto out; + } + ret = c->get_config_item(c, "lxc.mount.entry", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(lxc.mount.entry) returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("lxc.mount.entry returned %d %s\n", ret, v2); + + if (!c->set_config_item(c, "lxc.aa_profile", "unconfined")) { + fprintf(stderr, "%d: failed to set aa_profile\n", __LINE__); + ret = 1; + goto out; + } + ret = c->get_config_item(c, "lxc.aa_profile", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(lxc.aa_profile) returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("lxc.aa_profile returned %d %s\n", ret, v2); + + lxc_container_put(c); + + // new test with real container + if ((c = lxc_container_new(MYNAME)) == NULL) { + fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME); + ret = 1; + goto out; + } + c->destroy(c); + lxc_container_put(c); + + if ((c = lxc_container_new(MYNAME)) == NULL) { + fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME); + ret = 1; + goto out; + } + if (!c->createl(c, "ubuntu", "-r", "lucid", NULL)) { + fprintf(stderr, "%d: failed to create a lucid container\n", __LINE__); + ret = 1; + goto out; + } + + lxc_container_put(c); + + /* XXX TODO load_config needs to clear out any old config first */ + if ((c = lxc_container_new(MYNAME)) == NULL) { + fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME); + ret = 1; + goto out; + } + + ret = c->get_config_item(c, "lxc.cap.drop", NULL, 300); + if (ret < 5 || ret > 255) { + fprintf(stderr, "%d: get_config_item(lxc.cap.drop) with NULL returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + ret = c->get_config_item(c, "lxc.cap.drop", v1, 1); + if (ret < 5 || ret > 255) { + fprintf(stderr, "%d: get_config_item(lxc.cap.drop) returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + ret = c->get_config_item(c, "lxc.cap.drop", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(lxc.cap.drop) returned %d %s\n", __LINE__, ret, v2); + ret = 1; + goto out; + } + printf("%d: get_config_item(lxc.cap.drop) returned %d %s\n", __LINE__, ret, v2); + ret = c->get_config_item(c, "lxc.network", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("%d: get_config_item(lxc.network) returned %d %s\n", __LINE__, ret, v2); + + if (!c->set_config_item(c, "lxc.network.ipv4", "10.2.3.4")) { + fprintf(stderr, "%d: failed to set ipv4\n", __LINE__); + ret = 1; + goto out; + } + + ret = c->get_config_item(c, "lxc.network.0.ipv4", v2, 255); + if (ret <= 0) { + fprintf(stderr, "%d: lxc.network.0.ipv4 returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + if (!c->clear_config_item(c, "lxc.network.0.ipv4")) { + fprintf(stderr, "%d: failed clearing all ipv4 entries\n", __LINE__); + ret = 1; + goto out; + } + ret = c->get_config_item(c, "lxc.network.0.ipv4", v2, 255); + if (ret != 0) { + fprintf(stderr, "%d: after clearing ipv4 entries get_item(lxc.network.0.ipv4 returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + + ret = c->get_config_item(c, "lxc.network.0.link", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("%d: get_config_item (link) returned %d %s\n", __LINE__, ret, v2); + ret = c->get_config_item(c, "lxc.network.0.name", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("%d: get_config_item (name) returned %d %s\n", __LINE__, ret, v2); + + if (!c->clear_config_item(c, "lxc.network")) { + fprintf(stderr, "%d: clear_config_item failed\n", __LINE__); + ret = 1; + goto out; + } + ret = c->get_config_item(c, "lxc.network", v2, 255); + if (ret != 0) { + fprintf(stderr, "%d: network was not actually cleared (get_network returned %d)\n", __LINE__, ret); + ret = 1; + goto out; + } + + ret = c->get_config_item(c, "lxc.cgroup", v3, 2047); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(cgroup.devices) returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("%d: get_config_item (cgroup.devices) returned %d %s\n", __LINE__, ret, v3); + + ret = c->get_config_item(c, "lxc.cgroup.devices.allow", v3, 2047); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(cgroup.devices.devices.allow) returned %d\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("%d: get_config_item (cgroup.devices.devices.allow) returned %d %s\n", __LINE__, ret, v3); + + if (!c->clear_config_item(c, "lxc.cgroup")) { + fprintf(stderr, "%d: failed clearing lxc.cgroup", __LINE__); + ret = 1; + goto out; + } + if (!c->clear_config_item(c, "lxc.cap.drop")) { + fprintf(stderr, "%d: failed clearing lxc.cap.drop", __LINE__); + ret = 1; + goto out; + } + if (!c->clear_config_item(c, "lxc.mount.entries")) { + fprintf(stderr, "%d: failed clearing lxc.mount.entries", __LINE__); + ret = 1; + goto out; + } + if (!c->clear_config_item(c, "lxc.hook")) { + fprintf(stderr, "%d: failed clearing lxc.hook", __LINE__); + ret = 1; + goto out; + } + c->destroy(c); + printf("All get_item tests passed\n"); + ret = 0; +out: + lxc_container_put(c); + exit(ret); +}; diff --git a/src/tests/getkeys.c b/src/tests/getkeys.c new file mode 100644 index 000000000..644b368c4 --- /dev/null +++ b/src/tests/getkeys.c @@ -0,0 +1,71 @@ +/* liblxcapi + * + * Copyright © 2012 Serge Hallyn . + * Copyright © 2012 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "../lxc/lxccontainer.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MYNAME "lxctest1" + +int main(int argc, char *argv[]) +{ + struct lxc_container *c; + int len, ret; + char v1[2], v2[256], v3[2048]; + + if ((c = lxc_container_new(MYNAME)) == NULL) { + fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME); + ret = 1; + goto out; + } + + c->set_config_item(c, "lxc.network.type", "veth"); + + len = c->get_keys(c, NULL, NULL, 0); + if (len < 0) { + fprintf(stderr, "%d: failed to get length of all keys (%d)\n", __LINE__, len); + ret = 1; + goto out; + } + ret = c->get_keys(c, NULL, v3, len+1); + if (ret != len) { + fprintf(stderr, "%d: failed to get keys (%d)\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("get_keys returned %d\n%s", ret, v3); + + ret = c->get_keys(c, "lxc.network.0", v3, 2000); + if (ret < 0) { + fprintf(stderr, "%d: failed to get nic 0 keys(%d)\n", __LINE__, ret); + ret = 1; + goto out; + } + printf("get_keys for nic 1 returned %d\n%s", ret, v3); + +out: + lxc_container_put(c); + exit(ret); +} diff --git a/src/tests/locktests.c b/src/tests/locktests.c new file mode 100644 index 000000000..f08555805 --- /dev/null +++ b/src/tests/locktests.c @@ -0,0 +1,239 @@ +/* liblxcapi + * + * Copyright © 2012 Serge Hallyn . + * Copyright © 2012 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "../lxc/lxclock.h" +#include +#include +#include +#include +#include +#include + +#define mycontainername "lxctest.sem" +#define TIMEOUT_SECS 3 + +int timedout; +int pid_to_kill; + +void timeouthandler(int sig) +{ + // timeout received + timedout = 1; + kill(pid_to_kill, SIGTERM); +} + +void starttimer(int secs) +{ + timedout = 0; + signal(SIGALRM, timeouthandler); + alarm(secs); +} +void stoptimer(void) +{ + alarm(0); + signal(SIGALRM, NULL); +} + +int test_one_lock(sem_t *lock) +{ + int ret; + starttimer(TIMEOUT_SECS); + ret = lxclock(lock, TIMEOUT_SECS*2); + stoptimer(); + if (ret == 0) { + lxcunlock(lock); + return 0; + } + if (timedout) + fprintf(stderr, "%d: timed out waiting for lock\n", __LINE__); + else + fprintf(stderr, "%d: failed to get single lock\n", __LINE__); + return 1; +} + +/* + * get one lock. Fork a second task to try to get a second lock, + * with infinite timeout. If our alarm hits, kill the second + * task. If second task does not + */ +int test_two_locks(sem_t *lock) +{ + int status; + int ret; + + ret = lxclock(lock, 1); + if (ret) { + fprintf(stderr, "%d: Error getting first lock\n", __LINE__); + return 2; + } + + pid_to_kill = fork(); + if (pid_to_kill < 0) { + fprintf(stderr, "%d: Failed to fork\n", __LINE__); + lxcunlock(lock); + return 3; + } + + if (pid_to_kill == 0) { // child + ret = lxclock(lock, TIMEOUT_SECS*2); + if (ret == 0) { + lxcunlock(lock); + exit(0); + } + fprintf(stderr, "%d: child, was not able to get lock\n", __LINE__); + exit(1); + } + starttimer(TIMEOUT_SECS); + waitpid(pid_to_kill, &status, 0); + stoptimer(); + if (WIFEXITED(status)) { + // child exited normally - timeout didn't kill it + if (WEXITSTATUS(status) == 0) + fprintf(stderr, "%d: child was able to get the lock\n", __LINE__); + else + fprintf(stderr, "%d: child timed out too early\n", __LINE__); + lxcunlock(lock); + return 1; + } + lxcunlock(lock); + return 0; +} + +/* + * get one lock. try to get second lock, but asking for timeout. If + * should return failure. If our own alarm, set at twice the lock + * request's timeout, hits, then lxclock() did not properly time out. + */ +int test_with_timeout(sem_t *lock) +{ + int status; + int ret = 0; + + ret = lxclock(lock, 0); + if (ret) { + fprintf(stderr, "%d: Error getting first lock\n", __LINE__); + return 2; + } + pid_to_kill = fork(); + if (pid_to_kill < 0) { + fprintf(stderr, "%d: Error on fork\n", __LINE__); + lxcunlock(lock); + return 2; + } + if (pid_to_kill == 0) { + ret = lxclock(lock, TIMEOUT_SECS); + if (ret == 0) { + lxcunlock(lock); + exit(0); + } + exit(1); + } + starttimer(TIMEOUT_SECS * 2); + waitpid(pid_to_kill, &status, 0); + stoptimer(); + if (!WIFEXITED(status)) { + fprintf(stderr, "%d: lxclock did not honor its timeout\n", __LINE__); + lxcunlock(lock); + return 1; + } + if (WEXITSTATUS(status) == 0) { + fprintf(stderr, "%d: child was able to get lock, should have failed with timeout\n", __LINE__); + ret = 1; + } + lxcunlock(lock); + return ret; +} + +int main(int argc, char *argv[]) +{ + int ret, sval, r; + sem_t *lock; + + lock = lxc_newlock(NULL); + if (!lock) { + fprintf(stderr, "%d: failed to get unnamed lock\n", __LINE__); + exit(1); + } + ret = lxclock(lock, 0); + if (ret) { + fprintf(stderr, "%d: failed to take unnamed lock (%d)\n", __LINE__, ret); + exit(1); + } + + ret = lxcunlock(lock); + if (ret) { + fprintf(stderr, "%d: failed to put unnamed lock (%d)\n", __LINE__, ret); + exit(1); + } + + sem_destroy(lock); + free(lock); + + lock = lxc_newlock(mycontainername); + if (!lock) { + fprintf(stderr, "%d: failed to get lock\n", __LINE__); + exit(1); + } + r = sem_getvalue(lock, &sval); + if (!r) { + fprintf(stderr, "%d: sem value at start is %d\n", __LINE__, sval); + } else { + fprintf(stderr, "%d: failed to get initial value\n", __LINE__); + } + + ret = test_one_lock(lock); + if (ret) { + fprintf(stderr, "%d: test failed\n", __LINE__); + goto out; + } + r = sem_getvalue(lock, &sval); + if (!r) { + fprintf(stderr, "%d: sem value is %d\n", __LINE__, sval); + } else { + fprintf(stderr, "%d: failed to get sem value\n", __LINE__); + } + + ret = test_two_locks(lock); + if (ret) { + fprintf(stderr, "%d: test failed\n", __LINE__); + goto out; + } + r = sem_getvalue(lock, &sval); + if (!r) { + fprintf(stderr, "%d: sem value is %d\n", __LINE__, sval); + } else { + fprintf(stderr, "%d: failed to get value\n", __LINE__); + } + + ret = test_with_timeout(lock); + if (ret) { + fprintf(stderr, "%d: test failed\n", __LINE__); + goto out; + } + r = sem_getvalue(lock, &sval); + if (!r) { + fprintf(stderr, "%d: sem value is %d\n", __LINE__, sval); + } else { + fprintf(stderr, "%d: failed to get value\n", __LINE__); + } + + fprintf(stderr, "all tests passed\n"); + +out: + exit(ret); +} diff --git a/src/tests/saveconfig.c b/src/tests/saveconfig.c new file mode 100644 index 000000000..20afdc52c --- /dev/null +++ b/src/tests/saveconfig.c @@ -0,0 +1,106 @@ +/* liblxcapi + * + * Copyright © 2012 Serge Hallyn . + * Copyright © 2012 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "../lxc/lxccontainer.h" + +#include +#include +#include +#include +#include +#include +#include + +#define MYNAME "lxctest1" + +static int create_ubuntu(void) +{ + int status, ret; + pid_t pid = fork(); + + if (pid < 0) { + perror("fork"); + return -1; + } + if (pid == 0) { + ret = execlp("lxc-create", "lxc-create", "-t", "ubuntu", "-f", "/etc/lxc/lxc.conf", "-n", MYNAME, NULL); + // Should not return + perror("execl"); + exit(1); + } +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == -EINTR) + goto again; + perror("waitpid"); + return -1; + } + if (ret != pid) + goto again; + if (!WIFEXITED(status)) { // did not exit normally + fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__); + return -1; + } + return WEXITSTATUS(status); +} + +int main(int argc, char *argv[]) +{ + struct lxc_container *c; + int ret = 1; + + if ((c = lxc_container_new(MYNAME)) == NULL) { + fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME); + ret = 1; + goto out; + } + + if (c->is_defined(c)) { + fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME); + goto out; + } + + if (create_ubuntu()) { + fprintf(stderr, "%d: failed to create a ubuntu container\n", __LINE__); + goto out; + } + + if (!c->is_defined(c)) { + fprintf(stderr, "%d: %s thought it was not defined\n", __LINE__, MYNAME); + goto out; + } + + c->load_config(c, NULL); + unlink("/tmp/lxctest1"); + if (!c->save_config(c, "/tmp/lxctest1")) { + fprintf(stderr, "%d: failed writing config file /tmp/lxctest1\n", __LINE__); + goto out; + } + rename("/var/lib/lxc/" MYNAME "/config", "/var/lib/lxc/" MYNAME "/config.bak"); + if (!c->save_config(c, NULL)) { + fprintf(stderr, "%d: failed writing config file\n", __LINE__); + goto out; + } + + fprintf(stderr, "all lxc_container tests passed for %s\n", c->name); + ret = 0; +out: + lxc_container_put(c); + exit(ret); +} diff --git a/src/tests/shutdowntest.c b/src/tests/shutdowntest.c new file mode 100644 index 000000000..9ad8a6510 --- /dev/null +++ b/src/tests/shutdowntest.c @@ -0,0 +1,93 @@ + +/* liblxcapi + * + * Copyright © 2012 Serge Hallyn . + * Copyright © 2012 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "../lxc/lxccontainer.h" + +#include +#include +#include +#include +#include +#include +#include + +#define MYNAME "lxctest1" + +int main(int argc, char *argv[]) +{ + struct lxc_container *c; + int ret = 1; + + if ((c = lxc_container_new(MYNAME)) == NULL) { + fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME); + ret = 1; + goto out; + } + + if (c->is_defined(c)) { + fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME); + goto out; + } + + if (!c->set_config_item(c, "lxc.network.type", "veth")) { + fprintf(stderr, "%d: failed to set network type\n", __LINE__); + goto out; + } + c->set_config_item(c, "lxc.network.link", "lxcbr0"); + c->set_config_item(c, "lxc.network.flags", "up"); + if (!c->createl(c, "ubuntu", "-r", "lucid", NULL)) { + fprintf(stderr, "%d: failed to create a lucid container\n", __LINE__); + goto out; + } + + if (!c->is_defined(c)) { + fprintf(stderr, "%d: %s thought it was not defined\n", __LINE__, MYNAME); + goto out; + } + + c->load_config(c, NULL); + c->want_daemonize(c); + if (!c->startl(c, 0, NULL)) { + fprintf(stderr, "%d: failed to start %s\n", __LINE__, MYNAME); + goto out; + } + fprintf(stderr, "%d: %s started, you have 60 seconds to test a console\n", __LINE__, MYNAME); + sleep(60); // wait a minute to let user connect to console + + if (!c->shutdown(c, 60)) { + fprintf(stderr, "%d: failed to shut down %s\n", __LINE__, MYNAME); + goto out; + } + + if (!c->destroy(c)) { + fprintf(stderr, "%d: error deleting %s\n", __LINE__, MYNAME); + goto out; + } + + if (c->is_defined(c)) { + fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME); + goto out; + } + + fprintf(stderr, "all lxc_container tests passed for %s\n", c->name); + ret = 0; +out: + lxc_container_put(c); + exit(ret); +} diff --git a/src/tests/startone.c b/src/tests/startone.c new file mode 100644 index 000000000..9097b8e92 --- /dev/null +++ b/src/tests/startone.c @@ -0,0 +1,202 @@ +/* liblxcapi + * + * Copyright © 2012 Serge Hallyn . + * Copyright © 2012 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "../lxc/lxccontainer.h" + +#include +#include +#include +#include +#include +#include +#include + +#define MYNAME "lxctest1" + +static int destroy_ubuntu(void) +{ + int status, ret; + pid_t pid = fork(); + + if (pid < 0) { + perror("fork"); + return -1; + } + if (pid == 0) { + ret = execlp("lxc-destroy", "lxc-destroy", "-f", "-n", MYNAME, NULL); + // Should not return + perror("execl"); + exit(1); + } +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == -EINTR) + goto again; + perror("waitpid"); + return -1; + } + if (ret != pid) + goto again; + if (!WIFEXITED(status)) { // did not exit normally + fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__); + return -1; + } + return WEXITSTATUS(status); +} + +static int create_ubuntu(void) +{ + int status, ret; + pid_t pid = fork(); + + if (pid < 0) { + perror("fork"); + return -1; + } + if (pid == 0) { + ret = execlp("lxc-create", "lxc-create", "-t", "ubuntu", "-f", "/etc/lxc/lxc.conf", "-n", MYNAME, NULL); + // Should not return + perror("execl"); + exit(1); + } +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == -EINTR) + goto again; + perror("waitpid"); + return -1; + } + if (ret != pid) + goto again; + if (!WIFEXITED(status)) { // did not exit normally + fprintf(stderr, "%d: lxc-create exited abnormally\n", __LINE__); + return -1; + } + return WEXITSTATUS(status); +} + +int main(int argc, char *argv[]) +{ + struct lxc_container *c; + int ret = 0; + const char *s; + bool b; + + ret = 1; + /* test a real container */ + c = lxc_container_new(MYNAME); + if (!c) { + fprintf(stderr, "%d: error creating lxc_container %s\n", __LINE__, MYNAME); + ret = 1; + goto out; + } + + if (c->is_defined(c)) { + fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME); + goto out; + } + + ret = create_ubuntu(); + if (ret) { + fprintf(stderr, "%d: failed to create a ubuntu container\n", __LINE__); + goto out; + } + + b = c->is_defined(c); + if (!b) { + fprintf(stderr, "%d: %s thought it was not defined\n", __LINE__, MYNAME); + goto out; + } + + s = c->state(c); + if (!s || strcmp(s, "STOPPED")) { + fprintf(stderr, "%d: %s is in state %s, not in STOPPED.\n", __LINE__, c->name, s ? s : "undefined"); + goto out; + } + + b = c->load_config(c, NULL); + if (!b) { + fprintf(stderr, "%d: %s failed to read its config\n", __LINE__, c->name); + goto out; + } + + if (!c->set_config_item(c, "lxc.utsname", "bobo")) { + fprintf(stderr, "%d: failed setting lxc.utsname\n", __LINE__); + goto out; + } + + printf("hit return to start container"); + char mychar; + scanf("%c", &mychar); + + if (!lxc_container_get(c)) { + fprintf(stderr, "%d: failed to get extra ref to container\n", __LINE__); + exit(1); + } + pid_t pid = fork(); + if (pid < 0) { + fprintf(stderr, "%d: fork failed\n", __LINE__); + goto out; + } + if (pid == 0) { + b = c->startl(c, 0, NULL); + if (!b) + fprintf(stderr, "%d: %s failed to start\n", __LINE__, c->name); + lxc_container_put(c); + exit(!b); + } + + sleep(3); + s = c->state(c); + if (!s || strcmp(s, "RUNNING")) { + fprintf(stderr, "%d: %s is in state %s, not in RUNNING.\n", __LINE__, c->name, s ? s : "undefined"); + goto out; + } + + printf("hit return to finish"); + scanf("%c", &mychar); + c->stop(c); + + system("mkdir -p /var/lib/lxc/lxctest1/rootfs//usr/local/libexec/lxc"); + system("mkdir -p /var/lib/lxc/lxctest1/rootfs/usr/lib/lxc/"); + system("cp src/lxc/lxc-init /var/lib/lxc/lxctest1/rootfs//usr/local/libexec/lxc"); + system("cp src/lxc/liblxc.so /var/lib/lxc/lxctest1/rootfs/usr/lib/lxc"); + system("cp src/lxc/liblxc.so /var/lib/lxc/lxctest1/rootfs/usr/lib/lxc/liblxc.so.0"); + system("cp src/lxc/liblxc.so /var/lib/lxc/lxctest1/rootfs/usr/lib"); + system("mkdir -p /var/lib/lxc/lxctest1/rootfs/dev/shm"); + system("chroot /var/lib/lxc/lxctest1/rootfs apt-get install --no-install-recommends lxc"); + // next write out the config file; does it match? + if (!c->startl(c, 1, "/bin/hostname", NULL)) { + fprintf(stderr, "%d: failed to lxc-execute /bin/hostname", __LINE__); + goto out; + } + // auto-check result? ('bobo' is printed on stdout) + + fprintf(stderr, "all lxc_container tests passed for %s\n", c->name); + ret = 0; + +out: + if (c) { + c->stop(c); + destroy_ubuntu(); + } + lxc_container_put(c); + exit(ret); +} From 7a44c8b447c4cac2b71bf842a61440bae9caf918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 27 Aug 2012 19:01:36 -0400 Subject: [PATCH 016/171] When starting a container daemonized, wait for it to reach RUNNING state before returning the result of start(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the container doesn't reach RUNNING state in 5 seconds, a failure will be returned to the user. Signed-off-by: Serge Hallyn Acked-by: Stéphane Graber --- src/lxc/lxccontainer.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index f61ba0060..e88460b86 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -244,6 +244,26 @@ static void lxcapi_want_daemonize(struct lxc_container *c) c->daemonize = 1; } +static bool lxcapi_wait(struct lxc_container *c, char *state, int timeout) +{ + int ret; + + if (!c) + return false; + + ret = lxc_wait(c->name, state, timeout); + return ret == 0; +} + + +static bool wait_on_daemonized_start(struct lxc_container *c) +{ + /* we'll probably want to make this timeout configurable? */ + int timeout = 5; + + return lxcapi_wait(c, "RUNNING", timeout); +} + /* * I can't decide if it'd be more convenient for callers if we accept '...', * or a null-terminated array (i.e. execl vs execv) @@ -298,7 +318,7 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char ** argv) return false; } if (pid != 0) - return true; + return wait_on_daemonized_start(c); /* like daemon(), chdir to / and redirect 0,1,2 to /dev/null */ chdir("/"); close(0); @@ -408,17 +428,6 @@ static bool lxcapi_stop(struct lxc_container *c) return ret == 0; } -static bool lxcapi_wait(struct lxc_container *c, char *state, int timeout) -{ - int ret; - - if (!c) - return false; - - ret = lxc_wait(c->name, state, timeout); - return ret == 0; -} - static bool valid_template(char *t) { struct stat statbuf; From be2e4e54da3c8054525321422f7f290d45b32a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 27 Aug 2012 19:04:43 -0400 Subject: [PATCH 017/171] Add python-lxc based on the new liblxc API. This adds a basic python binding done in C and a python overlay to extend some features and provide a user-friendlier API. This python API only supports python 3.x and was tested with >= 3.2. It's disabled by default in configure and can be turned on by using --enable-python. A basic example of the API can be found in src/python-lxc/test.py. More documentation and examples will be added soon. --- configure.ac | 18 ++ src/Makefile.am | 2 +- src/python-lxc/Makefile.am | 18 ++ src/python-lxc/lxc.c | 576 +++++++++++++++++++++++++++++++++ src/python-lxc/lxc/__init__.py | 372 +++++++++++++++++++++ src/python-lxc/setup.py | 10 + src/python-lxc/test.py | 28 ++ 7 files changed, 1023 insertions(+), 1 deletion(-) create mode 100644 src/python-lxc/Makefile.am create mode 100644 src/python-lxc/lxc.c create mode 100644 src/python-lxc/lxc/__init__.py create mode 100644 src/python-lxc/setup.py create mode 100644 src/python-lxc/test.py diff --git a/configure.ac b/configure.ac index dbaf48ba9..7b98306b2 100644 --- a/configure.ac +++ b/configure.ac @@ -12,6 +12,11 @@ AM_PROG_CC_C_O AC_GNU_SOURCE AC_CHECK_PROG(SETCAP, setcap, yes, no, $PATH$PATH_SEPARATOR/sbin) +if test -f /etc/debian_version; then + osname="debian" +fi +AM_CONDITIONAL([HAVE_DEBIAN], [test x"$osname" == xdebian]) + AC_ARG_ENABLE([rpath], [AC_HELP_STRING([--disable-rpath], [do not set rpath in executables])], [], [enable_rpath=yes]) @@ -67,6 +72,17 @@ AC_ARG_ENABLE([examples], AM_CONDITIONAL([ENABLE_EXAMPLES], [test "x$enable_examples" = "xyes"]) +AC_ARG_ENABLE([python], + [AC_HELP_STRING([--enable-python], [enable python binding])], + [enable_python=yes], [enable_python=no]) + +AM_CONDITIONAL([ENABLE_PYTHON], [test "x$enable_python" = "xyes"]) + +AM_COND_IF([ENABLE_PYTHON], + [AM_PATH_PYTHON([3.2], [], [AC_MSG_ERROR([You must install python3])]) + AC_CHECK_HEADER([python$PYTHON_VERSION/Python.h],[],[AC_MSG_ERROR([You must install python3-dev])]) + AC_DEFINE_UNQUOTED([ENABLE_PYTHON], 1, [Python3 is available])]) + AS_AC_EXPAND(PREFIX, $prefix) AS_AC_EXPAND(LIBDIR, $libdir) AS_AC_EXPAND(BINDIR, $bindir) @@ -192,6 +208,8 @@ AC_CONFIG_FILES([ src/lxc/lxc-shutdown src/lxc/lxc-destroy + src/python-lxc/Makefile + src/tests/Makefile ]) diff --git a/src/Makefile.am b/src/Makefile.am index ca3b09203..4e4d66b5e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1 +1 @@ -SUBDIRS = lxc tests +SUBDIRS = lxc tests python-lxc diff --git a/src/python-lxc/Makefile.am b/src/python-lxc/Makefile.am new file mode 100644 index 000000000..15c61eaa5 --- /dev/null +++ b/src/python-lxc/Makefile.am @@ -0,0 +1,18 @@ +if ENABLE_PYTHON + +if HAVE_DEBIAN + DISTSETUPOPTS=--install-layout=deb +else + DISTSETUPOPTS= +endif + +all: + CFLAGS="$(CFLAGS) -I ../../src -L../../src/lxc/" $(PYTHON) setup.py build + +install: + python3 setup.py install --root=$(DESTDIR) --prefix=$(PREFIX) --no-compile $(DISTSETUPOPTS) + +clean: + rm -rf build + +endif diff --git a/src/python-lxc/lxc.c b/src/python-lxc/lxc.c new file mode 100644 index 000000000..f58a954de --- /dev/null +++ b/src/python-lxc/lxc.c @@ -0,0 +1,576 @@ +/* + * python-lxc: Python bindings for LXC + * + * (C) Copyright Canonical Ltd. 2012 + * + * Authors: + * Stéphane Graber + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "structmember.h" +#include +#include +#include + +typedef struct { + PyObject_HEAD + struct lxc_container *container; +} Container; + +char** +convert_tuple_to_char_pointer_array(PyObject *argv) { + int argc = PyTuple_Size(argv); + int i; + + char **result = (char**) malloc(sizeof(char*)*argc + 1); + + for (i = 0; i < argc; i++) { + PyObject *pyobj = PyTuple_GetItem(argv, i); + + char *str = NULL; + PyObject *pystr; + if (!PyUnicode_Check(pyobj)) { + PyErr_SetString(PyExc_ValueError, "Expected a string"); + return NULL; + } + + pystr = PyUnicode_AsUTF8String(pyobj); + str = PyBytes_AsString(pystr); + memcpy((char *) &result[i], (char *) &str, sizeof(str)); + } + + result[argc] = NULL; + + return result; +} + +void zombie_handler(int sig) +{ + signal(SIGCHLD,zombie_handler); + int status; + + waitpid(-1, &status, WNOHANG); +} + +static void +Container_dealloc(Container* self) +{ + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static PyObject * +Container_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + Container *self; + + self = (Container *)type->tp_alloc(type, 0); + + return (PyObject *)self; +} + +static int +Container_init(Container *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, + &name)) + return -1; + + self->container = lxc_container_new(name); + if (!self->container) { + fprintf(stderr, "%d: error creating lxc_container %s\n", __LINE__, name); + return -1; + } + + return 0; +} + +// Container properties +static PyObject * +Container_config_file_name(Container *self, PyObject *args, PyObject *kwds) +{ + return PyUnicode_FromString(self->container->config_file_name(self->container)); +} + +static PyObject * +Container_defined(Container *self, PyObject *args, PyObject *kwds) +{ + if (self->container->is_defined(self->container)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyObject * +Container_init_pid(Container *self, PyObject *args, PyObject *kwds) +{ + return Py_BuildValue("i", self->container->init_pid(self->container)); +} + +static PyObject * +Container_name(Container *self, PyObject *args, PyObject *kwds) +{ + return PyUnicode_FromString(self->container->name); +} + +static PyObject * +Container_running(Container *self, PyObject *args, PyObject *kwds) +{ + if (self->container->is_running(self->container)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyObject * +Container_state(Container *self, PyObject *args, PyObject *kwds) +{ + return PyUnicode_FromString(self->container->state(self->container)); +} + +// Container Functions +static PyObject * +Container_clear_config_item(Container *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", NULL}; + char *key = NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, + &key)) + Py_RETURN_FALSE; + + if (self->container->clear_config_item(self->container, key)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyObject * +Container_create(Container *self, PyObject *args, PyObject *kwds) +{ + char* template_name = NULL; + char** create_args = {NULL}; + PyObject *vargs = NULL; + static char *kwlist[] = {"template", "args", NULL}; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist, + &template_name, &vargs)) + Py_RETURN_FALSE; + + if (vargs && PyTuple_Check(vargs)) { + create_args = convert_tuple_to_char_pointer_array(vargs); + if (!create_args) { + return NULL; + } + } + + if (self->container->create(self->container, template_name, create_args)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyObject * +Container_destroy(Container *self, PyObject *args, PyObject *kwds) +{ + if (self->container->destroy(self->container)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyObject * +Container_freeze(Container *self, PyObject *args, PyObject *kwds) +{ + if (self->container->freeze(self->container)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyObject * +Container_get_config_item(Container *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", NULL}; + char* key = NULL; + int len = 0; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, + &key)) + Py_RETURN_FALSE; + + len = self->container->get_config_item(self->container, key, NULL, 0); + + if (len <= 0) { + Py_RETURN_FALSE; + } + + char* value = (char*) malloc(sizeof(char)*len + 1); + if (self->container->get_config_item(self->container, key, value, len + 1) != len) { + Py_RETURN_FALSE; + } + + return PyUnicode_FromString(value); +} + +static PyObject * +Container_get_keys(Container *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", NULL}; + char* key = NULL; + int len = 0; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, + &key)) + Py_RETURN_FALSE; + + len = self->container->get_keys(self->container, key, NULL, 0); + + if (len <= 0) { + Py_RETURN_FALSE; + } + + char* value = (char*) malloc(sizeof(char)*len + 1); + if (self->container->get_keys(self->container, key, value, len + 1) != len) { + Py_RETURN_FALSE; + } + + return PyUnicode_FromString(value); +} + +static PyObject * +Container_load_config(Container *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"path", NULL}; + char* path = NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, + &path)) + Py_RETURN_FALSE; + + if (self->container->load_config(self->container, path)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyObject * +Container_save_config(Container *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"path", NULL}; + char* path = NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, + &path)) + Py_RETURN_FALSE; + + if (self->container->save_config(self->container, path)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyObject * +Container_set_config_item(Container *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", "value", NULL}; + char *key = NULL; + char *value = NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss|", kwlist, + &key, &value)) + Py_RETURN_FALSE; + + if (self->container->set_config_item(self->container, key, value)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyObject * +Container_shutdown(Container *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"timeout", NULL}; + int timeout = -1; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, + &timeout)) + Py_RETURN_FALSE; + + if (self->container->shutdown(self->container, timeout)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyObject * +Container_start(Container *self, PyObject *args, PyObject *kwds) +{ + char** init_args = {NULL}; + PyObject *useinit = NULL, *vargs = NULL; + int init_useinit = 0; + static char *kwlist[] = {"useinit", "cmd", NULL}; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, + &useinit, &vargs)) + Py_RETURN_FALSE; + + if (useinit && useinit == Py_True) { + init_useinit = 1; + } + + if (vargs && PyTuple_Check(vargs)) { + init_args = convert_tuple_to_char_pointer_array(vargs); + if (!init_args) { + return NULL; + } + } + + signal(SIGCHLD, zombie_handler); + self->container->want_daemonize(self->container); + + if (self->container->start(self->container, init_useinit, init_args)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyObject * +Container_stop(Container *self, PyObject *args, PyObject *kwds) +{ + if (self->container->stop(self->container)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyObject * +Container_unfreeze(Container *self, PyObject *args, PyObject *kwds) +{ + if (self->container->unfreeze(self->container)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyObject * +Container_wait(Container *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"state", "timeout", NULL}; + char *state = NULL; + int timeout = -1; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, + &state, &timeout)) + Py_RETURN_FALSE; + + if (self->container->wait(self->container, state, timeout)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyGetSetDef Container_getseters[] = { + {"config_file_name", + (getter)Container_config_file_name, 0, + "Path to the container configuration", + NULL}, + {"defined", + (getter)Container_defined, 0, + "Boolean indicating whether the container configuration exists", + NULL}, + {"init_pid", + (getter)Container_init_pid, 0, + "PID of the container's init process in the host's PID namespace", + NULL}, + {"name", + (getter)Container_name, 0, + "Container name", + NULL}, + {"running", + (getter)Container_running, 0, + "Boolean indicating whether the container is running or not", + NULL}, + {"state", + (getter)Container_state, 0, + "Container state", + NULL}, +}; + +static PyMethodDef Container_methods[] = { + {"clear_config_item", (PyCFunction)Container_clear_config_item, METH_VARARGS | METH_KEYWORDS, + "clear_config_item(key) -> boolean\n" + "\n" + "Clear the current value of a config key." + }, + {"create", (PyCFunction)Container_create, METH_VARARGS | METH_KEYWORDS, + "create(template, args = (,)) -> boolean\n" + "\n" + "Create a new rootfs for the container, using the given template " + "and passing some optional arguments to it." + }, + {"destroy", (PyCFunction)Container_destroy, METH_NOARGS, + "destroy() -> boolean\n" + "\n" + "Destroys the container." + }, + {"freeze", (PyCFunction)Container_freeze, METH_NOARGS, + "freeze() -> boolean\n" + "\n" + "Freezes the container and returns its return code." + }, + {"get_config_item", (PyCFunction)Container_get_config_item, METH_VARARGS | METH_KEYWORDS, + "get_config_item(key) -> string\n" + "\n" + "Get the current value of a config key." + }, + {"get_keys", (PyCFunction)Container_get_keys, METH_VARARGS | METH_KEYWORDS, + "get_keys(key) -> string\n" + "\n" + "Get a list of valid sub-keys for a key." + }, + {"load_config", (PyCFunction)Container_load_config, METH_VARARGS | METH_KEYWORDS, + "load_config(path = DEFAULT) -> boolean\n" + "\n" + "Read the container configuration from its default " + "location or from an alternative location if provided." + }, + {"save_config", (PyCFunction)Container_save_config, METH_VARARGS | METH_KEYWORDS, + "save_config(path = DEFAULT) -> boolean\n" + "\n" + "Save the container configuration to its default " + "location or to an alternative location if provided." + }, + {"set_config_item", (PyCFunction)Container_set_config_item, METH_VARARGS | METH_KEYWORDS, + "set_config_item(key, value) -> boolean\n" + "\n" + "Set a config key to the provided value." + }, + {"shutdown", (PyCFunction)Container_shutdown, METH_VARARGS | METH_KEYWORDS, + "shutdown(timeout = -1) -> boolean\n" + "\n" + "Sends SIGPWR to the container and wait for it to shutdown " + "unless timeout is set to a positive value, in which case " + "the container will be killed when the timeout is reached." + }, + {"start", (PyCFunction)Container_start, METH_VARARGS | METH_KEYWORDS, + "start(useinit = False, cmd = (,)) -> boolean\n" + "\n" + "Start the container, optionally using lxc-init and" + "an alternate init command, then returns its return code." + }, + {"stop", (PyCFunction)Container_stop, METH_NOARGS, + "stop() -> boolean\n" + "\n" + "Stop the container and returns its return code." + }, + {"unfreeze", (PyCFunction)Container_unfreeze, METH_NOARGS, + "unfreeze() -> boolean\n" + "\n" + "Unfreezes the container and returns its return code." + }, + {"wait", (PyCFunction)Container_wait, METH_VARARGS | METH_KEYWORDS, + "wait(state, timeout = -1) -> boolean\n" + "\n" + "Wait for the container to reach a given state or timeout." + }, + {NULL} /* Sentinel */ +}; + +static PyTypeObject _lxc_ContainerType = { +PyVarObject_HEAD_INIT(NULL, 0) + "lxc.Container", /* tp_name */ + sizeof(Container), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Container_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Container objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Container_methods, /* tp_methods */ + 0, /* tp_members */ + Container_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Container_init, /* tp_init */ + 0, /* tp_alloc */ + Container_new, /* tp_new */ +}; + +static PyModuleDef _lxcmodule = { + PyModuleDef_HEAD_INIT, + "_lxc", + "Binding for liblxc in python", + -1, + NULL, NULL, NULL, NULL, NULL +}; + +PyMODINIT_FUNC +PyInit__lxc(void) +{ + PyObject* m; + + if (PyType_Ready(&_lxc_ContainerType) < 0) + return NULL; + + m = PyModule_Create(&_lxcmodule); + if (m == NULL) + return NULL; + + Py_INCREF(&_lxc_ContainerType); + PyModule_AddObject(m, "Container", (PyObject *)&_lxc_ContainerType); + return m; +} diff --git a/src/python-lxc/lxc/__init__.py b/src/python-lxc/lxc/__init__.py new file mode 100644 index 000000000..94616f59a --- /dev/null +++ b/src/python-lxc/lxc/__init__.py @@ -0,0 +1,372 @@ +# +# python-lxc: Python bindings for LXC +# +# (C) Copyright Canonical Ltd. 2012 +# +# Authors: +# Stéphane Graber +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import _lxc +import glob +import os +import subprocess +import tempfile +import time +import warnings + +warnings.warn("The python-lxc API isn't yet stable " + "and may change at any point in the future.", Warning, 2) + +class ContainerNetwork(): + props = {} + + def __init__(self, container, index): + self.container = container + self.index = index + + for key in self.container.get_keys("lxc.network.%s" % self.index): + if "." in key: + self.props[key.replace(".", "_")] = key + else: + self.props[key] = key + + if not self.props: + return False + + def __delattr__(self, key): + if key in ["container", "index", "props"]: + return object.__delattr__(self, key) + + if key not in self.props: + raise AttributeError("'%s' network has no attribute '%s'" % ( + self.__get_network_item("type"), key)) + + return self.__clear_network_item(self.props[key]) + + def __dir__(self): + return sorted(self.props.keys()) + + def __getattr__(self, key): + if key in ["container", "index", "props"]: + return object.__getattribute__(self, key) + + if key not in self.props: + raise AttributeError("'%s' network has no attribute '%s'" % ( + self.__get_network_item("type"), key)) + + return self.__get_network_item(self.props[key]) + + def __hasattr__(self, key): + if key in ["container", "index", "props"]: + return object.__hasattr__(self, key) + + if key not in self.props: + raise AttributeError("'%s' network has no attribute '%s'" % ( + self.__get_network_item("type"), key)) + + return True + + def __repr__(self): + return "'%s' network at index '%s'" % ( + self.__get_network_item("type"), self.index) + + def __setattr__(self, key, value): + if key in ["container", "index", "props"]: + return object.__setattr__(self, key, value) + + if key not in self.props: + raise AttributeError("'%s' network has no attribute '%s'" % ( + self.__get_network_item("type"), key)) + + return self.__set_network_item(self.props[key], value) + + def __clear_network_item(self, key): + return self.container.clear_config_item("lxc.network.%s.%s" % ( + self.index, key)) + + def __get_network_item(self, key): + return self.container.get_config_item("lxc.network.%s.%s" % ( + self.index, key)) + + def __set_network_item(self, key, value): + return self.container.set_config_item("lxc.network.%s.%s" % ( + self.index, key), value) + + +class ContainerNetworkList(): + def __init__(self, container): + self.container = container + + def __getitem__(self, index): + count = len(self.container.get_config_item("lxc.network")) + if index >= count: + raise IndexError("list index out of range") + + return ContainerNetwork(self.container, index) + + def __len__(self): + return len(self.container.get_config_item("lxc.network")) + + def add(self, network_type): + index = len(self.container.get_config_item("lxc.network")) + + return self.container.set_config_item("lxc.network.%s.type" % index, + network_type) + + def remove(self, index): + count = len(self.container.get_config_item("lxc.network")) + if index >= count: + raise IndexError("list index out of range") + + return self.container.clear_config_item("lxc.network.%s" % index) + + +class Container(_lxc.Container): + def __init__(self, name): + """ + Creates a new Container instance. + """ + + _lxc.Container.__init__(self, name) + self.network = ContainerNetworkList(self) + + def append_config_item(self, key, value): + """ + Append 'value' to 'key', assuming 'key' is a list. + If 'key' isn't a list, 'value' will be set as the value of 'key'. + """ + + return _lxc.Container.set_config_item(self, key, value) + + def attach(self, namespace="ALL", *cmd): + """ + Attach to a running container. + """ + + if not self.running: + return False + + attach = ["lxc-attach", "-n", self.name] + if namespace != "ALL": + attach += ["-s", namespace] + + if cmd: + attach += ["--"] + list(cmd) + + if subprocess.call( + attach, + universal_newlines=True) != 0: + return False + return True + + def create(self, template, args={}): + """ + Create a new rootfs for the container. + + "template" must be a valid template name. + + "args" (optional) is a dictionary of parameters and values to pass + to the template. + """ + + template_args = [] + for item in args.items(): + template_args.append("--%s" % item[0]) + template_args.append("%s" % item[1]) + + return _lxc.Container.create(self, template, tuple(template_args)) + + def clone(self, container): + """ + Clone an existing container into a new one. + """ + + if self.defined: + return False + + if isinstance(container, Container): + source = container + else: + source = Container(container) + + if not source.defined: + return False + + if subprocess.call( + ["lxc-clone", "-o", source.name, "-n", self.name], + universal_newlines=True) != 0: + return False + + self.load_config() + return True + + def console(self, tty="1"): + """ + Access the console of a container. + """ + + if not self.running: + return False + + if subprocess.call( + ["lxc-console", "-n", self.name, "-t", "%s" % tty], + universal_newlines=True) != 0: + return False + return True + + def get_config_item(self, key): + """ + Returns the value for a given config key. + A list is returned when multiple values are set. + """ + value = _lxc.Container.get_config_item(self, key) + + if value is False: + return False + elif value.endswith("\n"): + return value.rstrip("\n").split("\n") + else: + return value + + def get_ips(self, timeout=60, interface=None, protocol=None): + """ + Returns the list of IP addresses for the container. + """ + + if not self.defined or not self.running: + return False + + try: + os.makedirs("/run/netns") + except: + pass + + path = tempfile.mktemp(dir="/run/netns") + + os.symlink("/proc/%s/ns/net" % self.init_pid, path) + + ips = [] + + count = 0 + while count < timeout: + if count != 0: + time.sleep(1) + + base_cmd = ["ip", "netns", "exec", path.split("/")[-1], "ip"] + + # Get IPv6 + if protocol in ("ipv6", None): + ip6_cmd = base_cmd + ["-6", "addr", "show", "scope", "global"] + if interface: + ip = subprocess.Popen(ip6_cmd + ["dev", interface], + stdout=subprocess.PIPE, universal_newlines=True) + else: + ip = subprocess.Popen(ip6_cmd, stdout=subprocess.PIPE, + universal_newlines=True) + + ip.wait() + for line in ip.stdout.read().split("\n"): + fields = line.split() + if len(fields) > 2 and fields[0] == "inet6": + ips.append(fields[1].split('/')[0]) + + # Get IPv4 + if protocol in ("ipv4", None): + ip4_cmd = base_cmd + ["-4", "addr", "show", "scope", "global"] + if interface: + ip = subprocess.Popen(ip4_cmd + ["dev", interface], + stdout=subprocess.PIPE, universal_newlines=True) + else: + ip = subprocess.Popen(ip4_cmd, stdout=subprocess.PIPE, + universal_newlines=True) + + ip.wait() + for line in ip.stdout.read().split("\n"): + fields = line.split() + if len(fields) > 2 and fields[0] == "inet": + ips.append(fields[1].split('/')[0]) + + if ips: + break + + count += 1 + + os.remove(path) + return ips + + def get_keys(self, key): + """ + Returns a list of valid sub-keys. + """ + value = _lxc.Container.get_keys(self, key) + + if value is False: + return False + elif value.endswith("\n"): + return value.rstrip("\n").split("\n") + else: + return value + + def set_config_item(self, key, value): + """ + Set a config key to a provided value. + The value can be a list for the keys supporting multiple values. + """ + old_value = self.get_config_item(key) + + # Check if it's a list + def set_key(key, value): + self.clear_config_item(key) + if isinstance(value, list): + for entry in value: + if not _lxc.Container.set_config_item(self, key, entry): + return False + else: + _lxc.Container.set_config_item(self, key, value) + + set_key(key, value) + new_value = self.get_config_item(key) + + if isinstance(value, str) and isinstance(new_value, str) and \ + value == new_value: + return True + elif isinstance(value, list) and isinstance(new_value, list) and \ + set(value) == set(new_value): + return True + elif isinstance(value, str) and isinstance(new_value, list) and \ + set([value]) == set(new_value): + return True + elif old_value: + set_key(key, old_value) + return False + else: + self.clear_config_item(key) + return False + + +def list_containers(as_object=False): + """ + List the containers on the system. + """ + containers = [] + for entry in glob.glob("/var/lib/lxc/*/config"): + if as_object: + containers.append(Container(entry.split("/")[-2])) + else: + containers.append(entry.split("/")[-2]) + return containers diff --git a/src/python-lxc/setup.py b/src/python-lxc/setup.py new file mode 100644 index 000000000..bf635ea31 --- /dev/null +++ b/src/python-lxc/setup.py @@ -0,0 +1,10 @@ +from distutils.core import setup, Extension + +module = Extension('_lxc', sources = ['lxc.c'], libraries = ['lxc']) + +setup (name = '_lxc', + version = '0.1', + description = 'LXC', + packages = ['lxc'], + package_dir = {'lxc':'lxc'}, + ext_modules = [module]) diff --git a/src/python-lxc/test.py b/src/python-lxc/test.py new file mode 100644 index 000000000..5552fe13b --- /dev/null +++ b/src/python-lxc/test.py @@ -0,0 +1,28 @@ +import lxc + +t1 = lxc.Container("test") +print("Name set properly: %s" % (t1.name == "test")) +print("Test config loaded properly: %s" % t1.load_config("/etc/lxc/lxc.conf")) +print("Real config loaded properly: %s" % t1.load_config()) +print("Test config path: %s" % (t1.config_file_name == "/var/lib/lxc/test/config")) +print("Set config item: %s" % t1.set_config_item("lxc.utsname", "blabla")) +print("Container defined: %s" % (t1.defined)) +print("Started properly: %s" % t1.start()) +print("Container running: %s" % t1.wait("RUNNING")) +print("Container state: %s" % t1.state) +print("Container running: %s" % t1.running) +print("Container init process: %s" % t1.init_pid) +print("Freezing: %s" % t1.freeze()) +print("Container frozen: %s" % t1.wait("FROZEN")) +print("Container state: %s" % t1.state) +print("Unfreezing: %s" % t1.unfreeze()) +print("Container running: %s" % t1.wait("RUNNING")) +print("Container state: %s" % t1.state) +print("Stopped properly: %s" % t1.stop()) +print("Container state: %s" % t1.state) + +#print("Started properly: %s" % t1.start(useinit=True)) +#print("Container running: %s" % t1.wait("RUNNING")) +#print("Container state: %s" % t1.state) +#print("Stopped properly: %s" % t1.stop()) +#print("Container state: %s" % t1.state) From cbe3a58b138e7596d53bb93e5a17d084ba1350dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 28 Aug 2012 13:51:02 -0400 Subject: [PATCH 018/171] Remove duplicate copy of runapitests.bash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- lxc/runapitests.bash | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 lxc/runapitests.bash diff --git a/lxc/runapitests.bash b/lxc/runapitests.bash deleted file mode 100644 index 2b1b3baec..000000000 --- a/lxc/runapitests.bash +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -cleanup() { - rm -f /etc/lxc/test-busybox.conf - rm -f liblxc.so.0 -} - -if [ `id -u` -ne 0 ]; then - echo "Run as root" - exit 1 -fi - -cat > /etc/lxc/test-busybox.conf << EOF -lxc.network.type=veth -lxc.network.link=lxcbr0 -lxc.network.flags=up -EOF - -[ -f liblxc.so.0 ] || ln -s src/lxc/liblxc.so ./liblxc.so.0 -export LD_LIBRARY_PATH=. -TESTS="containertests locktests startone" -for curtest in $TESTS; do - echo "running $curtest" - ./src/tests/$curtest - if [ $? -ne 0 ]; then - echo "Test $curtest failed. Stopping" - cleanup - exit 1 - fi -done -echo "All tests passed" -cleanup From 0a8722fd78ca1f7eae1ebdd831fabf3289e7e67e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 28 Aug 2012 13:53:30 -0400 Subject: [PATCH 019/171] Rename runapitests.bash to runapitests.sh and make it use /bin/sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a simple POSIX shell script, so no need for the weird extension or for the explicit use of /bin/bash Signed-off-by: Stéphane Graber --- runapitests.bash => runapitests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename runapitests.bash => runapitests.sh (97%) diff --git a/runapitests.bash b/runapitests.sh similarity index 97% rename from runapitests.bash rename to runapitests.sh index 2b1b3baec..6419b187d 100644 --- a/runapitests.bash +++ b/runapitests.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh cleanup() { rm -f /etc/lxc/test-busybox.conf From 69d66f1e729aadfcf2f47aaedaf738a888e4646d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 29 Aug 2012 09:27:53 -0700 Subject: [PATCH 020/171] Add lxc.aa_profile example to all templates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LXC has optional apparmor support, default profile is lxc-container-default. This change adds a commented "lxc.aa_profile = default" line to all templates, uncommenting this will bypass apparmor for the container. Signed-off-by: Stéphane Graber --- templates/lxc-busybox.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/lxc-busybox.in b/templates/lxc-busybox.in index f6e8b5a9a..581074cae 100644 --- a/templates/lxc-busybox.in +++ b/templates/lxc-busybox.in @@ -233,6 +233,9 @@ cat <> $path/config lxc.utsname = $name lxc.tty = 1 lxc.pts = 1 + +# When using LXC with apparmor, uncomment the next line to run unconfined: +#lxc.aa_profile = unconfined EOF if [ -d "$rootfs/lib" ]; then From 06200a37fcdb627fce265e263947862b70bacdbf Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 31 Aug 2012 11:28:42 -0500 Subject: [PATCH 021/171] lxc-wait: initialize timeout to -1 Otherwise it defaults to 0, meaning don't wait. -1 means wait forever, which is what we want as the default behavior. Signed-off-by: Serge Hallyn --- src/lxc/lxc_wait.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lxc/lxc_wait.c b/src/lxc/lxc_wait.c index efb0a6984..385392695 100644 --- a/src/lxc/lxc_wait.c +++ b/src/lxc/lxc_wait.c @@ -75,6 +75,7 @@ Options :\n\ .options = my_longopts, .parser = my_parser, .checker = my_checker, + .timeout = -1, }; static void timeout_handler(int signal) From 5ea6163a62c386f403b6d01df2780ff9308bf08f Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 31 Aug 2012 12:25:38 -0500 Subject: [PATCH 022/171] Add lxc.hook.pre-mount This happens in the container's namespace, but before the rootfs is setup and mounted. This gives us a chance to mangle the rootfs - i.e. ecryptfs-mount it. Signed-off-by: Serge Hallyn --- src/lxc/conf.c | 6 +++++- src/lxc/conf.h | 2 +- src/lxc/confile.c | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 34788e649..2862b8084 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -110,7 +110,7 @@ lxc_log_define(lxc_conf, lxc); #endif char *lxchook_names[NUM_LXC_HOOKS] = { - "pre-start", "mount", "start", "post-stop" }; + "pre-start", "pre-mount", "mount", "start", "post-stop" }; extern int pivot_root(const char * new_root, const char * put_old); @@ -2253,6 +2253,8 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf) return -1; } + HOOK(name, "pre-mount", lxc_conf); + if (setup_rootfs(&lxc_conf->rootfs)) { ERROR("failed to setup rootfs for '%s'", name); return -1; @@ -2340,6 +2342,8 @@ int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf) if (strcmp(hook, "pre-start") == 0) which = LXCHOOK_PRESTART; + else if (strcmp(hook, "pre-mount") == 0) + which = LXCHOOK_PREMOUNT; else if (strcmp(hook, "mount") == 0) which = LXCHOOK_MOUNT; else if (strcmp(hook, "start") == 0) diff --git a/src/lxc/conf.h b/src/lxc/conf.h index f75b43061..0c39916c9 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -207,7 +207,7 @@ struct lxc_rootfs { #endif */ enum lxchooks { - LXCHOOK_PRESTART, LXCHOOK_MOUNT, LXCHOOK_START, + LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_START, LXCHOOK_POSTSTOP, NUM_LXC_HOOKS}; extern char *lxchook_names[NUM_LXC_HOOKS]; diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 1b2a8f096..dd207c101 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -98,6 +98,7 @@ static struct lxc_config_t config[] = { { "lxc.pivotdir", config_pivotdir }, { "lxc.utsname", config_utsname }, { "lxc.hook.pre-start", config_hook }, + { "lxc.hook.pre-mount", config_hook }, { "lxc.hook.mount", config_hook }, { "lxc.hook.start", config_hook }, { "lxc.hook.post-stop", config_hook }, @@ -801,6 +802,8 @@ static int config_hook(const char *key, char *value, } if (strcmp(key, "lxc.hook.pre-start") == 0) return add_hook(lxc_conf, LXCHOOK_PRESTART, copy); + else if (strcmp(key, "lxc.hook.pre-mount") == 0) + return add_hook(lxc_conf, LXCHOOK_PREMOUNT, copy); else if (strcmp(key, "lxc.hook.mount") == 0) return add_hook(lxc_conf, LXCHOOK_MOUNT, copy); else if (strcmp(key, "lxc.hook.start") == 0) From 525421c923d798cdea9e5691bcee1e5e5530491d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Sat, 1 Sep 2012 19:57:11 -0400 Subject: [PATCH 023/171] Make building the API tests/examples optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new --enable-tests option to configure which is used to optionally build the tests/examples. Default is off. Signed-off-by: Stéphane Graber --- configure.ac | 6 ++++++ src/tests/Makefile.am | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/configure.ac b/configure.ac index 7b98306b2..7018082bc 100644 --- a/configure.ac +++ b/configure.ac @@ -83,6 +83,12 @@ AM_COND_IF([ENABLE_PYTHON], AC_CHECK_HEADER([python$PYTHON_VERSION/Python.h],[],[AC_MSG_ERROR([You must install python3-dev])]) AC_DEFINE_UNQUOTED([ENABLE_PYTHON], 1, [Python3 is available])]) +AC_ARG_ENABLE([tests], + [AC_HELP_STRING([--enable-tests], [build test/example binaries])], + [enable_tests=yes], [enable_tests=no]) + +AM_CONDITIONAL([ENABLE_TESTS], [test "x$enable_tests" = "xyes"]) + AS_AC_EXPAND(PREFIX, $prefix) AS_AC_EXPAND(LIBDIR, $libdir) AS_AC_EXPAND(BINDIR, $bindir) diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index b880bc421..c77f1fbbe 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -1,3 +1,5 @@ +if ENABLE_TESTS + LDADD = ../lxc/liblxc.so -lrt containertests_SOURCES = containertests.c locktests_SOURCES = locktests.c @@ -15,3 +17,5 @@ AM_CFLAGS=-I$(top_srcdir)/src \ -DLXCINITDIR=\"$(LXCINITDIR)\" bin_PROGRAMS = containertests locktests startone destroytest saveconfig createtest shutdowntest get_item getkeys + +endif From 427b3a21ef99f2fb99ae35ea02b7c49b1a9e117c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Sat, 1 Sep 2012 20:17:32 -0400 Subject: [PATCH 024/171] Change lxc_remove_nic from returning int to void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function wasn't returning anything and none of the callers were checking for a return code. Signed-off-by: Stéphane Graber --- src/lxc/conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 2862b8084..c6e507e41 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2362,7 +2362,7 @@ int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf) return 0; } -static int lxc_remove_nic(struct lxc_list *it) +static void lxc_remove_nic(struct lxc_list *it) { struct lxc_netdev *netdev = it->elem; struct lxc_list *it2; From fe88b9d2f39375b043970788cfc16bd0bfd42322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Sat, 1 Sep 2012 20:19:53 -0400 Subject: [PATCH 025/171] Remove unused "i" variable in lxc_get_item_nic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- src/lxc/confile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/confile.c b/src/lxc/confile.c index dd207c101..8b8cb6daa 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -1400,7 +1400,7 @@ static int lxc_get_mount_entries(struct lxc_conf *c, char *retv, int inlen) static int lxc_get_item_nic(struct lxc_conf *c, char *retv, int inlen, char *key) { char *p1; - int i, len, fulllen = 0; + int len, fulllen = 0; struct lxc_netdev *netdev; if (!retv) From 85a9d078274587b5ef7bf97f7e26c9f121947c03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Sat, 1 Sep 2012 20:22:39 -0400 Subject: [PATCH 026/171] Cleanup lxc_wait MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove unused timeout_handler function. - Remove unsused variables from main() Signed-off-by: Stéphane Graber --- src/lxc/lxc_wait.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/lxc/lxc_wait.c b/src/lxc/lxc_wait.c index 385392695..9c654152a 100644 --- a/src/lxc/lxc_wait.c +++ b/src/lxc/lxc_wait.c @@ -78,17 +78,8 @@ Options :\n\ .timeout = -1, }; -static void timeout_handler(int signal) -{ - exit(-1); -} - int main(int argc, char *argv[]) { - struct lxc_msg msg; - int s[MAX_STATE] = { }, fd; - int state, ret; - if (lxc_arguments_parse(&my_args, argc, argv)) return -1; From 38b280ca2cf8895fc1d03c0034ef5bbce847cf0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Sat, 1 Sep 2012 20:25:28 -0400 Subject: [PATCH 027/171] Remove unused v1 and v2 variables in main() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- src/tests/getkeys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/getkeys.c b/src/tests/getkeys.c index 644b368c4..9bb559395 100644 --- a/src/tests/getkeys.c +++ b/src/tests/getkeys.c @@ -33,7 +33,7 @@ int main(int argc, char *argv[]) { struct lxc_container *c; int len, ret; - char v1[2], v2[256], v3[2048]; + char v3[2048]; if ((c = lxc_container_new(MYNAME)) == NULL) { fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME); From e0de36d791e82d9916cad4ac41562077cf9789c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Sat, 1 Sep 2012 22:55:03 -0400 Subject: [PATCH 028/171] Add better example/test of the python3-lxc API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaced python-lxc/test.py by a new api_test.py script that uses all the available function of the API to run a batch of basic tests. This example is useful both as a test of the API and as a guide on how to use the python API to manage containers. Signed-off-by: Stéphane Graber --- src/python-lxc/examples/api_test.py | 155 ++++++++++++++++++++++++++++ src/python-lxc/test.py | 28 ----- 2 files changed, 155 insertions(+), 28 deletions(-) create mode 100644 src/python-lxc/examples/api_test.py delete mode 100644 src/python-lxc/test.py diff --git a/src/python-lxc/examples/api_test.py b/src/python-lxc/examples/api_test.py new file mode 100644 index 000000000..1a9f2e785 --- /dev/null +++ b/src/python-lxc/examples/api_test.py @@ -0,0 +1,155 @@ +#!/usr/bin/python3 +# +# api_test.py: Test/demo of the python3-lxc API +# +# (C) Copyright Canonical Ltd. 2012 +# +# Authors: +# Stéphane Graber +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import warnings +warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable") + +import lxc +import uuid +import sys + +# Some constants +LXC_PATH_LIB = "/var/lib/lxc" +LXC_TEMPLATE = "ubuntu" + +# Let's pick a random name, avoiding clashes +CONTAINER_NAME = str(uuid.uuid1()) +CLONE_NAME = str(uuid.uuid1()) + +## Instantiate the container instance +print("Getting instance for '%s'" % CONTAINER_NAME) +container = lxc.Container(CONTAINER_NAME) + +# A few basic checks of the current state +assert(container.config_file_name == "%s/%s/config" % + (LXC_PATH_LIB, CONTAINER_NAME)) +assert(container.defined == False) +assert(container.init_pid == -1) +assert(container.name == CONTAINER_NAME) +assert(container.running == False) +assert(container.state == "STOPPED") + +## Create a rootfs +print("Creating rootfs using '%s'" % LXC_TEMPLATE) +container.create(LXC_TEMPLATE) +container.load_config() # FIXME: workaround for get_config_item segfault + +assert(container.defined == True) +assert(container.name == CONTAINER_NAME + == container.get_config_item("lxc.utsname")) +assert(container.name in lxc.list_containers()) + +## Test the config +print("Testing the configuration") +capdrop = container.get_config_item("lxc.cap.drop") +container.clear_config_item("lxc.cap.drop") +container.set_config_item("lxc.cap.drop", capdrop[:-1]) +container.append_config_item("lxc.cap.drop", capdrop[-1]) +container.save_config() + +# A few basic checks of the current state +assert(isinstance(capdrop, list)) +assert(capdrop == container.get_config_item("lxc.cap.drop")) + +## Test the networking +print("Testing the networking") +container.network.remove(0) # FIXME: workaround for get_config_item segfault + +# A few basic checks of the current state +assert("name" in container.get_keys("lxc.network.0")) +assert(len(container.network) == 1) +assert(container.network[0].hwaddr.startswith("00:16:3e")) + +## Starting the container +print("Starting the container") +container.start() +container.wait("RUNNING", 3) + +# A few basic checks of the current state +assert(container.init_pid > 1) +assert(container.running == True) +assert(container.state == "RUNNING") + +## Checking IP address +print("Getting the IP addresses") +ips = container.get_ips(timeout=10) +container.attach("NETWORK|UTSNAME", "/sbin/ifconfig", "eth0") + +# A few basic checks of the current state +assert(len(ips) > 0) + +## Freezing the container +print("Freezing the container") +container.freeze() +container.wait("FROZEN", 3) + +# A few basic checks of the current state +assert(container.init_pid > 1) +assert(container.running == True) +assert(container.state == "FROZEN") + +## Unfreezing the container +print("Unfreezing the container") +container.unfreeze() +container.wait("RUNNING", 3) + +# A few basic checks of the current state +assert(container.init_pid > 1) +assert(container.running == True) +assert(container.state == "RUNNING") + +if len(sys.argv) > 1 and sys.argv[1] == "--with-console": + ## Attaching to tty1 + print("Attaching to tty1") + container.console(tty=1) + +## Shutting down the container +print("Shutting down the container") +container.shutdown(3) + +if container.running: + print("Stopping the container") + container.stop() + container.wait("STOPPED", 3) + +# A few basic checks of the current state +assert(container.init_pid == -1) +assert(container.running == False) +assert(container.state == "STOPPED") + +## Cloning the container +print("Cloning the container") +clone = lxc.Container(CLONE_NAME) +clone.clone(container) +clone.load_config() # FIXME: workaround for get_config_item segfault +clone.network.remove(0) # FIXME: workaround for get_config_item segfault +clone.start() +clone.stop() +clone.destroy() + +## Destroy the container +print("Destroying the container") +container.destroy() + +assert(container.defined == False) diff --git a/src/python-lxc/test.py b/src/python-lxc/test.py deleted file mode 100644 index 5552fe13b..000000000 --- a/src/python-lxc/test.py +++ /dev/null @@ -1,28 +0,0 @@ -import lxc - -t1 = lxc.Container("test") -print("Name set properly: %s" % (t1.name == "test")) -print("Test config loaded properly: %s" % t1.load_config("/etc/lxc/lxc.conf")) -print("Real config loaded properly: %s" % t1.load_config()) -print("Test config path: %s" % (t1.config_file_name == "/var/lib/lxc/test/config")) -print("Set config item: %s" % t1.set_config_item("lxc.utsname", "blabla")) -print("Container defined: %s" % (t1.defined)) -print("Started properly: %s" % t1.start()) -print("Container running: %s" % t1.wait("RUNNING")) -print("Container state: %s" % t1.state) -print("Container running: %s" % t1.running) -print("Container init process: %s" % t1.init_pid) -print("Freezing: %s" % t1.freeze()) -print("Container frozen: %s" % t1.wait("FROZEN")) -print("Container state: %s" % t1.state) -print("Unfreezing: %s" % t1.unfreeze()) -print("Container running: %s" % t1.wait("RUNNING")) -print("Container state: %s" % t1.state) -print("Stopped properly: %s" % t1.stop()) -print("Container state: %s" % t1.state) - -#print("Started properly: %s" % t1.start(useinit=True)) -#print("Container running: %s" % t1.wait("RUNNING")) -#print("Container state: %s" % t1.state) -#print("Stopped properly: %s" % t1.stop()) -#print("Container state: %s" % t1.state) From 89eaa05ed1a1ddd5d2552c323b5228def627d731 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 4 Sep 2012 13:57:39 -0500 Subject: [PATCH 029/171] replace HOOK define with proper code. Signed-off-by: Serge Hallyn --- src/lxc/conf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index c6e507e41..dfdff54a5 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2253,7 +2253,10 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf) return -1; } - HOOK(name, "pre-mount", lxc_conf); + if (run_lxc_hooks(name, "pre-mount", lxc_conf)) { + ERROR("failed to run pre-mount hooks for container '%s'.", name); + return -1; + } if (setup_rootfs(&lxc_conf->rootfs)) { ERROR("failed to setup rootfs for '%s'", name); From c278cef2ecd6fc42132d02f982e113414f5f9ce4 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 4 Sep 2012 14:10:40 -0500 Subject: [PATCH 030/171] check chdir(/) return value Signed-off-by: Serge Hallyn --- src/lxc/lxccontainer.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index e88460b86..840679b5b 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -320,7 +320,10 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char ** argv) if (pid != 0) return wait_on_daemonized_start(c); /* like daemon(), chdir to / and redirect 0,1,2 to /dev/null */ - chdir("/"); + if (chdir("/")) { + SYSERROR("Error chdir()ing to /."); + return false; + } close(0); close(1); close(2); From 4a7c7daa260467f3c9b234495bf3283fe31c01cb Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 4 Sep 2012 14:18:03 -0500 Subject: [PATCH 031/171] Fix passing non-const char* in for const char* Signed-off-by: Serge Hallyn --- src/lxc/lxc.h | 2 +- src/lxc/lxc_wait.c | 2 +- src/lxc/lxccontainer.c | 2 +- src/lxc/lxccontainer.h | 2 +- src/tests/containertests.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lxc/lxc.h b/src/lxc/lxc.h index 807a0cecb..93849eca3 100644 --- a/src/lxc/lxc.h +++ b/src/lxc/lxc.h @@ -201,7 +201,7 @@ extern int lxc_container_put(struct lxc_container *c); * Get a list of valid wait states. * If states is NULL, simply return the number of states */ -extern int lxc_get_wait_states(char **states); +extern int lxc_get_wait_states(const char **states); #ifdef __cplusplus } diff --git a/src/lxc/lxc_wait.c b/src/lxc/lxc_wait.c index 9c654152a..de1163e37 100644 --- a/src/lxc/lxc_wait.c +++ b/src/lxc/lxc_wait.c @@ -87,5 +87,5 @@ int main(int argc, char *argv[]) my_args.progname, my_args.quiet)) return -1; - return lxc_wait(my_args.name, my_args.states, my_args.timeout); + return lxc_wait(strdup(my_args.name), my_args.states, my_args.timeout); } diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 840679b5b..471fecc2d 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -906,7 +906,7 @@ err: return NULL; } -int lxc_get_wait_states(char **states) +int lxc_get_wait_states(const char **states) { int i; diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index 632063342..cad31ee98 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -63,7 +63,7 @@ struct lxc_container { struct lxc_container *lxc_container_new(char *name); int lxc_container_get(struct lxc_container *c); int lxc_container_put(struct lxc_container *c); -int lxc_get_wait_states(char **states); +int lxc_get_wait_states(const char **states); #if 0 char ** lxc_get_valid_keys(); diff --git a/src/tests/containertests.c b/src/tests/containertests.c index ef3cda65b..700374fcc 100644 --- a/src/tests/containertests.c +++ b/src/tests/containertests.c @@ -210,7 +210,7 @@ int main(int argc, char *argv[]) fprintf(stderr, "%d: lxc_get_wait_states gave %d not %d\n", __LINE__, numstates, MAX_STATE); goto out; } - char **sstr = malloc(numstates * sizeof(char *)); + const char **sstr = malloc(numstates * sizeof(const char *)); numstates = lxc_get_wait_states(sstr); int i; for (i=0; i Date: Tue, 4 Sep 2012 18:06:44 -0500 Subject: [PATCH 032/171] get_item(utsname): don't dereference utsname if it is NULL Signed-off-by: Serge Hallyn --- src/lxc/confile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 8b8cb6daa..11d863b34 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -1530,7 +1530,7 @@ int lxc_get_config_item(struct lxc_conf *c, char *key, char *retv, int inlen) else if (strncmp(key, "lxc.cgroup.", 11) == 0) // specific cgroup info return lxc_get_cgroup_entry(c, retv, inlen, key + 11); else if (strcmp(key, "lxc.utsname") == 0) - v = c->utsname->nodename; + v = c->utsname ? c->utsname->nodename : NULL; else if (strcmp(key, "lxc.console") == 0) v = c->console.path; else if (strcmp(key, "lxc.rootfs.mount") == 0) From 8eb5694bafff6be81a35542c584e82817a1e0852 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Wed, 5 Sep 2012 21:55:38 -0500 Subject: [PATCH 033/171] Add lxc_conf_free() Then after lxcapi container->create(), free whatever lxc_conf may be loaded and reload from the newly created configuration file. Signed-off-by: Serge Hallyn --- src/lxc/conf.c | 18 ++++++++++++++++++ src/lxc/conf.h | 1 + src/lxc/lxccontainer.c | 27 ++++++++++++++++++++------- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index dfdff54a5..c110f6a27 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2556,3 +2556,21 @@ int lxc_clear_hooks(struct lxc_conf *c) } return 0; } + +void lxc_conf_free(struct lxc_conf *conf) +{ + if (!conf) + return; + if (conf->console.path) + free(conf->console.path); + if (conf->rootfs.mount != LXCROOTFSMOUNT) + free(conf->rootfs.mount); + lxc_clear_config_network(conf); + if (conf->aa_profile) + free(conf->aa_profile); + lxc_clear_config_caps(conf); + lxc_clear_cgroups(conf, "lxc.cgroup"); + lxc_clear_hooks(conf); + lxc_clear_mount_entries(conf); + free(conf); +} diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 0c39916c9..dcf79fe5c 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -245,6 +245,7 @@ int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf); * Initialize the lxc configuration structure */ extern struct lxc_conf *lxc_conf_init(void); +extern void lxc_conf_free(struct lxc_conf *conf); extern int pin_rootfs(const char *rootfs); diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 471fecc2d..c8dc8c483 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -215,6 +215,15 @@ static pid_t lxcapi_init_pid(struct lxc_container *c) return ret; } +static bool load_config_locked(struct lxc_container *c, char *fname) +{ + if (!c->lxc_conf) + c->lxc_conf = lxc_conf_init(); + if (c->lxc_conf && !lxc_config_read(fname, c->lxc_conf)) + return true; + return false; +} + static bool lxcapi_load_config(struct lxc_container *c, char *alt_file) { bool ret = false; @@ -229,10 +238,7 @@ static bool lxcapi_load_config(struct lxc_container *c, char *alt_file) return false; if (lxclock(c->slock, 0)) return false; - if (!c->lxc_conf) - c->lxc_conf = lxc_conf_init(); - if (c->lxc_conf && !lxc_config_read(fname, c->lxc_conf)) - ret = true; + ret = load_config_locked(c, fname); lxcunlock(c->slock); return ret; } @@ -596,11 +602,18 @@ again: goto out_unlock; } - if (WEXITSTATUS(status) != 0) + if (WEXITSTATUS(status) != 0) { ERROR("container creation template for %s exited with %d\n", c->name, WEXITSTATUS(status)); - else - bret = true; + goto out_unlock; + } + + // now clear out the lxc_conf we have, reload from the created + // container + if (c->lxc_conf) + lxc_conf_free(c->lxc_conf); + c->lxc_conf = NULL; + bret = load_config_locked(c, c->configfile); out_unlock: lxcunlock(c->slock); From 472c97e97684494ecfd0541fa779c301a1855447 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Thu, 6 Sep 2012 12:26:29 -0500 Subject: [PATCH 034/171] document lxc.hooks in lxc.conf manpage Signed-off-by: Serge Hallyn --- doc/lxc.conf.sgml.in | 79 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in index 80c4335e3..351966e35 100644 --- a/doc/lxc.conf.sgml.in +++ b/doc/lxc.conf.sgml.in @@ -660,6 +660,85 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Startup hooks + + Startup hooks are programs or scripts which can be executed + at various times in a container's lifetime. + + + + + + + + + A hook to be run in the host's namespace before the + container ttys, consoles, or mounts are up. + + + + + + + + + + + + (Not yet implemented) + A hook to be run in the container's fs namespace but before + the rootfs has been set up. This allows for manipulation + of the rootfs, i.e. to mount an encrypted filesystem. Mounts + done in this hook will not be reflected on the host (apart from + mounts propagation), so they will be automatically cleaned up + when the container shuts down. + + + + + + + + + + + + A hook to be run in the container's namespace after + mounting has been done, but before the pivot_root. + + + + + + + + + + + + A hook to be run in the container's namespace immediately + before executing the container's init. This requires the + program to be available in the container. + + + + + + + + + + + + A hook to be run in the host's namespace after the + container has been shut down. + + + + + + From 767d4c6743155a6835a596fc6b1baf56e14ad430 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Thu, 6 Sep 2012 12:45:16 -0500 Subject: [PATCH 035/171] premount hook is implemented in git Signed-off-by: Serge Hallyn --- doc/lxc.conf.sgml.in | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in index 351966e35..1428f252c 100644 --- a/doc/lxc.conf.sgml.in +++ b/doc/lxc.conf.sgml.in @@ -686,7 +686,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - (Not yet implemented) A hook to be run in the container's fs namespace but before the rootfs has been set up. This allows for manipulation of the rootfs, i.e. to mount an encrypted filesystem. Mounts From cbd4c46406b0a34291a9962c834b7de871ce30ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 6 Sep 2012 16:06:27 -0400 Subject: [PATCH 036/171] Raise exception when getting Container instance as non-root in python3-lxc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The liblxc API currently doesn't work as non-root, so check that the euid is 0 when getting a Container instance in the python API. Signed-off-by: Stéphane Graber --- src/python-lxc/lxc/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/python-lxc/lxc/__init__.py b/src/python-lxc/lxc/__init__.py index 94616f59a..5fa99ed41 100644 --- a/src/python-lxc/lxc/__init__.py +++ b/src/python-lxc/lxc/__init__.py @@ -32,6 +32,7 @@ import warnings warnings.warn("The python-lxc API isn't yet stable " "and may change at any point in the future.", Warning, 2) + class ContainerNetwork(): props = {} @@ -142,6 +143,9 @@ class Container(_lxc.Container): Creates a new Container instance. """ + if os.geteuid() != 0: + raise Exception("Running as non-root.") + _lxc.Container.__init__(self, name) self.network = ContainerNetworkList(self) From 59b3bc264cbf0996cd7b446afd219ab1ff0fe53c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 7 Sep 2012 10:50:36 -0400 Subject: [PATCH 037/171] Remove zombie_handler from python-lxc code This code was addeed to deal with stopped/dead containers but really shouldn't be implemented there. Instead the setsid() call in start() should be enough to prevent python from getting the SIGCHLD and having to deal with it. --- src/python-lxc/lxc.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/python-lxc/lxc.c b/src/python-lxc/lxc.c index f58a954de..529278cd0 100644 --- a/src/python-lxc/lxc.c +++ b/src/python-lxc/lxc.c @@ -59,14 +59,6 @@ convert_tuple_to_char_pointer_array(PyObject *argv) { return result; } -void zombie_handler(int sig) -{ - signal(SIGCHLD,zombie_handler); - int status; - - waitpid(-1, &status, WNOHANG); -} - static void Container_dealloc(Container* self) { @@ -353,7 +345,6 @@ Container_start(Container *self, PyObject *args, PyObject *kwds) } } - signal(SIGCHLD, zombie_handler); self->container->want_daemonize(self->container); if (self->container->start(self->container, init_useinit, init_args)) { From 697fa6390c5d52eaef36a15c31b68d093a0d0941 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 7 Sep 2012 11:14:04 -0500 Subject: [PATCH 038/171] dual-fork for daemonized fork in lxcapi-start So the container will be reparented by init. Otherwise children of the lxc-start might be reaped by python3 rather than lxc-start. Signed-off-by: Serge Hallyn --- src/lxc/lxccontainer.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index c8dc8c483..ea39710b3 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -265,8 +265,15 @@ static bool lxcapi_wait(struct lxc_container *c, char *state, int timeout) static bool wait_on_daemonized_start(struct lxc_container *c) { /* we'll probably want to make this timeout configurable? */ - int timeout = 5; + int timeout = 5, ret, status; + /* + * our child is going to fork again, then exit. reap the + * child + */ + ret = wait(&status); + if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) + DEBUG("failed waiting for first dual-fork child"); return lxcapi_wait(c, "RUNNING", timeout); } @@ -325,6 +332,14 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char ** argv) } if (pid != 0) return wait_on_daemonized_start(c); + /* second fork to be reparented by init */ + pid = fork(); + if (pid < 0) { + SYSERROR("Error doing dual-fork"); + return false; + } + if (pid != 0) + exit(0); /* like daemon(), chdir to / and redirect 0,1,2 to /dev/null */ if (chdir("/")) { SYSERROR("Error chdir()ing to /."); From d7415aea482652cd4b035d18c4bcf0edfc409d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 7 Sep 2012 13:11:05 -0400 Subject: [PATCH 039/171] Add lxc-start-ephemeral MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds lxc-start-ephemeral as a python script using the new python-lxc API. This script is somewhat similar to lxc-clone except that it uses overlayfs or aufs to provide an overlay on top of the source container. It also allows the user to directly run a command in the container using SSH and can fetch the IP address from the container when starting the container in the background. The initial work on lxc-start-ephemeral was done by Serge Hallyn in Ubuntu, this is a re-implementation of it using python and the new LXC hooks. Compared to the shell implementation, there are three notable differences: - When starting without a command, lxc-start-ephemeral now attaches to tty1 - When starting in the background (-d), the name and IP of the container is shown on screen. - A new "-k" option is added, allowing the user to keep the ephemeral container after shutdown. This turns off the tmpfs backend and sets up the hooks so that the container can be started/stopped multiple times. Signed-off-by: Stéphane Graber --- src/lxc/Makefile.am | 4 + src/lxc/lxc-start-ephemeral | 290 ++++++++++++++++++++++++++++++++++++ 2 files changed, 294 insertions(+) create mode 100644 src/lxc/lxc-start-ephemeral diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index b2f2128c4..7d86ad661 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -97,6 +97,10 @@ bin_SCRIPTS = \ lxc-shutdown \ lxc-destroy +if ENABLE_PYTHON + bin_SCRIPTS += lxc-start-ephemeral +endif + bin_PROGRAMS = \ lxc-attach \ lxc-unshare \ diff --git a/src/lxc/lxc-start-ephemeral b/src/lxc/lxc-start-ephemeral new file mode 100644 index 000000000..4f238a361 --- /dev/null +++ b/src/lxc/lxc-start-ephemeral @@ -0,0 +1,290 @@ +#!/usr/bin/python3 +# +# lxc-start-ephemeral: Start a copy of a container using an overlay +# +# This python implementation is based on the work done in the original +# shell implementation done by Serge Hallyn in Ubuntu (and other contributors) +# +# (C) Copyright Canonical Ltd. 2012 +# +# Authors: +# Stéphane Graber +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# NOTE: To remove once the API is stabilized +import warnings +warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable") + +import argparse +import gettext +import lxc +import os +import sys +import subprocess +import tempfile + +_ = gettext.gettext +gettext.textdomain("lxc-start-ephemeral") + + +# Other functions +def randomMAC(): + import random + + mac = [0x00, 0x16, 0x3e, + random.randint(0x00, 0x7f), + random.randint(0x00, 0xff), + random.randint(0x00, 0xff)] + return ':'.join(map(lambda x: "%02x" % x, mac)) + +# Begin parsing the command line +parser = argparse.ArgumentParser( + description=_("LXC: Start an ephemeral container"), + formatter_class=argparse.RawTextHelpFormatter, epilog=_( +"""If a COMMAND is given, then the container will run only as long +as the command runs. +If no COMMAND is given, this command will attach to tty1 and stop the +container when exiting (with ctrl-a-q). + +If no COMMAND is given and -d is used, the name and IP addresses of the +container will be printed to the console.""")) + +parser.add_argument("--orig", "-o", type=str, required=True, + help=_("name of the original container")) + +parser.add_argument("--bdir", "-b", type=str, + help=_("directory to bind mount into container")) + +parser.add_argument("--user", "-u", type=str, + help=_("the user to connect to the container as")) + +parser.add_argument("--key", "-S", type=str, + help=_("the path to the SSH key to use to connect")) + +parser.add_argument("--daemon", "-d", action="store_true", + help=_("run in the background")) + +parser.add_argument("--union-type", "-U", type=str, default="overlayfs", + choices=("overlayfs", "aufs"), + help=_("type of union (overlayfs or aufs), defaults to overlayfs.")) + +parser.add_argument("--keep-data", "-k", action="store_true", + help=_("Use a persistent backend instead of tmpfs.")) + +parser.add_argument("command", metavar='CMD', type=str, nargs="*", + help=_("Run specific command in container (command as argument)")) + +args = parser.parse_args() + +# Basic requirements check +## Check that -d and CMD aren't used at the same time +if args.command and args.daemon: + print(_("You can't use -d and a command at the same time.")) + sys.exit(1) + +## The user needs to be uid 0 +if not os.geteuid() == 0: + print(_("You must be root to run this script. Try running: sudo %s" % + (sys.argv[0]))) + sys.exit(1) + +# Load the orig container +orig = lxc.Container(args.orig) +if not orig.defined: + print(_("Source container '%s' doesn't exist." % args.orig)) + sys.exit(1) + +# Create the new container paths +dest_path = tempfile.mkdtemp(prefix="%s-" % args.orig, dir="/var/lib/lxc/") +os.mkdir(os.path.join(dest_path, "rootfs")) + +# Setup the new container's configuration +dest = lxc.Container(os.path.basename(dest_path)) +dest.load_config(orig.config_file_name) +dest.set_config_item("lxc.utsname", dest.name) +dest.set_config_item("lxc.rootfs", os.path.join(dest_path, "rootfs")) +dest.set_config_item("lxc.network.hwaddr", randomMAC()) + +overlay_dirs = [(orig.get_config_item("lxc.rootfs"), "%s/rootfs/" % dest_path)] + +# Generate a new fstab +if orig.get_config_item("lxc.mount"): + dest.set_config_item("lxc.mount", os.path.join(dest_path, "fstab")) + with open(orig.get_config_item("lxc.mount"), "r") as orig_fd: + with open(dest.get_config_item("lxc.mount"), "w+") as dest_fd: + for line in orig_fd.read().split("\n"): + # Start by replacing any reference to the container rootfs + line.replace(orig.get_config_item("lxc.rootfs"), + dest.get_config_item("lxc.rootfs")) + + # Skip any line that's not a bind mount + fields = line.split() + if len(fields) < 4: + dest_fd.write("%s\n" % line) + continue + + if fields[2] != "bind" and "bind" not in fields[3]: + dest_fd.write("%s\n" % line) + continue + + # Process any remaining line + dest_mount = os.path.abspath(os.path.join("%s/rootfs/" % ( + dest_path), fields[1])) + + if dest_mount == os.path.abspath("%s/rootfs/%s" % ( + dest_path, args.bdir)): + + dest_fd.write("%s\n" % line) + continue + + if "%s/rootfs/" % dest_path not in dest_mount: + print(_( +"Skipping mount entry '%s' as it's outside of the container rootfs.") % line) + + overlay_dirs += [(fields[0], dest_mount)] + +# Generate pre-mount script +with open(os.path.join(dest_path, "pre-mount"), "w+") as fd: + os.fchmod(fd.fileno(), 0o755) + fd.write("""#!/bin/sh +LXC_DIR="%s" +LXC_BASE="%s" +LXC_NAME="%s" +""" % (dest_path, orig.name, dest.name)) + + count = 0 + for entry in overlay_dirs: + target = "%s/delta%s" % (dest_path, count) + fd.write("mkdir -p %s %s\n" % (target, entry[1])) + + if not args.keep_data: + fd.write("mount -n -t tmpfs none %s\n" % (target)) + + if args.union_type == "overlayfs": + fd.write("mount -n -t overlayfs" + " -oupperdir=%s,lowerdir=%s none %s\n" % ( + target, + entry[0], + entry[1])) + elif args.union_type == "aufs": + fd.write("mount -n -t aufs " + "-o br=${upper}=rw:${lower}=ro,noplink none %s\n" % ( + target, + entry[0], + entry[1])) + count += 1 + + if args.bdir: + if not os.path.exists(args.bdir): + print(_("Path '%s' doesn't exist, won't be bind-mounted.") % + args.bdir) + else: + src_path = os.path.abspath(args.bdir) + dst_path = "%s/rootfs/%s" % (dest_path, os.path.abspath(args.bdir)) + fd.write("mkdir -p %s\nmount -n --bind %s %s\n" % ( + dst_path, src_path, dst_path)) + + fd.write(""" +[ -e $LXC_DIR/configured ] && exit 0 +for file in $LXC_DIR/rootfs/etc/hostname \\ + $LXC_DIR/rootfs/etc/hosts \\ + $LXC_DIR/rootfs/etc/sysconfig/network \\ + $LXC_DIR/rootfs/etc/sysconfig/network-scripts/ifcfg-eth0; do + [ -f "$file" ] && sed -i -e "s/$LXC_BASE/$LXC_NAME/" $file +done +touch $LXC_DIR/configured +""") + +dest.set_config_item("lxc.hook.pre-mount", + os.path.join(dest_path, "pre-mount")) + +# Generate post-stop script +if not args.keep_data: + with open(os.path.join(dest_path, "post-stop"), "w+") as fd: + os.fchmod(fd.fileno(), 0o755) + fd.write("""#!/bin/sh +[ -n "$1" ] && [ -d "/var/lib/lxc/$1/" ] && rm -Rf /var/lib/lxc/$1/ +""") + + dest.set_config_item("lxc.hook.post-stop", + os.path.join(dest_path, "post-stop")) + +dest.save_config() + +# Start the container +if not dest.start() or not dest.wait("RUNNING", timeout=5): + print(_("The container '%s' failed to start.") % dest.name) + dest.stop() + if dest.defined: + dest.destroy() + sys.exit(1) + +# Try to get the IP addresses +ips = dest.get_ips(timeout=5) + +# Deal with the case where we don't start a command in the container +if not args.command: + if args.daemon: + print(_("""The ephemeral container is now started. + +You can enter it from the command line with: lxc-console -n %s +The following IP addresses have be found in the container: +%s""") % ( + dest.name, + "\n".join([" - %s" % entry for entry in ips] + or [" - %s" % _("No address could be found")]) + )) + sys.exit(0) + else: + dest.console(tty=1) + if not dest.shutdown(timeout=5): + dest.stop() + sys.exit(0) + +# Now deal with the case where we want to run a command in the container +if not ips: + print(_("Failed to get an IP for container '%s'.") % dest.name) + dest.stop() + if dest.defined: + dest.destroy() + sys.exit(1) + +# NOTE: To replace by .attach() once the kernel supports it +cmd = ["ssh", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null"] + +if args.user: + cmd += ["-l", args.user] + +if args.key: + cmd += ["-k", args.key] + +for ip in ips: + ssh_cmd = cmd + [ip] + args.command + retval = subprocess.call(ssh_cmd, universal_newlines=True) + if retval == 255: + print(_("SSH failed to connect, trying next IP address.")) + continue + + if retval != 0: + print(_("Command returned with non-zero return code: %s") % retval) + break + +# Shutdown the container +if not dest.shutdown(timeout=5): + dest.stop() From caf32f58cd5b24cdd127dce6823e10a8a5323112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 7 Sep 2012 14:37:58 -0400 Subject: [PATCH 040/171] Remove hardcoded /var/lib/lxc from lxc-start-ephemeral Add dependency on sed and add a Makefile.am section for lxc-start-ephemeral so that it gets updated at build time for the right container path. --- src/lxc/{lxc-start-ephemeral => lxc-start-ephemeral.in} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/lxc/{lxc-start-ephemeral => lxc-start-ephemeral.in} (100%) diff --git a/src/lxc/lxc-start-ephemeral b/src/lxc/lxc-start-ephemeral.in similarity index 100% rename from src/lxc/lxc-start-ephemeral rename to src/lxc/lxc-start-ephemeral.in From 95a717e9b907de94bf29c91abca13012ec47d088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 7 Sep 2012 14:53:19 -0400 Subject: [PATCH 041/171] Fix previous commit, removing hardcoded /var/lib/lxc from lxc-start-ephemeral MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit was missing part of the changes, leading to a non-working version of lxc-start-ephemeral. This commit adds the missing parts. Signed-off-by: Stéphane Graber --- configure.ac | 1 + src/lxc/Makefile.am | 4 ++++ src/lxc/lxc-start-ephemeral.in | 6 +++--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 7018082bc..6f819d27a 100644 --- a/configure.ac +++ b/configure.ac @@ -143,6 +143,7 @@ AC_CHECK_DECLS([PR_CAPBSET_DROP], [], [], [#include ]) AC_CHECK_HEADERS([sys/signalfd.h]) AC_PROG_GCC_TRADITIONAL +AC_PROG_SED if test "x$GCC" = "xyes"; then CFLAGS="$CFLAGS -Wall" diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 7d86ad661..11c025728 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -144,6 +144,10 @@ lxc_unshare_SOURCES = lxc_unshare.c lxc_wait_SOURCES = lxc_wait.c lxc_kill_SOURCES = lxc_kill.c +lxc-start-ephemeral: lxc-start-ephemeral.in + [ -f $@ ] && rm -f $@ || true + $(SED) -e "s:[@]LXCPATH@:$(LXCPATH):" $< > $@ + install-exec-local: install-soPROGRAMS mv $(DESTDIR)$(libdir)/liblxc.so $(DESTDIR)$(libdir)/liblxc.so.$(VERSION) /sbin/ldconfig -l $(DESTDIR)$(libdir)/liblxc.so.$(VERSION) diff --git a/src/lxc/lxc-start-ephemeral.in b/src/lxc/lxc-start-ephemeral.in index 4f238a361..71b186131 100644 --- a/src/lxc/lxc-start-ephemeral.in +++ b/src/lxc/lxc-start-ephemeral.in @@ -109,7 +109,7 @@ if not orig.defined: sys.exit(1) # Create the new container paths -dest_path = tempfile.mkdtemp(prefix="%s-" % args.orig, dir="/var/lib/lxc/") +dest_path = tempfile.mkdtemp(prefix="%s-" % args.orig, dir="@LXCPATH@") os.mkdir(os.path.join(dest_path, "rootfs")) # Setup the new container's configuration @@ -217,8 +217,8 @@ if not args.keep_data: with open(os.path.join(dest_path, "post-stop"), "w+") as fd: os.fchmod(fd.fileno(), 0o755) fd.write("""#!/bin/sh -[ -n "$1" ] && [ -d "/var/lib/lxc/$1/" ] && rm -Rf /var/lib/lxc/$1/ -""") +[ -d "%s" ] && rm -Rf "%s" +""" % (dest.get_config_item("lxc.rootfs"), dest.get_config_item("lxc.rootfs")) dest.set_config_item("lxc.hook.post-stop", os.path.join(dest_path, "post-stop")) From 733a0e89ac30930a951f38620ab28a014e621a79 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 7 Sep 2012 17:48:29 -0500 Subject: [PATCH 042/171] check sscanf return value Signed-off-by: Serge Hallyn --- src/tests/containertests.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tests/containertests.c b/src/tests/containertests.c index 700374fcc..db6942fa2 100644 --- a/src/tests/containertests.c +++ b/src/tests/containertests.c @@ -220,7 +220,9 @@ int main(int argc, char *argv[]) printf("hit return to start container"); char mychar; - scanf("%c", &mychar); + ret = scanf("%c", &mychar); + if (ret < 0) + goto out; /* non-daemonized is tested in 'startone' */ c->want_daemonize(c); @@ -242,7 +244,10 @@ int main(int argc, char *argv[]) } printf("hit return to finish"); - scanf("%c", &mychar); + ret = scanf("%c", &mychar); + if (ret < 0) + goto out; + fprintf(stderr, "all lxc_container tests passed for %s\n", c->name); ret = 0; From 9c9b984556a3ad14847cb603897ab1689a0db721 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 7 Sep 2012 17:52:06 -0500 Subject: [PATCH 043/171] check return values of scanf and system Signed-off-by: Serge Hallyn --- src/tests/startone.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/tests/startone.c b/src/tests/startone.c index 9097b8e92..d5b8d3fd3 100644 --- a/src/tests/startone.c +++ b/src/tests/startone.c @@ -144,7 +144,9 @@ int main(int argc, char *argv[]) printf("hit return to start container"); char mychar; - scanf("%c", &mychar); + ret = scanf("%c", &mychar); + if (ret < 0) + goto out; if (!lxc_container_get(c)) { fprintf(stderr, "%d: failed to get extra ref to container\n", __LINE__); @@ -171,20 +173,33 @@ int main(int argc, char *argv[]) } printf("hit return to finish"); - scanf("%c", &mychar); + ret = scanf("%c", &mychar); + if (ret < 0) + goto out; c->stop(c); - system("mkdir -p /var/lib/lxc/lxctest1/rootfs//usr/local/libexec/lxc"); - system("mkdir -p /var/lib/lxc/lxctest1/rootfs/usr/lib/lxc/"); - system("cp src/lxc/lxc-init /var/lib/lxc/lxctest1/rootfs//usr/local/libexec/lxc"); - system("cp src/lxc/liblxc.so /var/lib/lxc/lxctest1/rootfs/usr/lib/lxc"); - system("cp src/lxc/liblxc.so /var/lib/lxc/lxctest1/rootfs/usr/lib/lxc/liblxc.so.0"); - system("cp src/lxc/liblxc.so /var/lib/lxc/lxctest1/rootfs/usr/lib"); - system("mkdir -p /var/lib/lxc/lxctest1/rootfs/dev/shm"); - system("chroot /var/lib/lxc/lxctest1/rootfs apt-get install --no-install-recommends lxc"); + ret = system("mkdir -p /var/lib/lxc/lxctest1/rootfs//usr/local/libexec/lxc"); + if (!ret) + system("mkdir -p /var/lib/lxc/lxctest1/rootfs/usr/lib/lxc/"); + if (!ret) + ret = system("cp src/lxc/lxc-init /var/lib/lxc/lxctest1/rootfs//usr/local/libexec/lxc"); + if (!ret) + ret = system("cp src/lxc/liblxc.so /var/lib/lxc/lxctest1/rootfs/usr/lib/lxc"); + if (!ret) + ret = system("cp src/lxc/liblxc.so /var/lib/lxc/lxctest1/rootfs/usr/lib/lxc/liblxc.so.0"); + if (!ret) + ret = system("cp src/lxc/liblxc.so /var/lib/lxc/lxctest1/rootfs/usr/lib"); + if (!ret) + ret = system("mkdir -p /var/lib/lxc/lxctest1/rootfs/dev/shm"); + if (!ret) + ret = system("chroot /var/lib/lxc/lxctest1/rootfs apt-get install --no-install-recommends lxc"); + if (ret) { + fprintf(stderr, "%d: failed to installing lxc-init in container\n", __LINE__); + goto out; + } // next write out the config file; does it match? if (!c->startl(c, 1, "/bin/hostname", NULL)) { - fprintf(stderr, "%d: failed to lxc-execute /bin/hostname", __LINE__); + fprintf(stderr, "%d: failed to lxc-execute /bin/hostname\n", __LINE__); goto out; } // auto-check result? ('bobo' is printed on stdout) From 16216c83297543692b8dede52c9dd8a998758e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Sun, 9 Sep 2012 13:14:38 -0400 Subject: [PATCH 044/171] Prefix the test binaries by lxc-test- MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- runapitests.sh | 2 +- src/tests/Makefile.am | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/runapitests.sh b/runapitests.sh index 6419b187d..65bee6cad 100644 --- a/runapitests.sh +++ b/runapitests.sh @@ -18,7 +18,7 @@ EOF [ -f liblxc.so.0 ] || ln -s src/lxc/liblxc.so ./liblxc.so.0 export LD_LIBRARY_PATH=. -TESTS="containertests locktests startone" +TESTS="lxc-test-containertests lxc-test-locktests lxc-test-startone" for curtest in $TESTS; do echo "running $curtest" ./src/tests/$curtest diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index c77f1fbbe..f4e4a5a21 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -1,21 +1,23 @@ if ENABLE_TESTS LDADD = ../lxc/liblxc.so -lrt -containertests_SOURCES = containertests.c -locktests_SOURCES = locktests.c -startone_SOURCES = startone.c -destroytest_SOURCES = destroytest.c -saveconfig_SOURCES = saveconfig.c -createtest_SOURCES = createtest.c -shutdowntest_SOURCES = shutdowntest.c -get_item_SOURCES = get_item.c -getkeys_SOURCES = getkeys.c +lxc_test_containertests_SOURCES = containertests.c +lxc_test_locktests_SOURCES = locktests.c +lxc_test_startone_SOURCES = startone.c +lxc_test_destroytest_SOURCES = destroytest.c +lxc_test_saveconfig_SOURCES = saveconfig.c +lxc_test_createtest_SOURCES = createtest.c +lxc_test_shutdowntest_SOURCES = shutdowntest.c +lxc_test_get_item_SOURCES = get_item.c +lxc_test_getkeys_SOURCES = getkeys.c AM_CFLAGS=-I$(top_srcdir)/src \ -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -DLXCPATH=\"$(LXCPATH)\" \ -DLXCINITDIR=\"$(LXCINITDIR)\" -bin_PROGRAMS = containertests locktests startone destroytest saveconfig createtest shutdowntest get_item getkeys +bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \ + lxc-test-destroytest lxc-test-saveconfig lxc-test-createtest \ + lxc-test-shutdowntest lxc-test-get_item lxc-test-getkeys endif From 87540ad7d8cc3716d3e2204ffbdc7a81d5bf7d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 10 Sep 2012 14:06:06 -0400 Subject: [PATCH 045/171] python-lxc: Always convert state passed to wait() to uppercase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At Serge's suggestion, always convert the state passed to the wait() function in the python API to its uppercase equivalent. Signed-off-by: Stéphane Graber --- src/python-lxc/lxc/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/python-lxc/lxc/__init__.py b/src/python-lxc/lxc/__init__.py index 5fa99ed41..6b671db5f 100644 --- a/src/python-lxc/lxc/__init__.py +++ b/src/python-lxc/lxc/__init__.py @@ -362,6 +362,15 @@ class Container(_lxc.Container): self.clear_config_item(key) return False + def wait(self, state, timeout = -1): + """ + Wait for the container to reach a given state or timeout. + """ + + if isinstance(state, str): + state = state.upper() + + _lxc.Container.wait(self, state, timeout) def list_containers(as_object=False): """ From 1f530df63238a22a9e74c13df27cb027b8c9cfe6 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 10 Sep 2012 14:26:43 -0400 Subject: [PATCH 046/171] fix compile without apparmor (against git staging) Add a few missing #if's to fix compilation when configured without AppArmor. Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/conf.c | 2 ++ src/lxc/confile.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index c110f6a27..0218cdac7 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2566,8 +2566,10 @@ void lxc_conf_free(struct lxc_conf *conf) if (conf->rootfs.mount != LXCROOTFSMOUNT) free(conf->rootfs.mount); lxc_clear_config_network(conf); +#if HAVE_APPARMOR if (conf->aa_profile) free(conf->aa_profile); +#endif lxc_clear_config_caps(conf); lxc_clear_cgroups(conf, "lxc.cgroup"); lxc_clear_hooks(conf); diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 11d863b34..abe4cfa2a 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -1523,8 +1523,10 @@ int lxc_get_config_item(struct lxc_conf *c, char *key, char *retv, int inlen) v = c->ttydir; else if (strcmp(key, "lxc.arch") == 0) return lxc_get_arch_entry(c, retv, inlen); +#if HAVE_APPARMOR else if (strcmp(key, "lxc.aa_profile") == 0) v = c->aa_profile; +#endif else if (strcmp(key, "lxc.cgroup") == 0) // all cgroup info return lxc_get_cgroup_entry(c, retv, inlen, "all"); else if (strncmp(key, "lxc.cgroup.", 11) == 0) // specific cgroup info @@ -1598,8 +1600,10 @@ void write_config(FILE *fout, struct lxc_conf *c) case PER_LINUX: fprintf(fout, "lxc.arch = x86_64\n"); break; default: break; } +#if HAVE_APPARMOR if (c->aa_profile) fprintf(fout, "lxc.aa_profile = %s\n", c->aa_profile); +#endif lxc_list_for_each(it, &c->cgroup) { struct lxc_cgroup *cg = it->elem; fprintf(fout, "lxc.cgroup.%s = %s\n", cg->subsystem, cg->value); From 0d2787be930588ac04854dcc2dd0ba85a2cbec48 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 10 Sep 2012 14:26:36 -0400 Subject: [PATCH 047/171] fix gcc error: typedef redefinition (against git staging) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix gcc error confile.c:83: error: redefinition of typedef ‘config_cb’. Its already defined the same way in confile.h. Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/confile.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lxc/confile.c b/src/lxc/confile.c index abe4cfa2a..3c548b1d3 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -80,8 +80,6 @@ static int config_seccomp(const char *, char *, struct lxc_conf *); static int config_includefile(const char *, char *, struct lxc_conf *); static int config_network_nic(const char *, char *, struct lxc_conf *); -typedef int (*config_cb)(const char *, char *, struct lxc_conf *); - static struct lxc_config_t config[] = { { "lxc.arch", config_personality }, From 5b12984bf9d1d952a402932d3d87aaef2d1ca6f2 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 11 Sep 2012 17:06:05 -0400 Subject: [PATCH 048/171] fix expansion of LXCPATH,LXCROOTFSMOUNT,LXCTEMPLATEDIR These variables are not expanded correctly in doc/lxc-create.sgml.in and a workaround is in place to ensure ${localstatedir}, and ${datadir} are set in the various shell scripts that use it. There is no workaround to ensure ${datadir} is set in src/lxc/lxc-create.in, nor is ${localstatedir} set in templates/lxc-altlinux.in so I think that these are currently broken. Using AS_AC_EXPAND instead of AC_SUBST fixes these problems and removes the need for the workarounds. In addition the lxc-start-ephemeral.in script can be autoconf'ed instead of sed'ed by the makefile. Signed-off-by: Dwight Engen --- configure.ac | 1 + src/lxc/Makefile.am | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 6f819d27a..6dbb7c22f 100644 --- a/configure.ac +++ b/configure.ac @@ -213,6 +213,7 @@ AC_CONFIG_FILES([ src/lxc/lxc-create src/lxc/lxc-clone src/lxc/lxc-shutdown + src/lxc/lxc-start-ephemeral src/lxc/lxc-destroy src/python-lxc/Makefile diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 11c025728..7d86ad661 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -144,10 +144,6 @@ lxc_unshare_SOURCES = lxc_unshare.c lxc_wait_SOURCES = lxc_wait.c lxc_kill_SOURCES = lxc_kill.c -lxc-start-ephemeral: lxc-start-ephemeral.in - [ -f $@ ] && rm -f $@ || true - $(SED) -e "s:[@]LXCPATH@:$(LXCPATH):" $< > $@ - install-exec-local: install-soPROGRAMS mv $(DESTDIR)$(libdir)/liblxc.so $(DESTDIR)$(libdir)/liblxc.so.$(VERSION) /sbin/ldconfig -l $(DESTDIR)$(libdir)/liblxc.so.$(VERSION) From 921ceb26bdb8ae6425f87789005e337f98aa4305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 12 Sep 2012 12:48:34 -0400 Subject: [PATCH 049/171] lxc-start-ephemeral: Fix typo causing crash at startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently a ")" was dropped in a recent change, causing lxc-start-ephemeral to fail to start completely (invalid syntax). Signed-off-by: Stéphane Graber --- src/lxc/lxc-start-ephemeral.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/lxc-start-ephemeral.in b/src/lxc/lxc-start-ephemeral.in index 71b186131..72b2114ba 100644 --- a/src/lxc/lxc-start-ephemeral.in +++ b/src/lxc/lxc-start-ephemeral.in @@ -218,7 +218,7 @@ if not args.keep_data: os.fchmod(fd.fileno(), 0o755) fd.write("""#!/bin/sh [ -d "%s" ] && rm -Rf "%s" -""" % (dest.get_config_item("lxc.rootfs"), dest.get_config_item("lxc.rootfs")) +""" % (dest.get_config_item("lxc.rootfs"), dest.get_config_item("lxc.rootfs"))) dest.set_config_item("lxc.hook.post-stop", os.path.join(dest_path, "post-stop")) From 225b52ef15d77d017c47b026313f3fabcf423a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 12 Sep 2012 13:12:50 -0400 Subject: [PATCH 050/171] lxc-start-ephemeral: Add missing return call to wait override MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When overriding wait(), I forgot to actually return the value coming from the C binding... Signed-off-by: Stéphane Graber Signed-off-by: Serge Hallyn --- src/python-lxc/lxc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python-lxc/lxc/__init__.py b/src/python-lxc/lxc/__init__.py index 6b671db5f..183c7789f 100644 --- a/src/python-lxc/lxc/__init__.py +++ b/src/python-lxc/lxc/__init__.py @@ -370,7 +370,7 @@ class Container(_lxc.Container): if isinstance(state, str): state = state.upper() - _lxc.Container.wait(self, state, timeout) + return _lxc.Container.wait(self, state, timeout) def list_containers(as_object=False): """ From 9737a2060ca725bb6622736f2bf2ebb6656a8d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 13 Sep 2012 10:03:21 -0400 Subject: [PATCH 051/171] lxc-start-ephemeral: Drop stop() calls when shutdown() returns non-True MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit shutdown() when given a timeout already does a stop call so there's no need to check its return value and do another one. Signed-off-by: Stéphane Graber --- src/lxc/lxc-start-ephemeral.in | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lxc/lxc-start-ephemeral.in b/src/lxc/lxc-start-ephemeral.in index 72b2114ba..70b1150da 100644 --- a/src/lxc/lxc-start-ephemeral.in +++ b/src/lxc/lxc-start-ephemeral.in @@ -251,8 +251,7 @@ The following IP addresses have be found in the container: sys.exit(0) else: dest.console(tty=1) - if not dest.shutdown(timeout=5): - dest.stop() + dest.shutdown(timeout=5) sys.exit(0) # Now deal with the case where we want to run a command in the container @@ -286,5 +285,4 @@ for ip in ips: break # Shutdown the container -if not dest.shutdown(timeout=5): - dest.stop() +dest.shutdown(timeout=5) From 6506255cfdab516114e35266c9d70b7ed3cd2bec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 13 Sep 2012 10:04:57 -0400 Subject: [PATCH 052/171] lxc-start-ephemeral: Exit with command return code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using lxc-start-ephemeral to directly call a command, return ssh's return code (the command's return code) when exiting. Signed-off-by: Stéphane Graber --- src/lxc/lxc-start-ephemeral.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lxc/lxc-start-ephemeral.in b/src/lxc/lxc-start-ephemeral.in index 70b1150da..85772563d 100644 --- a/src/lxc/lxc-start-ephemeral.in +++ b/src/lxc/lxc-start-ephemeral.in @@ -286,3 +286,5 @@ for ip in ips: # Shutdown the container dest.shutdown(timeout=5) + +sys.exit(retval) From abbe2ead95fdee6a0d59da35ce3542439ec2abcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 13 Sep 2012 10:08:39 -0400 Subject: [PATCH 053/171] lxc-start-ephemeral: startup time improvement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-organize the code to only call get_ips() when we actually need the IP address of the container. Also bump the timeout for get_ips() from 5s to 10s to accomodate slower machines. Signed-off-by: Stéphane Graber --- src/lxc/lxc-start-ephemeral.in | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/lxc/lxc-start-ephemeral.in b/src/lxc/lxc-start-ephemeral.in index 85772563d..2eb6157d9 100644 --- a/src/lxc/lxc-start-ephemeral.in +++ b/src/lxc/lxc-start-ephemeral.in @@ -233,26 +233,27 @@ if not dest.start() or not dest.wait("RUNNING", timeout=5): dest.destroy() sys.exit(1) -# Try to get the IP addresses -ips = dest.get_ips(timeout=5) +# Deal with the case where we just attach to the container's console +if not args.command and not args.daemon: + dest.console(tty=1) + dest.shutdown(timeout=5) + sys.exit(0) -# Deal with the case where we don't start a command in the container -if not args.command: - if args.daemon: - print(_("""The ephemeral container is now started. +# Try to get the IP addresses +ips = dest.get_ips(timeout=10) + +# Deal with the case where we just print info about the container +if args.daemon: + print(_("""The ephemeral container is now started. You can enter it from the command line with: lxc-console -n %s The following IP addresses have be found in the container: %s""") % ( - dest.name, - "\n".join([" - %s" % entry for entry in ips] - or [" - %s" % _("No address could be found")]) - )) - sys.exit(0) - else: - dest.console(tty=1) - dest.shutdown(timeout=5) - sys.exit(0) + dest.name, + "\n".join([" - %s" % entry for entry in ips] + or [" - %s" % _("No address could be found")]) + )) + sys.exit(0) # Now deal with the case where we want to run a command in the container if not ips: From 5a339bbb87e06cb4687056e07985eeb6bafef002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 13 Sep 2012 10:31:24 -0400 Subject: [PATCH 054/171] api_test.py: Remove workarounds for API bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The script used to contain a workaround for back when create() wouldn't properly flush the config and reload it. As these issues have now been fixed, these workarounds can be removed. Signed-off-by: Stéphane Graber --- src/python-lxc/examples/api_test.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/python-lxc/examples/api_test.py b/src/python-lxc/examples/api_test.py index 1a9f2e785..14236c795 100644 --- a/src/python-lxc/examples/api_test.py +++ b/src/python-lxc/examples/api_test.py @@ -53,7 +53,6 @@ assert(container.state == "STOPPED") ## Create a rootfs print("Creating rootfs using '%s'" % LXC_TEMPLATE) container.create(LXC_TEMPLATE) -container.load_config() # FIXME: workaround for get_config_item segfault assert(container.defined == True) assert(container.name == CONTAINER_NAME @@ -74,7 +73,6 @@ assert(capdrop == container.get_config_item("lxc.cap.drop")) ## Test the networking print("Testing the networking") -container.network.remove(0) # FIXME: workaround for get_config_item segfault # A few basic checks of the current state assert("name" in container.get_keys("lxc.network.0")) @@ -142,8 +140,6 @@ assert(container.state == "STOPPED") print("Cloning the container") clone = lxc.Container(CLONE_NAME) clone.clone(container) -clone.load_config() # FIXME: workaround for get_config_item segfault -clone.network.remove(0) # FIXME: workaround for get_config_item segfault clone.start() clone.stop() clone.destroy() From f6144ed443995a7d5bdbae217c6ba3ef3e341d16 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Thu, 13 Sep 2012 09:41:01 -0500 Subject: [PATCH 055/171] api shutdown: don't c->stop() if already stopped. Signed-off-by: Serge Hallyn --- src/lxc/lxccontainer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index ea39710b3..37f5ed7b0 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -655,7 +655,7 @@ static bool lxcapi_shutdown(struct lxc_container *c, int timeout) return true; kill(pid, SIGPWR); retv = c->wait(c, "STOPPED", timeout); - if (timeout > 0) { + if (!retv && timeout > 0) { c->stop(c); retv = c->wait(c, "STOPPED", 0); // 0 means don't wait } From 17ed13a3bca0a809273daf535f38ee166b110188 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 14 Sep 2012 14:42:24 -0500 Subject: [PATCH 056/171] Support individual hook types in clear_config_item Without this patch, only clear_config_item("lxc.hook") works. Signed-off-by: Serge Hallyn --- src/lxc/conf.c | 25 +++++++++++++++++++------ src/lxc/conf.h | 2 +- src/lxc/confile.c | 4 ++-- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 0218cdac7..f3c2334bf 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2542,18 +2542,31 @@ int lxc_clear_mount_entries(struct lxc_conf *c) return 0; } -int lxc_clear_hooks(struct lxc_conf *c) +int lxc_clear_hooks(struct lxc_conf *c, char *key) { struct lxc_list *it; + bool all = false, done = false; + char *k = key + 9; int i; + if (strcmp(key, "lxc.hook") == 0) + all = true; + for (i=0; ihooks[i]) { - lxc_list_del(it); - free(it->elem); - free(it); + if (all || strcmp(k, lxchook_names[i]) == 0) { + lxc_list_for_each(it, &c->hooks[i]) { + lxc_list_del(it); + free(it->elem); + free(it); + } + done = true; } } + + if (!done) { + ERROR("Invalid hook key: %s", key); + return -1; + } return 0; } @@ -2572,7 +2585,7 @@ void lxc_conf_free(struct lxc_conf *conf) #endif lxc_clear_config_caps(conf); lxc_clear_cgroups(conf, "lxc.cgroup"); - lxc_clear_hooks(conf); + lxc_clear_hooks(conf, "lxc.hook"); lxc_clear_mount_entries(conf); free(conf); } diff --git a/src/lxc/conf.h b/src/lxc/conf.h index dcf79fe5c..dccc17690 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -262,7 +262,7 @@ extern int lxc_clear_nic(struct lxc_conf *c, char *key); extern int lxc_clear_config_caps(struct lxc_conf *c); extern int lxc_clear_cgroups(struct lxc_conf *c, char *key); extern int lxc_clear_mount_entries(struct lxc_conf *c); -extern int lxc_clear_hooks(struct lxc_conf *c); +extern int lxc_clear_hooks(struct lxc_conf *c, char *key); /* * Configure the container from inside diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 3c548b1d3..cf1c8913a 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -1568,8 +1568,8 @@ int lxc_clear_config_item(struct lxc_conf *c, char *key) return lxc_clear_cgroups(c, key); else if (strcmp(key, "lxc.mount.entries") == 0) return lxc_clear_mount_entries(c); - else if (strcmp(key, "lxc.hook") == 0) - return lxc_clear_hooks(c); + else if (strncmp(key, "lxc.hook", 8) == 0) + return lxc_clear_hooks(c, key); return -1; } From 037ba55cbee97bb9e1be95423c358ac1a7b33a2a Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 26 Sep 2012 12:59:24 -0400 Subject: [PATCH 057/171] fix minor spelling error Signed-off-by: Dwight Engen --- doc/lxc-attach.sgml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lxc-attach.sgml.in b/doc/lxc-attach.sgml.in index a9a29021b..34e902743 100644 --- a/doc/lxc-attach.sgml.in +++ b/doc/lxc-attach.sgml.in @@ -133,7 +133,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Specify the namespaces to attach to, as a pipe-separated liste, + Specify the namespaces to attach to, as a pipe-separated list, e.g. NETWORK|IPC. Allowed values are MOUNT, PID, UTSNAME, IPC, From 708f4a80ea464edd3805f2024d65a2e795265080 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 5 Oct 2012 16:40:21 -0500 Subject: [PATCH 058/171] add oracle template (From: Dwight Engen) This is a new template to create containers based on Oracle Linux. A version such as 5.8, 6.3, or 6.latest can be specified with -R in which case a rootfs will be created from rpms downloaded from the Oracle public-yum repo. Alternatively the path to an existing rootfs of Oracle 5 or 6 may be given to the template with the -t option. The architecture of the downloaded rpms installed in the container can be specified with the -a template option. Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- configure.ac | 1 + templates/Makefile.am | 1 + templates/lxc-oracle.in | 496 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 498 insertions(+) create mode 100644 templates/lxc-oracle.in diff --git a/configure.ac b/configure.ac index 6dbb7c22f..ae04fe2fc 100644 --- a/configure.ac +++ b/configure.ac @@ -197,6 +197,7 @@ AC_CONFIG_FILES([ templates/lxc-opensuse templates/lxc-busybox templates/lxc-fedora + templates/lxc-oracle templates/lxc-altlinux templates/lxc-sshd templates/lxc-archlinux diff --git a/templates/Makefile.am b/templates/Makefile.am index d6b389289..523498dba 100644 --- a/templates/Makefile.am +++ b/templates/Makefile.am @@ -7,6 +7,7 @@ templates_SCRIPTS = \ lxc-ubuntu-cloud \ lxc-opensuse \ lxc-fedora \ + lxc-oracle \ lxc-altlinux \ lxc-busybox \ lxc-sshd \ diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in new file mode 100644 index 000000000..fc7324957 --- /dev/null +++ b/templates/lxc-oracle.in @@ -0,0 +1,496 @@ +#!/bin/bash +# +# Template script for generating Oracle Enterprise Linux container for LXC +# based on lxc-fedora, lxc-ubuntu +# +# Copyright © 2011 Wim Coekaerts +# Copyright © 2012 Dwight Engen +# +# Modified for Oracle Linux 5 +# Wim Coekaerts +# +# Modified for Oracle Linux 6, combined OL5,6 into one template script +# Dwight Engen +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# use virbr0 that is setup by default by libvirtd +lxc_network_type=veth +lxc_network_link=virbr0 + +die() +{ + echo "failed: $1" + exit 1 +} + +is_btrfs_subvolume() +{ + if which btrfs >/dev/null 2>&1 && \ + btrfs subvolume list "$1" >/dev/null 2>&1; then + return 0 + fi + return 1 +} + +# fix up the container_rootfs +container_rootfs_configure() +{ + echo "Configuring container for Oracle Linux $container_release_major" + + # "disable" selinux. init in OL 5 honors /etc/selinux/config. note that + # this doesnt actually disable it if it's enabled in the host, since + # libselinux::is_selinux_enabled() in the guest will check + # /proc/filesystems and see selinuxfs, thus reporting that it is on + # (ie. check the output of sestatus in the guest) + mkdir -p $container_rootfs/selinux + echo 0 > $container_rootfs/selinux/enforce + if [ -e $container_rootfs/etc/selinux/config ]; then + sed -i 's|SELINUX=enforcing|SELINUX=disabled|' $container_rootfs/etc/selinux/config + else + echo "SELINUX=disabled" >$container_rootfs/etc/selinux/config + fi + if [ $container_release_major = "5" ]; then + sed -i 's|session[ ]*required[ ]*pam_selinux.so[ ]*close|#session required pam_selinux.so close|' $container_rootfs/etc/pam.d/login + sed -i 's|session[ ]*required[ ]*pam_selinux.so[ ]*open|#session required pam_selinux.so open|' $container_rootfs/etc/pam.d/login + fi + + # configure the network to use dhcp. we set DHCP_HOSTNAME so the guest + # will report its name and be resolv'able by the hosts dnsmasq + touch $container_rootfs/etc/resolv.conf + cat < $container_rootfs/etc/sysconfig/network-scripts/ifcfg-eth0 +DEVICE=eth0 +BOOTPROTO=dhcp +ONBOOT=yes +HOSTNAME=$name +DHCP_HOSTNAME=$name +NM_CONTROLLED=no +TYPE=Ethernet +EOF + + # set the hostname + cat < $container_rootfs/etc/sysconfig/network +NETWORKING=yes +NETWORKING_IPV6=no +HOSTNAME=$name +EOF + + # set minimal hosts + echo "127.0.0.1 localhost $name" > $container_rootfs/etc/hosts + + # disable ipv6 + echo "blacklist ipv6" >>$container_rootfs/etc/modprobe.d/blacklist.conf + echo "blacklist net-pf-10" >>$container_rootfs/etc/modprobe.d/blacklist.conf + rm $container_rootfs/etc/sysconfig/network-scripts/init.ipv6-global + + cat < $container_rootfs/etc/fstab +proc /proc proc nodev,noexec,nosuid 0 0 +devpts /dev/pts devpts defaults 0 0 +sysfs /sys sysfs defaults 0 0 +EOF + + # remove module stuff for iptables it just shows errors that are not + # relevant in a container + if [ -f "$container_rootfs/etc/sysconfig/iptables-config" ]; then + sed -i 's|IPTABLES_MODULES=".*|IPTABLES_MODULES=""|' $container_rootfs/etc/sysconfig/iptables-config + sed -i 's|IPTABLES_MODULES_UNLOAD=".*|IPTABLES_MODULES_UNLOAD="no"|' $container_rootfs/etc/sysconfig/iptables-config + fi + + # disable readahead in the container + if [ $container_release_major = "6" -a -e $container_rootfs/etc/sysconfig/readahead ]; then + rm -f $container_root/etc/init/readahead-collector.conf + rm -f $container_root/etc/init/readahead-disable-services.conf + sed -i 's|READAHEAD="yes"|READAHEAD="no"|' $container_rootfs/etc/sysconfig/readahead + fi + + # disable udev in the container + sed -i 's|.sbin.start_udev||' $container_rootfs/etc/rc.sysinit + sed -i 's|.sbin.start_udev||' $container_rootfs/etc/rc.d/rc.sysinit + + # disable nash raidautorun in the container since no /dev/md* + if [ $container_release_major = "5" ]; then + sed -i 's|echo "raidautorun /dev/md0"|echo ""|' $container_rootfs/etc/rc.sysinit + sed -i 's|echo "raidautorun /dev/md0"|echo ""|' $container_rootfs/etc/rc.d/rc.sysinit + fi + + # prevent rc.sysinit from attempting to loadkeys + if [ $container_release_major = "5" -a -e $container_rootfs/etc/sysconfig/keyboard ]; then + rm $container_rootfs/etc/sysconfig/keyboard + fi + + # dont try to sync the hwclock at shutdown + sed -i 's|\[ -x /sbin/hwclock|\[ 0 -eq 1|' $container_rootfs/etc/rc.d/init.d/halt + + # dont start lvm + sed -i 's|action $"Setting up Logical Volume Management:"|#action $"Setting up Logical Volume Management:"|' $container_rootfs/etc/rc.sysinit + sed -i 's|action $"Setting up Logical Volume Management:"|/bin/true #action $"Setting up Logical Volume Management:"|' $container_rootfs/etc/rc.d/rc.sysinit + + # fix assumptions that plymouth is available + sed -i 's|\[ "$PROMPT" != no \] && plymouth|[ "$PROMPT" != no ] \&\& [ -n "$PLYMOUTH" ] \&\& plymouth|' $container_rootfs/etc/rc.sysinit + sed -i 's|\[ "$PROMPT" != no \] && plymouth|[ "$PROMPT" != no ] \&\& [ -n "$PLYMOUTH" ] \&\& plymouth|' $container_rootfs/etc/rc.d/rc.sysinit + rm -f $container_root/etc/init/plymouth-shutdown.conf + rm -f $container_root/etc/init/quit-plymouth.conf + rm -f $container_root/etc/init/splash-manager.conf + + # setup console and tty[1-4] for login. note that /dev/console and + # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and + # /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks. + # lxc will maintain these links and bind mount ptys over /dev/lxc/* + # since lxc.devttydir is specified in the config. + + # allow root login on console and tty[1-4] + echo "# LXC (Linux Containers)" >>$container_rootfs/etc/securetty + echo "lxc/console" >>$container_rootfs/etc/securetty + echo "lxc/tty1" >>$container_rootfs/etc/securetty + echo "lxc/tty2" >>$container_rootfs/etc/securetty + echo "lxc/tty3" >>$container_rootfs/etc/securetty + echo "lxc/tty4" >>$container_rootfs/etc/securetty + + # dont try to unmount /dev/lxc devices + sed -i 's|&& $1 !~ /^\\/dev\\/ram/|\&\& $2 !~ /^\\/dev\\/lxc/ \&\& $1 !~ /^\\/dev\\/ram/|' $container_rootfs/etc/init.d/halt + + # start a getty on /dev/console, /dev/tty[1-4] + if [ $container_release_major = "5" ]; then + sed -i '/1:2345:respawn/i cns:2345:respawn:/sbin/mingetty console' $container_rootfs/etc/inittab + sed -i '/5:2345:respawn/d' $container_rootfs/etc/inittab + sed -i '/6:2345:respawn/d' $container_rootfs/etc/inittab + fi + + if [ $container_release_major = "6" ]; then + cat < $container_rootfs/etc/init/console.conf +# console - getty +# +# This service maintains a getty on the console from the point the system is +# started until it is shut down again. + +start on stopped rc RUNLEVEL=[2345] +stop on runlevel [!2345] + +respawn +exec /sbin/mingetty /dev/console +EOF + fi + + # there might be other services that are useless but the below set is a good start + # some of these might not exist in the image, so we silence chkconfig complaining + # about the service file not being found + for service in \ + acpid auditd autofs cpuspeed dund gpm haldaemon hidd \ + ip6tables irqbalance iscsi iscsid isdn kdump kudzu \ + lm_sensors lvm2-monitor mdmonitor microcode_ctl \ + ntpd postfix sendmail udev-post ; + do + chroot $container_rootfs chkconfig 2>/dev/null $service off + done + + for service in rsyslog ; + do + chroot $container_rootfs chkconfig 2>/dev/null $service on + done + + # create required devices + # take care to not nuke /dev in case $container_rootfs isn't set + dev_path="$container_rootfs/dev" + if [ $container_rootfs != "/" -a -d $dev_path ]; then + rm -rf $dev_path + mkdir -p $dev_path + fi + mknod -m 666 $dev_path/null c 1 3 + mknod -m 666 $dev_path/zero c 1 5 + mknod -m 666 $dev_path/random c 1 8 + mknod -m 666 $dev_path/urandom c 1 9 + mkdir -m 755 $dev_path/pts + mkdir -m 1777 $dev_path/shm + mknod -m 666 $dev_path/tty c 5 0 + mknod -m 666 $dev_path/tty0 c 4 0 + mknod -m 666 $dev_path/tty1 c 4 1 + mknod -m 666 $dev_path/tty2 c 4 2 + mknod -m 666 $dev_path/tty3 c 4 3 + mknod -m 666 $dev_path/tty4 c 4 4 + mknod -m 600 $dev_path/console c 5 1 + mknod -m 666 $dev_path/full c 1 7 + mknod -m 600 $dev_path/initctl p + + # ensure /dev/ptmx refers to the newinstance devpts of the container, or + # pty's will get crossed up with the hosts (https://lkml.org/lkml/2012/1/23/512) + rm -f $container_rootfs/dev/ptmx + ln -s pts/ptmx $container_rootfs/dev/ptmx + + # start with a clean /var/log/messages + rm -f $container_rootfs/var/log/messages + + # add oracle user, set root password + chroot $container_rootfs useradd --create-home -s /bin/bash oracle + echo "oracle:oracle" | chroot $container_rootfs chpasswd + echo "root:root" | chroot $container_rootfs chpasswd + echo -e "Added container user:\033[1moracle\033[0m password:\033[1moracle\033[0m" + echo -e "Added container user:\033[1mroot\033[0m password:\033[1mroot\033[0m" +} + +# create the container's lxc config file +container_config_create() +{ + echo "Create configuration file $cfg_dir/config" + # generate a hwaddr for the container with a high mac address + # see http://sourceforge.net/tracker/?func=detail&aid=3411497&group_id=163076&atid=826303 + local hwaddr="fe:`dd if=/dev/urandom bs=8 count=1 2>/dev/null |od -t x8 | \ + head -1 |awk '{print $2}' | cut -c1-10 |\ + sed 's/\(..\)/\1:/g; s/.$//'`" + mkdir -p $cfg_dir || die "unable to create config dir $cfg_dir" + rm -f $cfg_dir/config + cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" +# Container configuration for Oracle Linux $release_major.$release_minor +lxc.arch = $arch +lxc.utsname = $name +lxc.devttydir = lxc +lxc.tty = 4 +lxc.pts = 1024 +lxc.rootfs = $container_rootfs +lxc.mount = $cfg_dir/fstab +# Networking +lxc.network.type = $lxc_network_type +lxc.network.flags = up +lxc.network.link = $lxc_network_link +lxc.network.name = eth0 +lxc.network.mtu = 1500 +lxc.network.hwaddr = $hwaddr +# Control Group devices: all denied except those whitelisted +lxc.cgroup.devices.deny = a +lxc.cgroup.devices.allow = c 1:3 rwm # /dev/null +lxc.cgroup.devices.allow = c 1:5 rwm # /dev/zero +lxc.cgroup.devices.allow = c 1:7 rwm # /dev/full +lxc.cgroup.devices.allow = c 5:0 rwm # /dev/tty +lxc.cgroup.devices.allow = c 1:8 rwm # /dev/random +lxc.cgroup.devices.allow = c 1:9 rwm # /dev/urandom +lxc.cgroup.devices.allow = c 136:* rwm # /dev/tty[1-4] ptys and lxc console +lxc.cgroup.devices.allow = c 5:2 rwm # /dev/ptmx pty master +lxc.cgroup.devices.allow = c 254:0 rwm # /dev/rtc0 +EOF + + cat < $cfg_dir/fstab || die "unable to create $cfg_dir/fstab" +proc $container_rootfs/proc proc nodev,noexec,nosuid 0 0 +devpts $container_rootfs/dev/pts devpts defaults 0 0 +sysfs $container_rootfs/sys sysfs defaults 0 0 +EOF +} + +container_rootfs_clone() +{ + if is_btrfs_subvolume $template_rootfs; then + # lxc-create already made $container_rootfs a btrfs subvolume, but + # in this case we want to snapshot the original subvolume so we we + # have to delete the one that lxc-create made + btrfs subvolume delete $container_rootfs + btrfs subvolume snapshot $template_rootfs $container_rootfs || die "btrfs clone template" + else + cp -ax $template_rootfs $container_rootfs || die "copy template" + fi +} + +container_rootfs_create() +{ + cmds="rpm wget yum" + if [ $release_major = "5" ]; then + cmds="$cmds db_dump db43_load" + fi + for cmd in $cmds; do + which $cmd >/dev/null 2>&1 + if [ $? -ne 0 ]; then + die "The $cmd command is required, please install it" + fi + done + + mkdir -p /var/lock/subsys/ + ( + flock -x 200 + if [ $? -ne 0 ]; then + die "The template is busy." + fi + + echo "Downloading release $release_major.$release_minor for $basearch" + + # get yum repo file + public_yum_url=http://public-yum.oracle.com + if [ $release_major = "5" ]; then + repofile=public-yum-el5.repo + elif [ $release_major = "6" ]; then + repofile=public-yum-ol6.repo + else + die "Unsupported release $release_major" + fi + mkdir -p $container_rootfs/etc/yum.repos.d + wget -q $public_yum_url/$repofile -O $container_rootfs/etc/yum.repos.d/$repofile + if [ $? -ne 0 ]; then + die "Failed to download repo file $public_yum_url/$repofile" + fi + + # yum will take $basearch from host, so force the arch we want + sed -i "s|\$basearch|$basearch|" $container_rootfs/etc/yum.repos.d/$repofile + + # replace url if they specified one + if [ -n "$repourl" ]; then + sed -i "s|baseurl=http://public-yum.oracle.com/repo|baseurl=$repourl/repo|" $container_rootfs/etc/yum.repos.d/$repofile + sed -i "s|gpgkey=http://public-yum.oracle.com|gpgkey=$repourl|" $container_rootfs/etc/yum.repos.d/$repofile + fi + + # disable all repos, then enable the repo for the version we are installing. + if [ $release_minor = "latest" ]; then + if [ $release_major = "5" ]; then + repo="el"$release_major"_"$release_minor + else + repo="ol"$release_major"_"$release_minor + fi + elif [ $release_minor = "0" ]; then + repo="ol"$release_major"_ga_base" + else + repo="ol"$release_major"_u"$release_minor"_base" + fi + sed -i "s|enabled=1|enabled=0|" $container_rootfs/etc/yum.repos.d/$repofile + sed -i "/\[$repo\]/,/\[/ s/enabled=0/enabled=1/" $container_rootfs/etc/yum.repos.d/$repofile + + # create rpm db, download and yum install minimal packages + mkdir -p $container_rootfs/var/lib/rpm + rpm --root $container_rootfs --initdb + yum_cmd="yum --installroot $container_rootfs --disablerepo=* --enablerepo=$repo -y --nogpgcheck" + min_pkgs="yum initscripts passwd rsyslog vim-minimal openssh-server dhclient chkconfig rootfiles policycoreutils oraclelinux-release" + + $yum_cmd install $min_pkgs + if [ $? -ne 0 ]; then + die "Failed to download and install the rootfs, aborting." + fi + + # rsyslog and pam depend on coreutils for some common commands in + # their POSTIN scriptlets, but coreutils wasn't installed yet. now + # that coreutils is installed, reinstall the packages so their POSTIN + # runs right. similarly, libutempter depends on libselinux.so.1 when + # it runs /usr/sbin/groupadd, so reinstall it too + if [ $release_major = "5" ]; then + rpm --root $container_rootfs --nodeps -e rsyslog pam libutempter + $yum_cmd install rsyslog pam libutempter + if [ $? -ne 0 ]; then + die "Unable to reinstall packages" + fi + fi + + # if we're on 6 and installing 5 we need to fix up the rpm database + # since 5 uses an older db format + if [ $release_major = "5" -a $host_release_major = "6" ]; then + echo "Fixing (downgrading) rpm database" + rm -f $container_rootfs/var/lib/rpm/__db* + for db in $container_rootfs/var/lib/rpm/* ; do + db_dump $db |db43_load $db.new + mv $db.new $db + done + chroot $container_rootfs rpm --rebuilddb + fi + + ) 200>/var/lock/subsys/lxc-oracle-$name +} + +usage() +{ + cat < architecture (ie. i686, x86_64) + -R|--release= release to download for the new container + -u|--url= replace yum repo url (ie. local yum mirror) + -t|--templatefs= copy/clone rootfs at path instead of downloading + -h|--help + +Release is of the format "major.minor", for example "5.8", "6.3", or "6.latest" +EOF + return 0 +} + +options=$(getopt -o hp:n:a:R:u:t: -l help,path:,name:,arch:,release:,url:,templatefs: -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi + +arch=$(arch) +eval set -- "$options" +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + -p|--path) cfg_dir=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -a|--arch) arch=$2; shift 2;; + -R|--release) release_version=$2; shift 2;; + -u|--url) repourl=$2; shift;; + -t|--templatefs) template_rootfs=$2; shift 2;; + --) shift 1; break ;; + *) break ;; + esac +done + +# make sure mandatory args are given and valid +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +if [ -z $name ]; then + echo "Container name must be given" + usage + exit 1 +fi + +if [ -z $cfg_dir ]; then + echo "Configuration directory must be given, check lxc-create" + usage + exit 1 +fi + +basearch=$arch +if [ "$arch" = "i686" ]; then + basearch="i386" +fi + +container_rootfs="$cfg_dir/rootfs" + +if [ -n "$template_rootfs" ]; then + release_version=`cat $template_rootfs/etc/oracle-release |awk '/^Oracle/ {print $5}'` +fi +if [ -z "$release_version" ]; then + echo "No release specified with -R, defaulting to 6.3" + release_version="6.3" +fi +release_major=`echo $release_version |awk -F '.' '{print $1}'` +release_minor=`echo $release_version |awk -F '.' '{print $2}'` + +host_release_version=`cat /etc/oracle-release |awk '/^Oracle/ {print $5}'` +host_release_major=`echo $host_release_version |awk -F '.' '{print $1}'` +host_release_minor=`echo $host_release_version |awk -F '.' '{print $2}'` + +trap cleanup SIGHUP SIGINT SIGTERM + +container_config_create +if [ -n "$template_rootfs" ]; then + container_rootfs_clone +else + container_rootfs_create +fi + +container_release_version=`cat $container_rootfs/etc/oracle-release |awk '/^Oracle/ {print $5}'` +container_release_major=`echo $container_release_version |awk -F '.' '{print $1}'` +container_release_minor=`echo $container_release_version |awk -F '.' '{print $2}'` + +container_rootfs_configure + +echo "Container : $container_rootfs" +echo "Config : $cfg_dir/config" +echo "Network : eth0 ($lxc_network_type) on $lxc_network_link" From b90270a5a3767933cab04cb82b530bf76fc0bf10 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 15 Oct 2012 09:42:02 -0400 Subject: [PATCH 059/171] Better rpm database downgrade logic Use the file command to see if the rpm database version needs to be downgraded. Use the lsb_release command to determine the host system, which is then used to set the commands needed to do the conversion, and lets us move the rpm database to the correct location if the host rpm doesn't put it where the guest expects it to be. Signed-off-by: Dwight Engen --- templates/lxc-oracle.in | 43 ++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in index fc7324957..05fac59a8 100644 --- a/templates/lxc-oracle.in +++ b/templates/lxc-oracle.in @@ -304,7 +304,17 @@ container_rootfs_create() { cmds="rpm wget yum" if [ $release_major = "5" ]; then - cmds="$cmds db_dump db43_load" + if [ $host_distribution = "Ubuntu" ]; then + db_dump_cmd="db5.1_dump" + db_load_cmd="db4.3_load" + fi + if [ $host_distribution = "OracleServer" -o \ + $host_distribution = "Fedora" ]; then + db_dump_cmd="db_dump" + db_load_cmd="db43_load" + fi + + cmds="$cmds $db_dump_cmd $db_load_cmd file" fi for cmd in $cmds; do which $cmd >/dev/null 2>&1 @@ -385,13 +395,21 @@ container_rootfs_create() fi fi - # if we're on 6 and installing 5 we need to fix up the rpm database - # since 5 uses an older db format - if [ $release_major = "5" -a $host_release_major = "6" ]; then - echo "Fixing (downgrading) rpm database" + # these distributions put the rpm database in a place the guest is + # not expecting it, so move it + if [ $host_distribution = "Ubuntu" ]; then + mv $container_rootfs/root/.rpmdb/* $container_rootfs/var/lib/rpm + fi + + # if the native rpm created the db with Hash version 9, we need to + # downgrade it to Hash version 8 for use with OL5.x + db_version=`file $container_rootfs/var/lib/rpm/Packages | \ + grep -o 'version [0-9]*' |awk '{print $2}'` + if [ $release_major = "5" -a $db_version != "8" ]; then + echo "Fixing (downgrading) rpm database from version $db_version" rm -f $container_rootfs/var/lib/rpm/__db* for db in $container_rootfs/var/lib/rpm/* ; do - db_dump $db |db43_load $db.new + $db_dump_cmd $db |$db_load_cmd $db.new mv $db.new $db done chroot $container_rootfs rpm --rebuilddb @@ -472,9 +490,16 @@ fi release_major=`echo $release_version |awk -F '.' '{print $1}'` release_minor=`echo $release_version |awk -F '.' '{print $2}'` -host_release_version=`cat /etc/oracle-release |awk '/^Oracle/ {print $5}'` -host_release_major=`echo $host_release_version |awk -F '.' '{print $1}'` -host_release_minor=`echo $host_release_version |awk -F '.' '{print $2}'` +if which lsb_release >/dev/null 2>&1; then + host_distribution=`lsb_release --id |awk '{print $3}'` + host_release_version=`lsb_release --release |awk '{print $2}'` + host_release_major=`echo $host_release_version |awk -F '.' '{print $1}'` + host_release_minor=`echo $host_release_version |awk -F '.' '{print $2}'` +else + echo "Unable to determine host distribution, ensure lsb_release is installed" + exit 1 +fi +echo "Host is $host_distribution $host_release_version" trap cleanup SIGHUP SIGINT SIGTERM From b88d7bb629849748346c1faf394d30c4c740ddf9 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 15 Oct 2012 09:42:10 -0400 Subject: [PATCH 060/171] Always rebuild rpm database Always rebuild the rpm database with the guest's rpm so there are no db version mismatches when you boot the guest and run rpm or yum. Signed-off-by: Dwight Engen --- templates/lxc-oracle.in | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in index 05fac59a8..566815112 100644 --- a/templates/lxc-oracle.in +++ b/templates/lxc-oracle.in @@ -412,9 +412,13 @@ container_rootfs_create() $db_dump_cmd $db |$db_load_cmd $db.new mv $db.new $db done - chroot $container_rootfs rpm --rebuilddb fi + # the host rpm may not be the same as the guest, rebuild the db with + # the guest rpm version + echo "Rebuilding rpm database" + rm -f $container_rootfs/var/lib/rpm/__db* + chroot $container_rootfs rpm --rebuilddb >/dev/null 2>&1 ) 200>/var/lock/subsys/lxc-oracle-$name } From 7060353a087b66f37c0134eb485baf777e949973 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 15 Oct 2012 09:42:18 -0400 Subject: [PATCH 061/171] Fix removal of unneeded startup/shutdown scripts Signed-off-by: Dwight Engen --- templates/lxc-oracle.in | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in index 566815112..81a772264 100644 --- a/templates/lxc-oracle.in +++ b/templates/lxc-oracle.in @@ -94,7 +94,7 @@ EOF # disable ipv6 echo "blacklist ipv6" >>$container_rootfs/etc/modprobe.d/blacklist.conf echo "blacklist net-pf-10" >>$container_rootfs/etc/modprobe.d/blacklist.conf - rm $container_rootfs/etc/sysconfig/network-scripts/init.ipv6-global + rm -f $container_rootfs/etc/sysconfig/network-scripts/init.ipv6-global cat < $container_rootfs/etc/fstab proc /proc proc nodev,noexec,nosuid 0 0 @@ -111,8 +111,8 @@ EOF # disable readahead in the container if [ $container_release_major = "6" -a -e $container_rootfs/etc/sysconfig/readahead ]; then - rm -f $container_root/etc/init/readahead-collector.conf - rm -f $container_root/etc/init/readahead-disable-services.conf + rm -f $container_rootfs/etc/init/readahead-collector.conf + rm -f $container_rootfs/etc/init/readahead-disable-services.conf sed -i 's|READAHEAD="yes"|READAHEAD="no"|' $container_rootfs/etc/sysconfig/readahead fi @@ -141,9 +141,9 @@ EOF # fix assumptions that plymouth is available sed -i 's|\[ "$PROMPT" != no \] && plymouth|[ "$PROMPT" != no ] \&\& [ -n "$PLYMOUTH" ] \&\& plymouth|' $container_rootfs/etc/rc.sysinit sed -i 's|\[ "$PROMPT" != no \] && plymouth|[ "$PROMPT" != no ] \&\& [ -n "$PLYMOUTH" ] \&\& plymouth|' $container_rootfs/etc/rc.d/rc.sysinit - rm -f $container_root/etc/init/plymouth-shutdown.conf - rm -f $container_root/etc/init/quit-plymouth.conf - rm -f $container_root/etc/init/splash-manager.conf + rm -f $container_rootfs/etc/init/plymouth-shutdown.conf + rm -f $container_rootfs/etc/init/quit-plymouth.conf + rm -f $container_rootfs/etc/init/splash-manager.conf # setup console and tty[1-4] for login. note that /dev/console and # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and From b1dc05fde974bc50f70c58857fc630c829aa2a18 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 15 Oct 2012 09:42:26 -0400 Subject: [PATCH 062/171] Honor network type and link from lxc-create -f Make the oracle template honor the lxc.network.type and lxc.network.link configuration items if a "base" configuration file is passed to lxc-create. If no configuration file is passed and the host system is Oracle or Fedora, the template assumes a type of veth and the default name created by libvirt. Signed-off-by: Dwight Engen --- templates/lxc-oracle.in | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in index 81a772264..6e11d992c 100644 --- a/templates/lxc-oracle.in +++ b/templates/lxc-oracle.in @@ -250,7 +250,6 @@ container_config_create() head -1 |awk '{print $2}' | cut -c1-10 |\ sed 's/\(..\)/\1:/g; s/.$//'`" mkdir -p $cfg_dir || die "unable to create config dir $cfg_dir" - rm -f $cfg_dir/config cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" # Container configuration for Oracle Linux $release_major.$release_minor lxc.arch = $arch @@ -261,9 +260,19 @@ lxc.pts = 1024 lxc.rootfs = $container_rootfs lxc.mount = $cfg_dir/fstab # Networking -lxc.network.type = $lxc_network_type -lxc.network.flags = up -lxc.network.link = $lxc_network_link +EOF + + # see if the network settings were already specified + lxc_network_type=`grep '^lxc.network.type' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + if [ -z "$lxc_network_type" -a \ + $host_distribution = "OracleServer" -o \ + $host_distribution = "Fedora" ]; then + echo "lxc.network.type = veth" >>$cfg_dir/config + echo "lxc.network.flags = up" >>$cfg_dir/config + echo "lxc.network.link = virbr0" >>$cfg_dir/config + fi + + cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" lxc.network.name = eth0 lxc.network.mtu = 1500 lxc.network.hwaddr = $hwaddr From 6f75ba0bdab3ab1f29773740611bb0ce6f9ab99c Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 17 Oct 2012 13:28:27 -0400 Subject: [PATCH 063/171] Add distro config file /etc/lxc/lxc.conf This allows a distro to put the distro specific default network configuration (for example bridge device, link type), or other lxc configuration in the case that -f is not passed by the user to lxc-create, in which case lxc-create will use the distro conf file as the basis for the containers config. Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- Makefile.am | 2 +- config/Makefile.am | 9 +++++++++ config/lxc.conf.libvirt | 3 +++ config/lxc.conf.ubuntu | 3 +++ config/lxc.conf.unknown | 1 + configure.ac | 42 ++++++++++++++++++++++++++++++++++++++--- lxc.spec.in | 1 + src/lxc/lxc-create.in | 25 ++++++++++-------------- 8 files changed, 67 insertions(+), 19 deletions(-) create mode 100644 config/lxc.conf.libvirt create mode 100644 config/lxc.conf.ubuntu create mode 100644 config/lxc.conf.unknown diff --git a/Makefile.am b/Makefile.am index 05a03f499..f99ad1ca3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ ACLOCAL_AMFLAGS = -I config -SUBDIRS = src templates doc +SUBDIRS = config src templates doc DIST_SUBDIRS = config src templates doc EXTRA_DIST = autogen.sh lxc.spec CONTRIBUTING MAINTAINERS ChangeLog diff --git a/config/Makefile.am b/config/Makefile.am index 783ba738d..f9949a78c 100644 --- a/config/Makefile.am +++ b/config/Makefile.am @@ -1,2 +1,11 @@ +configdir = $(sysconfdir)/lxc +config_DATA = lxc.conf +conffile = @LXC_CONFFILE@ + +EXTRA_DIST = lxc.conf.ubuntu lxc.conf.libvirt lxc.conf.unknown + +lxc.conf: + cp $(conffile) $@ + distclean: @$(RM) -f compile config.guess config.sub depcomp install-sh ltmain.sh missing Makefile.in Makefile diff --git a/config/lxc.conf.libvirt b/config/lxc.conf.libvirt new file mode 100644 index 000000000..71286190e --- /dev/null +++ b/config/lxc.conf.libvirt @@ -0,0 +1,3 @@ +lxc.network.type=veth +lxc.network.link=virbr0 +lxc.network.flags=up diff --git a/config/lxc.conf.ubuntu b/config/lxc.conf.ubuntu new file mode 100644 index 000000000..d2ac1678a --- /dev/null +++ b/config/lxc.conf.ubuntu @@ -0,0 +1,3 @@ +lxc.network.type=veth +lxc.network.link=lxcbr0 +lxc.network.flags=up diff --git a/config/lxc.conf.unknown b/config/lxc.conf.unknown new file mode 100644 index 000000000..16fa9d662 --- /dev/null +++ b/config/lxc.conf.unknown @@ -0,0 +1 @@ +lxc.network.type=empty diff --git a/configure.ac b/configure.ac index ae04fe2fc..82effb3bd 100644 --- a/configure.ac +++ b/configure.ac @@ -12,10 +12,45 @@ AM_PROG_CC_C_O AC_GNU_SOURCE AC_CHECK_PROG(SETCAP, setcap, yes, no, $PATH$PATH_SEPARATOR/sbin) -if test -f /etc/debian_version; then - osname="debian" +AC_MSG_CHECKING([host distribution]) +AC_ARG_WITH(distro, AS_HELP_STRING([--with-distro=DISTRO], [Specify the Linux distribution to target: One of redhat, oracle, fedora, suse, gentoo, debian, arch, slackware, paldo, mandriva or pardus])) +if test "z$with_distro" = "z"; then + with_distro=`lsb_release -is` fi -AM_CONDITIONAL([HAVE_DEBIAN], [test x"$osname" == xdebian]) +if test "z$with_distro" = "z"; then + AC_CHECK_FILE(/etc/redhat-release,with_distro="redhat") + AC_CHECK_FILE(/etc/oracle-release,with_distro="oracle") + AC_CHECK_FILE(/etc/fedora-release,with_distro="fedora") + AC_CHECK_FILE(/etc/SuSE-release,with_distro="suse") + AC_CHECK_FILE(/etc/gentoo-release,with_distro="gentoo") + AC_CHECK_FILE(/etc/debian_version,with_distro="debian") + AC_CHECK_FILE(/etc/arch-release,with_distro="arch") + AC_CHECK_FILE(/etc/slackware-version,with_distro="slackware") + AC_CHECK_FILE(/etc/frugalware-release,with_distro="frugalware") + AC_CHECK_FILE(/etc/mandrakelinux-release, with_distro="mandriva") + AC_CHECK_FILE(/etc/mandriva-release,with_distro="mandriva") + AC_CHECK_FILE(/etc/pardus-release,with_distro="pardus") +fi +with_distro=`echo ${with_distro} | tr '[[:upper:]]' '[[:lower:]]'` + +if test "z$with_distro" = "z"; then + with_distro="unknown" +fi +case $with_distro in + ubuntu) + conffile=lxc.conf.ubuntu + ;; + redhat|fedora|oracle|oracleserver) + conffile=lxc.conf.libvirt + ;; + *) + echo -n "Linux distribution network config unknown, defaulting to lxc.network.type = empty" + conffile=lxc.conf.unknown + ;; +esac +AC_MSG_RESULT([$with_distro]) + +AM_CONDITIONAL([HAVE_DEBIAN], [test x"$with_distro" = "xdebian" -o x"$with_distro" = "xubuntu"]) AC_ARG_ENABLE([rpath], [AC_HELP_STRING([--disable-rpath], [do not set rpath in executables])], @@ -111,6 +146,7 @@ AC_ARG_WITH([rootfs-path], [lxc rootfs mount point] )], [], [with_rootfs_path=['${libdir}/lxc/rootfs']]) +AS_AC_EXPAND(LXC_CONFFILE, $conffile) AS_AC_EXPAND(LXC_GENERATE_DATE, "$(date)") AS_AC_EXPAND(LXCPATH, "${with_config_path}") AS_AC_EXPAND(LXCROOTFSMOUNT, "${with_rootfs_path}") diff --git a/lxc.spec.in b/lxc.spec.in index e55c69a56..414b51c2f 100644 --- a/lxc.spec.in +++ b/lxc.spec.in @@ -91,6 +91,7 @@ rm -rf %{buildroot} %{_mandir}/* %{_datadir}/doc/* %{_datadir}/lxc/* +%{_sysconfdir}/lxc/* %files libs %defattr(-,root,root) diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in index 79c67d627..949edbe2c 100644 --- a/src/lxc/lxc-create.in +++ b/src/lxc/lxc-create.in @@ -260,16 +260,18 @@ trap cleanup HUP INT TERM mkdir -p $lxc_path/$lxc_name if [ -z "$lxc_config" ]; then - touch $lxc_path/$lxc_name/config -else - if [ ! -r "$lxc_config" ]; then - echo "$(basename $0): '$lxc_config' configuration file not found" >&2 - exit 1 - fi - - cp $lxc_config $lxc_path/$lxc_name/config + lxc_config="@SYSCONFDIR@/lxc/lxc.conf" + echo + echo "$(basename $0): No config file specified, using the default config $lxc_config" fi +if [ ! -r "$lxc_config" ]; then + echo "$(basename $0): '$lxc_config' configuration file not found" >&2 + exit 1 +fi + +cp $lxc_config $lxc_path/$lxc_name/config + if [ -n "$custom_rootfs" ]; then if grep -q "lxc.rootfs" $lxc_path/$lxc_name/config ; then echo "configuration file already specifies a lxc.rootfs" @@ -295,13 +297,6 @@ if [ ! -z $lxc_template ]; then cleanup fi - if [ -z "$lxc_config" ]; then - echo "Note: Usually the template option is called with a configuration" - echo "file option too, mostly to configure the network." - echo "For more information look at lxc.conf (5)" - echo - fi - ${templatedir}/lxc-$lxc_template --path=$lxc_path/$lxc_name --name=$lxc_name $* if [ $? -ne 0 ]; then echo "$(basename $0): failed to execute template '$lxc_template'" >&2 From f0e592fc66bbfd4c8cf73f91536f326ccf483f22 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Thu, 18 Oct 2012 12:50:02 -0400 Subject: [PATCH 064/171] Add distro config file /etc/lxc/lxc.conf [PATCH] Clean the lxc.conf file Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- config/Makefile.am | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/Makefile.am b/config/Makefile.am index f9949a78c..22636d94e 100644 --- a/config/Makefile.am +++ b/config/Makefile.am @@ -7,5 +7,9 @@ EXTRA_DIST = lxc.conf.ubuntu lxc.conf.libvirt lxc.conf.unknown lxc.conf: cp $(conffile) $@ +clean: + @$(RM) -f lxc.conf + distclean: + @$(RM) -f lxc.conf @$(RM) -f compile config.guess config.sub depcomp install-sh ltmain.sh missing Makefile.in Makefile From e2611fd5e14681470a4f2b48723584bba5b461ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 19 Oct 2012 23:06:12 +0200 Subject: [PATCH 065/171] python-lxc: Add missing space between two help lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One character fix making the documentation readable in help() Signed-off-by: Stéphane Graber --- src/python-lxc/lxc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python-lxc/lxc.c b/src/python-lxc/lxc.c index 529278cd0..b489079b5 100644 --- a/src/python-lxc/lxc.c +++ b/src/python-lxc/lxc.c @@ -478,7 +478,7 @@ static PyMethodDef Container_methods[] = { {"start", (PyCFunction)Container_start, METH_VARARGS | METH_KEYWORDS, "start(useinit = False, cmd = (,)) -> boolean\n" "\n" - "Start the container, optionally using lxc-init and" + "Start the container, optionally using lxc-init and " "an alternate init command, then returns its return code." }, {"stop", (PyCFunction)Container_stop, METH_NOARGS, From 75d0acd48f46e63b3d6e835ffbf36a771ecd2a4a Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Sat, 20 Oct 2012 11:47:22 +0200 Subject: [PATCH 066/171] Update documentation to Docbook 4.5 The package 'docbook-tools' [1] required to format Docbook 3.0 into man pages has been obsoleted a long time ago and can no longer be downloaded from its former homepage. Recent versions of that package -- now called 'docbook2X' --, cannot deal with that old markup format anymore (and don't support the '-w all' command line switch either). To remedy these issues, all SGML files have been updated to Docbook 4.5 so that recent versions of docbook2man can process them. [1] http://sources.redhat.com/docbook-tools/ [2] http://docbook2x.sourceforge.net/ Signed-off-by: Peter Simons --- doc/Makefile.am | 12 ++++++------ doc/lxc-attach.sgml.in | 4 ++-- doc/lxc-cgroup.sgml.in | 2 +- doc/lxc-checkpoint.sgml.in | 2 +- doc/lxc-console.sgml.in | 2 +- doc/lxc-create.sgml.in | 7 ++++--- doc/lxc-destroy.sgml.in | 2 +- doc/lxc-execute.sgml.in | 2 +- doc/lxc-freeze.sgml.in | 2 +- doc/lxc-kill.sgml.in | 2 +- doc/lxc-ls.sgml.in | 2 +- doc/lxc-monitor.sgml.in | 2 +- doc/lxc-ps.sgml.in | 2 +- doc/lxc-restart.sgml.in | 2 +- doc/lxc-start.sgml.in | 2 +- doc/lxc-stop.sgml.in | 2 +- doc/lxc-unfreeze.sgml.in | 2 +- doc/lxc-wait.sgml.in | 2 +- doc/lxc.conf.sgml.in | 2 +- doc/lxc.sgml.in | 10 +++++----- 20 files changed, 33 insertions(+), 32 deletions(-) diff --git a/doc/Makefile.am b/doc/Makefile.am index b18c5ebab..87f248902 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -30,14 +30,14 @@ man_MANS = \ lxc.7 -%.1 : %.sgml - docbook2man -w all $< +%.1 : %.sgml + docbook2man $< -%.5 : %.sgml - docbook2man -w all $< +%.5 : %.sgml + docbook2man $< -%.7 : %.sgml - docbook2man -w all $< +%.7 : %.sgml + docbook2man $< lxc-%.sgml : common_options.sgml see_also.sgml diff --git a/doc/lxc-attach.sgml.in b/doc/lxc-attach.sgml.in index 34e902743..549c22fe1 100644 --- a/doc/lxc-attach.sgml.in +++ b/doc/lxc-attach.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - @@ -220,7 +220,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Nevertheless, it will succeed on an unpatched kernel of version 3.0 or higher if the option is used to restrict the - namespaces that the process is to be attached to to one or more of + namespaces that the process is to be attached to to one or more of NETWORK, IPC and UTSNAME. diff --git a/doc/lxc-cgroup.sgml.in b/doc/lxc-cgroup.sgml.in index 9d49aa8ed..b321bc3ac 100644 --- a/doc/lxc-cgroup.sgml.in +++ b/doc/lxc-cgroup.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-checkpoint.sgml.in b/doc/lxc-checkpoint.sgml.in index 9cf0c79b1..78a6c9d38 100644 --- a/doc/lxc-checkpoint.sgml.in +++ b/doc/lxc-checkpoint.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-console.sgml.in b/doc/lxc-console.sgml.in index 2afba86ea..3c4533af5 100644 --- a/doc/lxc-console.sgml.in +++ b/doc/lxc-console.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-create.sgml.in b/doc/lxc-create.sgml.in index f4f4e592f..99bdb7199 100644 --- a/doc/lxc-create.sgml.in +++ b/doc/lxc-create.sgml.in @@ -1,4 +1,4 @@ - - @@ -145,6 +145,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA filesystem) of size SIZE rather than the default, which is 1G. + @@ -179,7 +180,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA available containers on the system. - + diff --git a/doc/lxc-destroy.sgml.in b/doc/lxc-destroy.sgml.in index a51e484b6..6b612c258 100644 --- a/doc/lxc-destroy.sgml.in +++ b/doc/lxc-destroy.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-execute.sgml.in b/doc/lxc-execute.sgml.in index 6371751c6..0da2284b3 100644 --- a/doc/lxc-execute.sgml.in +++ b/doc/lxc-execute.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-freeze.sgml.in b/doc/lxc-freeze.sgml.in index 7b7be4976..2fd2577a0 100644 --- a/doc/lxc-freeze.sgml.in +++ b/doc/lxc-freeze.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-kill.sgml.in b/doc/lxc-kill.sgml.in index d1e3f69ad..f1f6839ea 100644 --- a/doc/lxc-kill.sgml.in +++ b/doc/lxc-kill.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-ls.sgml.in b/doc/lxc-ls.sgml.in index 5a32f1da6..f5f65732c 100644 --- a/doc/lxc-ls.sgml.in +++ b/doc/lxc-ls.sgml.in @@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - ]> diff --git a/doc/lxc-monitor.sgml.in b/doc/lxc-monitor.sgml.in index d3951ff2f..c982c37c6 100644 --- a/doc/lxc-monitor.sgml.in +++ b/doc/lxc-monitor.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-ps.sgml.in b/doc/lxc-ps.sgml.in index 401a17c30..6f9d60e2d 100644 --- a/doc/lxc-ps.sgml.in +++ b/doc/lxc-ps.sgml.in @@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - ]> diff --git a/doc/lxc-restart.sgml.in b/doc/lxc-restart.sgml.in index ffc0ad2aa..5285b8123 100644 --- a/doc/lxc-restart.sgml.in +++ b/doc/lxc-restart.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-start.sgml.in b/doc/lxc-start.sgml.in index 2b6778f23..af79bbc6e 100644 --- a/doc/lxc-start.sgml.in +++ b/doc/lxc-start.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-stop.sgml.in b/doc/lxc-stop.sgml.in index d16cab5c5..b04d01910 100644 --- a/doc/lxc-stop.sgml.in +++ b/doc/lxc-stop.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-unfreeze.sgml.in b/doc/lxc-unfreeze.sgml.in index c4c545e90..c4e71debb 100644 --- a/doc/lxc-unfreeze.sgml.in +++ b/doc/lxc-unfreeze.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-wait.sgml.in b/doc/lxc-wait.sgml.in index 61bff8bdf..11e9b7e17 100644 --- a/doc/lxc-wait.sgml.in +++ b/doc/lxc-wait.sgml.in @@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in index 1428f252c..6c3d7b2aa 100644 --- a/doc/lxc.conf.sgml.in +++ b/doc/lxc.conf.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - ]> diff --git a/doc/lxc.sgml.in b/doc/lxc.sgml.in index 1b30fedb6..d98ca219f 100644 --- a/doc/lxc.sgml.in +++ b/doc/lxc.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - ]> @@ -280,7 +280,7 @@ rootfs - + @@ -570,7 +570,7 @@ rootfs to the background. - + From e03243397debaf3a72b227930c334935ff9f8cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 24 Oct 2012 15:32:52 +0200 Subject: [PATCH 067/171] Convert remaining file to new docbook format --- doc/lxc-shutdown.sgml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lxc-shutdown.sgml.in b/doc/lxc-shutdown.sgml.in index f64f575b3..a4f3accfd 100644 --- a/doc/lxc-shutdown.sgml.in +++ b/doc/lxc-shutdown.sgml.in @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - From efa99b32264acd577e36d9367f035d6cbde606a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 24 Oct 2012 20:41:01 +0200 Subject: [PATCH 068/171] Revert "Convert remaining file to new docbook format" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 8587ac4b855b7b66931a37742c45cd2c8c624658. The changes made the branch to fail to build on Ubuntu/Debian, so reverting the commits and re-opening the pull request. Signed-off-by: Stéphane Graber --- doc/lxc-shutdown.sgml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lxc-shutdown.sgml.in b/doc/lxc-shutdown.sgml.in index a4f3accfd..f64f575b3 100644 --- a/doc/lxc-shutdown.sgml.in +++ b/doc/lxc-shutdown.sgml.in @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - From f282a2f4b8f1d44702eb746c8554408d802f402d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 24 Oct 2012 20:41:07 +0200 Subject: [PATCH 069/171] Revert "Update documentation to Docbook 4.5" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 9a84044bc97098821cce2721ea40a1368f17a091. The changes made the branch to fail to build on Ubuntu/Debian, so reverting the commits and re-opening the pull request. Signed-off-by: Stéphane Graber --- doc/Makefile.am | 12 ++++++------ doc/lxc-attach.sgml.in | 4 ++-- doc/lxc-cgroup.sgml.in | 2 +- doc/lxc-checkpoint.sgml.in | 2 +- doc/lxc-console.sgml.in | 2 +- doc/lxc-create.sgml.in | 7 +++---- doc/lxc-destroy.sgml.in | 2 +- doc/lxc-execute.sgml.in | 2 +- doc/lxc-freeze.sgml.in | 2 +- doc/lxc-kill.sgml.in | 2 +- doc/lxc-ls.sgml.in | 2 +- doc/lxc-monitor.sgml.in | 2 +- doc/lxc-ps.sgml.in | 2 +- doc/lxc-restart.sgml.in | 2 +- doc/lxc-start.sgml.in | 2 +- doc/lxc-stop.sgml.in | 2 +- doc/lxc-unfreeze.sgml.in | 2 +- doc/lxc-wait.sgml.in | 2 +- doc/lxc.conf.sgml.in | 2 +- doc/lxc.sgml.in | 10 +++++----- 20 files changed, 32 insertions(+), 33 deletions(-) diff --git a/doc/Makefile.am b/doc/Makefile.am index 87f248902..b18c5ebab 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -30,14 +30,14 @@ man_MANS = \ lxc.7 -%.1 : %.sgml - docbook2man $< +%.1 : %.sgml + docbook2man -w all $< -%.5 : %.sgml - docbook2man $< +%.5 : %.sgml + docbook2man -w all $< -%.7 : %.sgml - docbook2man $< +%.7 : %.sgml + docbook2man -w all $< lxc-%.sgml : common_options.sgml see_also.sgml diff --git a/doc/lxc-attach.sgml.in b/doc/lxc-attach.sgml.in index 549c22fe1..34e902743 100644 --- a/doc/lxc-attach.sgml.in +++ b/doc/lxc-attach.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - @@ -220,7 +220,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Nevertheless, it will succeed on an unpatched kernel of version 3.0 or higher if the option is used to restrict the - namespaces that the process is to be attached to to one or more of + namespaces that the process is to be attached to to one or more of NETWORK, IPC and UTSNAME. diff --git a/doc/lxc-cgroup.sgml.in b/doc/lxc-cgroup.sgml.in index b321bc3ac..9d49aa8ed 100644 --- a/doc/lxc-cgroup.sgml.in +++ b/doc/lxc-cgroup.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-checkpoint.sgml.in b/doc/lxc-checkpoint.sgml.in index 78a6c9d38..9cf0c79b1 100644 --- a/doc/lxc-checkpoint.sgml.in +++ b/doc/lxc-checkpoint.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-console.sgml.in b/doc/lxc-console.sgml.in index 3c4533af5..2afba86ea 100644 --- a/doc/lxc-console.sgml.in +++ b/doc/lxc-console.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-create.sgml.in b/doc/lxc-create.sgml.in index 99bdb7199..f4f4e592f 100644 --- a/doc/lxc-create.sgml.in +++ b/doc/lxc-create.sgml.in @@ -1,4 +1,4 @@ - - @@ -145,7 +145,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA filesystem) of size SIZE rather than the default, which is 1G. - @@ -180,7 +179,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA available containers on the system. - + diff --git a/doc/lxc-destroy.sgml.in b/doc/lxc-destroy.sgml.in index 6b612c258..a51e484b6 100644 --- a/doc/lxc-destroy.sgml.in +++ b/doc/lxc-destroy.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-execute.sgml.in b/doc/lxc-execute.sgml.in index 0da2284b3..6371751c6 100644 --- a/doc/lxc-execute.sgml.in +++ b/doc/lxc-execute.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-freeze.sgml.in b/doc/lxc-freeze.sgml.in index 2fd2577a0..7b7be4976 100644 --- a/doc/lxc-freeze.sgml.in +++ b/doc/lxc-freeze.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-kill.sgml.in b/doc/lxc-kill.sgml.in index f1f6839ea..d1e3f69ad 100644 --- a/doc/lxc-kill.sgml.in +++ b/doc/lxc-kill.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-ls.sgml.in b/doc/lxc-ls.sgml.in index f5f65732c..5a32f1da6 100644 --- a/doc/lxc-ls.sgml.in +++ b/doc/lxc-ls.sgml.in @@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - ]> diff --git a/doc/lxc-monitor.sgml.in b/doc/lxc-monitor.sgml.in index c982c37c6..d3951ff2f 100644 --- a/doc/lxc-monitor.sgml.in +++ b/doc/lxc-monitor.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-ps.sgml.in b/doc/lxc-ps.sgml.in index 6f9d60e2d..401a17c30 100644 --- a/doc/lxc-ps.sgml.in +++ b/doc/lxc-ps.sgml.in @@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - ]> diff --git a/doc/lxc-restart.sgml.in b/doc/lxc-restart.sgml.in index 5285b8123..ffc0ad2aa 100644 --- a/doc/lxc-restart.sgml.in +++ b/doc/lxc-restart.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-start.sgml.in b/doc/lxc-start.sgml.in index af79bbc6e..2b6778f23 100644 --- a/doc/lxc-start.sgml.in +++ b/doc/lxc-start.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-stop.sgml.in b/doc/lxc-stop.sgml.in index b04d01910..d16cab5c5 100644 --- a/doc/lxc-stop.sgml.in +++ b/doc/lxc-stop.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-unfreeze.sgml.in b/doc/lxc-unfreeze.sgml.in index c4e71debb..c4c545e90 100644 --- a/doc/lxc-unfreeze.sgml.in +++ b/doc/lxc-unfreeze.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-wait.sgml.in b/doc/lxc-wait.sgml.in index 11e9b7e17..61bff8bdf 100644 --- a/doc/lxc-wait.sgml.in +++ b/doc/lxc-wait.sgml.in @@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in index 6c3d7b2aa..1428f252c 100644 --- a/doc/lxc.conf.sgml.in +++ b/doc/lxc.conf.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - ]> diff --git a/doc/lxc.sgml.in b/doc/lxc.sgml.in index d98ca219f..1b30fedb6 100644 --- a/doc/lxc.sgml.in +++ b/doc/lxc.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - ]> @@ -280,7 +280,7 @@ rootfs - + @@ -570,7 +570,7 @@ rootfs to the background. - + From d76db55b6c54c52fee905ecbb8773cd8ae1eb499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 31 Oct 2012 08:20:52 +0100 Subject: [PATCH 070/171] lxc-start-ephemeral: Wipe the whole container on exit, not just the rootfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber Signed-off-by: Serge Hallyn --- src/lxc/lxc-start-ephemeral.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/lxc-start-ephemeral.in b/src/lxc/lxc-start-ephemeral.in index 2eb6157d9..336620944 100644 --- a/src/lxc/lxc-start-ephemeral.in +++ b/src/lxc/lxc-start-ephemeral.in @@ -218,7 +218,7 @@ if not args.keep_data: os.fchmod(fd.fileno(), 0o755) fd.write("""#!/bin/sh [ -d "%s" ] && rm -Rf "%s" -""" % (dest.get_config_item("lxc.rootfs"), dest.get_config_item("lxc.rootfs"))) +""" % (dest_path, dest_path)) dest.set_config_item("lxc.hook.post-stop", os.path.join(dest_path, "post-stop")) From 64c3c9020bb797cc902f3915c71971a4b1bd8502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Elio=20Petten=C3=B2?= Date: Sat, 10 Nov 2012 20:55:10 -0800 Subject: [PATCH 071/171] build: make sure to expand all variables that are substituted. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes lxc-sshd still referring to '${libdir}'. Signed-off-by: Diego Elio Pettenò Acked-by: Stéphane Graber --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 82effb3bd..bd8f8501b 100644 --- a/configure.ac +++ b/configure.ac @@ -152,7 +152,7 @@ AS_AC_EXPAND(LXCPATH, "${with_config_path}") AS_AC_EXPAND(LXCROOTFSMOUNT, "${with_rootfs_path}") AS_AC_EXPAND(LXCTEMPLATEDIR, ['${datadir}/lxc/templates']) -AC_SUBST(LXCINITDIR, ['${libexecdir}']) +AS_AC_EXPAND(LXCINITDIR, ['${libexecdir}']) AC_CHECK_HEADERS([linux/unistd.h linux/netlink.h linux/genetlink.h], [], From 434445ee1e9f22b55ce8994d78794ae33a6d8247 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Thu, 25 Oct 2012 13:46:34 -0400 Subject: [PATCH 072/171] Reinstate README file in rootfs directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change 5fd8314f removed the README file explaining why the rootfs directory has to exist. Doing so broke the build on Fedora 17 since this directory will not be created by make install and thus the spec file cannot find it. Signed-off-by: Dwight Engen Acked-by: Stéphane Graber --- doc/rootfs/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/rootfs/Makefile.am b/doc/rootfs/Makefile.am index c9bb45db1..44d24ed9e 100644 --- a/doc/rootfs/Makefile.am +++ b/doc/rootfs/Makefile.am @@ -1,3 +1,3 @@ READMEdir=@LXCROOTFSMOUNT@ -README_DATA= +README_DATA=README From ad563aea971ac9317acac180e42cd271698208fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 12 Nov 2012 14:32:39 -0500 Subject: [PATCH 073/171] Set automake flags and CFLAGS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set automake's flags to -Wall -Werror as well as the general CFLAGS to -Wall and -Werror when building using gcc. This should catch any regression on build warnings now that we are in a pretty clean state. Signed-off-by: Stéphane Graber --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index bd8f8501b..0cc82c842 100644 --- a/configure.ac +++ b/configure.ac @@ -6,7 +6,7 @@ AC_INIT([lxc], [0.8.0]) AC_CONFIG_SRCDIR([configure.ac]) AC_CONFIG_AUX_DIR([config]) AM_CONFIG_HEADER([src/config.h]) -AM_INIT_AUTOMAKE([-Wno-portability]) +AM_INIT_AUTOMAKE([-Wall -Werror -Wno-portability]) AC_CANONICAL_HOST AM_PROG_CC_C_O AC_GNU_SOURCE @@ -182,7 +182,7 @@ AC_PROG_GCC_TRADITIONAL AC_PROG_SED if test "x$GCC" = "xyes"; then - CFLAGS="$CFLAGS -Wall" + CFLAGS="$CFLAGS -Wall -Werror" fi AC_CONFIG_FILES([ From eeb6cb8305a213c29ee20e4a0c4d4c1418c88dd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 12 Nov 2012 14:33:55 -0500 Subject: [PATCH 074/171] Fix autogen failing because of clean/distclean overrides MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A previous patch added a clean/distclean target to config/Makefile.am. This conflicts with automake's own target. This change replaces those by a clean-local and distclean-local target. Signed-off-by: Stéphane Graber --- config/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/Makefile.am b/config/Makefile.am index 22636d94e..1611b7d77 100644 --- a/config/Makefile.am +++ b/config/Makefile.am @@ -7,9 +7,9 @@ EXTRA_DIST = lxc.conf.ubuntu lxc.conf.libvirt lxc.conf.unknown lxc.conf: cp $(conffile) $@ -clean: +clean-local: @$(RM) -f lxc.conf -distclean: +distclean-local: @$(RM) -f lxc.conf @$(RM) -f compile config.guess config.sub depcomp install-sh ltmain.sh missing Makefile.in Makefile From 5bf2c5ce9ba2539efbf81928ef6ccb96d959bcf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 12 Nov 2012 14:39:43 -0500 Subject: [PATCH 075/171] Fix check against LXCROOTFSMOUNT to use strcmp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The check for conf->rootfs.mount not being equal to LXCROOTFSMOUNT wasn't done with strcmp which was leading to undefined behaviour and triggered gcc warnings. Signed-off-by: Stéphane Graber --- src/lxc/conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index f3c2334bf..96f2cf792 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2576,7 +2576,7 @@ void lxc_conf_free(struct lxc_conf *conf) return; if (conf->console.path) free(conf->console.path); - if (conf->rootfs.mount != LXCROOTFSMOUNT) + if (strcmp(conf->rootfs.mount, LXCROOTFSMOUNT) != 0) free(conf->rootfs.mount); lxc_clear_config_network(conf); #if HAVE_APPARMOR From e54d6cce171d9b18e2ec553e4c30c17d4462332e Mon Sep 17 00:00:00 2001 From: Frank Scholten Date: Fri, 26 Oct 2012 19:22:26 +0200 Subject: [PATCH 076/171] Updated README and INSTALL. autogen.sh command should be run before configure. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Scholten Acked-by: Stéphane Graber --- INSTALL | 2 +- README | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/INSTALL b/INSTALL index 4f3847c37..6970e369b 100644 --- a/INSTALL +++ b/INSTALL @@ -10,7 +10,7 @@ unlimited permission to copy, distribute and modify it. Basic Installation ================== -Briefly, the shell commands `./configure; make; make install' should +Briefly, the shell commands `./autogen.sh; ./configure; make; make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. diff --git a/README b/README index 0cf024629..b81e7a923 100644 --- a/README +++ b/README @@ -7,7 +7,7 @@ What is lxc: kernel. It provides the resource management through the control groups aka process containers and resource isolation through the namespaces. - The linux containers, lxc, aims to use these new functionnalities to pro- + The linux containers, lxc, aims to use these new functionalities to pro- vide an userspace container object which provides full resource isolation and resource control for an applications or a system. @@ -31,7 +31,7 @@ Downloading the current source code: For detailed build instruction refer to INSTALL and man lxc man page but a short command line should work: - ./configure && make && sudo make install && sudo lxc-setcap + ./autogen.sh && ./configure && make && sudo make install && sudo lxc-setcap preceded by ./autogen.sh if configure do not exist yet. Getting help: From aa8d013ec5b09cd1cd904173d6234ef126eb2126 Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Sat, 20 Oct 2012 11:47:22 +0200 Subject: [PATCH 077/171] Update documentation to Docbook 4.5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The package 'docbook-tools' [1] required to format Docbook 3.0 into man pages has been obsoleted a long time ago and can no longer be downloaded from its former homepage. Recent versions of that package -- now called 'docbook2X' --, cannot deal with that old markup format anymore (and don't support the '-w all' command line switch either). To remedy these issues, all SGML files have been updated to Docbook 4.5 so that recent versions of docbook2man can process them. [1] http://sources.redhat.com/docbook-tools/ [2] http://docbook2x.sourceforge.net/ Signed-off-by: Peter Simons Acked-by: Stéphane Graber --- doc/Makefile.am | 12 ++++++------ doc/lxc-attach.sgml.in | 4 ++-- doc/lxc-cgroup.sgml.in | 2 +- doc/lxc-checkpoint.sgml.in | 2 +- doc/lxc-console.sgml.in | 2 +- doc/lxc-create.sgml.in | 7 ++++--- doc/lxc-destroy.sgml.in | 2 +- doc/lxc-execute.sgml.in | 2 +- doc/lxc-freeze.sgml.in | 2 +- doc/lxc-kill.sgml.in | 2 +- doc/lxc-ls.sgml.in | 2 +- doc/lxc-monitor.sgml.in | 2 +- doc/lxc-ps.sgml.in | 2 +- doc/lxc-restart.sgml.in | 2 +- doc/lxc-shutdown.sgml.in | 2 +- doc/lxc-start.sgml.in | 2 +- doc/lxc-stop.sgml.in | 2 +- doc/lxc-unfreeze.sgml.in | 2 +- doc/lxc-wait.sgml.in | 2 +- doc/lxc.conf.sgml.in | 2 +- doc/lxc.sgml.in | 10 +++++----- lxc.spec.in | 2 +- 22 files changed, 35 insertions(+), 34 deletions(-) diff --git a/doc/Makefile.am b/doc/Makefile.am index b18c5ebab..87f248902 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -30,14 +30,14 @@ man_MANS = \ lxc.7 -%.1 : %.sgml - docbook2man -w all $< +%.1 : %.sgml + docbook2man $< -%.5 : %.sgml - docbook2man -w all $< +%.5 : %.sgml + docbook2man $< -%.7 : %.sgml - docbook2man -w all $< +%.7 : %.sgml + docbook2man $< lxc-%.sgml : common_options.sgml see_also.sgml diff --git a/doc/lxc-attach.sgml.in b/doc/lxc-attach.sgml.in index 34e902743..549c22fe1 100644 --- a/doc/lxc-attach.sgml.in +++ b/doc/lxc-attach.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - @@ -220,7 +220,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Nevertheless, it will succeed on an unpatched kernel of version 3.0 or higher if the option is used to restrict the - namespaces that the process is to be attached to to one or more of + namespaces that the process is to be attached to to one or more of NETWORK, IPC and UTSNAME. diff --git a/doc/lxc-cgroup.sgml.in b/doc/lxc-cgroup.sgml.in index 9d49aa8ed..b321bc3ac 100644 --- a/doc/lxc-cgroup.sgml.in +++ b/doc/lxc-cgroup.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-checkpoint.sgml.in b/doc/lxc-checkpoint.sgml.in index 9cf0c79b1..78a6c9d38 100644 --- a/doc/lxc-checkpoint.sgml.in +++ b/doc/lxc-checkpoint.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-console.sgml.in b/doc/lxc-console.sgml.in index 2afba86ea..3c4533af5 100644 --- a/doc/lxc-console.sgml.in +++ b/doc/lxc-console.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-create.sgml.in b/doc/lxc-create.sgml.in index f4f4e592f..99bdb7199 100644 --- a/doc/lxc-create.sgml.in +++ b/doc/lxc-create.sgml.in @@ -1,4 +1,4 @@ - - @@ -145,6 +145,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA filesystem) of size SIZE rather than the default, which is 1G. + @@ -179,7 +180,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA available containers on the system. - + diff --git a/doc/lxc-destroy.sgml.in b/doc/lxc-destroy.sgml.in index a51e484b6..6b612c258 100644 --- a/doc/lxc-destroy.sgml.in +++ b/doc/lxc-destroy.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-execute.sgml.in b/doc/lxc-execute.sgml.in index 6371751c6..0da2284b3 100644 --- a/doc/lxc-execute.sgml.in +++ b/doc/lxc-execute.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-freeze.sgml.in b/doc/lxc-freeze.sgml.in index 7b7be4976..2fd2577a0 100644 --- a/doc/lxc-freeze.sgml.in +++ b/doc/lxc-freeze.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-kill.sgml.in b/doc/lxc-kill.sgml.in index d1e3f69ad..f1f6839ea 100644 --- a/doc/lxc-kill.sgml.in +++ b/doc/lxc-kill.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-ls.sgml.in b/doc/lxc-ls.sgml.in index 5a32f1da6..f5f65732c 100644 --- a/doc/lxc-ls.sgml.in +++ b/doc/lxc-ls.sgml.in @@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - ]> diff --git a/doc/lxc-monitor.sgml.in b/doc/lxc-monitor.sgml.in index d3951ff2f..c982c37c6 100644 --- a/doc/lxc-monitor.sgml.in +++ b/doc/lxc-monitor.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-ps.sgml.in b/doc/lxc-ps.sgml.in index 401a17c30..6f9d60e2d 100644 --- a/doc/lxc-ps.sgml.in +++ b/doc/lxc-ps.sgml.in @@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - ]> diff --git a/doc/lxc-restart.sgml.in b/doc/lxc-restart.sgml.in index ffc0ad2aa..5285b8123 100644 --- a/doc/lxc-restart.sgml.in +++ b/doc/lxc-restart.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-shutdown.sgml.in b/doc/lxc-shutdown.sgml.in index f64f575b3..a4f3accfd 100644 --- a/doc/lxc-shutdown.sgml.in +++ b/doc/lxc-shutdown.sgml.in @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-start.sgml.in b/doc/lxc-start.sgml.in index 2b6778f23..af79bbc6e 100644 --- a/doc/lxc-start.sgml.in +++ b/doc/lxc-start.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-stop.sgml.in b/doc/lxc-stop.sgml.in index d16cab5c5..b04d01910 100644 --- a/doc/lxc-stop.sgml.in +++ b/doc/lxc-stop.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-unfreeze.sgml.in b/doc/lxc-unfreeze.sgml.in index c4c545e90..c4e71debb 100644 --- a/doc/lxc-unfreeze.sgml.in +++ b/doc/lxc-unfreeze.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-wait.sgml.in b/doc/lxc-wait.sgml.in index 61bff8bdf..11e9b7e17 100644 --- a/doc/lxc-wait.sgml.in +++ b/doc/lxc-wait.sgml.in @@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in index 1428f252c..6c3d7b2aa 100644 --- a/doc/lxc.conf.sgml.in +++ b/doc/lxc.conf.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - ]> diff --git a/doc/lxc.sgml.in b/doc/lxc.sgml.in index 1b30fedb6..d98ca219f 100644 --- a/doc/lxc.sgml.in +++ b/doc/lxc.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - ]> @@ -280,7 +280,7 @@ rootfs - + @@ -570,7 +570,7 @@ rootfs to the background. - + diff --git a/lxc.spec.in b/lxc.spec.in index 414b51c2f..22b8b7db6 100644 --- a/lxc.spec.in +++ b/lxc.spec.in @@ -30,7 +30,7 @@ Group: Applications/System License: LGPL BuildRoot: %{_tmppath}/%{name}-%{version}-build Requires: libcap -BuildRequires: libcap libcap-devel docbook-utils +BuildRequires: libcap libcap-devel docbook2x %description From 7822022c4c72cee06905b540b89b653491d6f6b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 12 Nov 2012 15:38:50 -0500 Subject: [PATCH 078/171] Detect which name to use for docbook2x-man MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docbook2x-man doesn't have the same name on Debian based systems as on RedHat based systems, add some magic to configure.ac to detect and substitute the proper name in Makefile.am Signed-off-by: Stéphane Graber --- configure.ac | 26 +++++++++++++++++++++----- doc/Makefile.am | 6 +++--- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/configure.ac b/configure.ac index 0cc82c842..b6fa36537 100644 --- a/configure.ac +++ b/configure.ac @@ -59,14 +59,30 @@ AC_ARG_ENABLE([rpath], AM_CONDITIONAL([ENABLE_RPATH], [test "x$enable_rpath" = "xyes"]) AC_ARG_ENABLE([doc], - [AC_HELP_STRING([--enable-doc], [make mans (require docbook2man installed) [default=auto]])], + [AC_HELP_STRING([--enable-doc], [make mans (require docbook2x-man installed) [default=auto]])], [], [enable_doc=auto]) if test "x$enable_doc" = "xyes" -o "x$enable_doc" = "xauto"; then - AC_CHECK_PROG(have_docbook, [docbook2man], [yes], [no]) + db2xman="" - test "x$have_docbook" = "xno" -a "x$enable_doc" = "xyes" && \ - AC_MSG_ERROR([docbook2man required by man request, but not found]) + AC_MSG_CHECKING(for docbook2x-man) + for name in docbook2x-man db2x_docbook2man; do + if "$name" --help >/dev/null 2>&1; then + db2xman="$name" + break; + fi + done + + if test -n "${db2xman}"; then + AC_MSG_RESULT(${db2xman}) + else + AC_MSG_RESULT(no) + if test "x$enable_doc" = "xyes"; then + AC_MSG_ERROR([docbook2x-man required by man request, but not found]) + fi + fi + + AC_SUBST(db2xman) fi AC_ARG_ENABLE([apparmor], @@ -99,7 +115,7 @@ AM_COND_IF([ENABLE_SECCOMP], AC_CHECK_LIB([seccomp], [seccomp_init],[],[AC_MSG_ERROR([You must install the seccomp development package in order to compile lxc])]) AC_SUBST([SECCOMP_LIBS], [-lseccomp])]) -AM_CONDITIONAL([ENABLE_DOCBOOK], [test "x$have_docbook" = "xyes"]) +AM_CONDITIONAL([ENABLE_DOCBOOK], [test "x$db2xman" != "x"]) AC_ARG_ENABLE([examples], [AC_HELP_STRING([--disable-examples], [do not install configuration examples])], diff --git a/doc/Makefile.am b/doc/Makefile.am index 87f248902..16171866e 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -31,13 +31,13 @@ man_MANS = \ %.1 : %.sgml - docbook2man $< + $(db2xman) $< %.5 : %.sgml - docbook2man $< + $(db2xman) $< %.7 : %.sgml - docbook2man $< + $(db2xman) $< lxc-%.sgml : common_options.sgml see_also.sgml From 9b106331e79de6dc328b5545b4b188b527224dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 12 Nov 2012 15:56:41 -0500 Subject: [PATCH 079/171] Update .gitignore for current list of binaries and templates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- .gitignore | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 8c84a2303..bdbea4afb 100644 --- a/.gitignore +++ b/.gitignore @@ -24,28 +24,27 @@ libtool lxc.spec lxc.pc -templates/lxc-debian -templates/lxc-lucid -templates/lxc-maverick -templates/lxc-natty -templates/lxc-oneiric -templates/lxc-fedora templates/lxc-altlinux -templates/lxc-sshd -templates/lxc-busybox templates/lxc-archlinux +templates/lxc-busybox +templates/lxc-debian +templates/lxc-fedora +templates/lxc-lenny +templates/lxc-opensuse +templates/lxc-oracle +templates/lxc-sshd +templates/lxc-ubuntu +templates/lxc-ubuntu-cloud + src/lxc/lxc-attach src/lxc/lxc-cgroup src/lxc/lxc-checkconfig src/lxc/lxc-checkpoint -src/lxc/lxc-cinit -src/lxc/lxc-cmd +src/lxc/lxc-clone src/lxc/lxc-console src/lxc/lxc-create src/lxc/lxc-destroy -src/lxc/lxc-enter -src/lxc/lxc-exec src/lxc/lxc-execute src/lxc/lxc-freeze src/lxc/lxc-info @@ -54,10 +53,13 @@ src/lxc/lxc-kill src/lxc/lxc-ls src/lxc/lxc-monitor src/lxc/lxc-netstat -src/lxc/lxc-setcap src/lxc/lxc-ps src/lxc/lxc-restart +src/lxc/lxc-setcap +src/lxc/lxc-setuid +src/lxc/lxc-shutdown src/lxc/lxc-start +src/lxc/lxc-start-ephemeral src/lxc/lxc-stop src/lxc/lxc-unfreeze src/lxc/lxc-unshare From aeb958be832011009226b39e04d61cfb2a6e7da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 12 Nov 2012 16:20:53 -0500 Subject: [PATCH 080/171] lxc-ls: Don't exit 1 when no container or help MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lxc-ls is currently exiting with return code 1 when called with --help or when called on a system without containers. This behaviour isn't documented in the manpage and isn't terribly intuitive. It's been the source of quite a few weird failures in scripts running with set -e. As a user calling --help is a voluntary action, lxc-ls should exit 0. Also, as lxc-ls's goal is solely to list containers, showing an error and exiting with return code 1 when there's no container seems counter-intuitive and error-prone. Signed-off-by: Stéphane Graber --- src/lxc/lxc-ls.in | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/lxc/lxc-ls.in b/src/lxc/lxc-ls.in index 9293323c9..f26572da2 100644 --- a/src/lxc/lxc-ls.in +++ b/src/lxc/lxc-ls.in @@ -75,7 +75,7 @@ directory=$(readlink -f "$lxc_path") for i in "$@"; do case $i in --help) - help; exit 1;; + help; exit;; --active) get_parent_cgroup; directory="$parent_cgroup"; shift;; --) @@ -90,10 +90,5 @@ if [ ! -z "$directory" ]; then containers=$(find $directory -mindepth 1 -maxdepth 1 -type d -printf "%f\n" 2>/dev/null) fi -if [ -z "$containers" ]; then - echo "$(basename $0): no containers found" >&2 - exit 1 -fi - cd "$directory" ls -d $@ -- $containers From 24b292c917fe325e51e2ccd7dc3e4e05fee2d1e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 12 Nov 2012 16:41:14 -0500 Subject: [PATCH 081/171] lxc-create: Support passing a full path to -t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some cases it may be useful to pass a full path to an executable template script directly to lxc-create. Signed-off-by: Stéphane Graber --- doc/lxc-create.sgml.in | 2 ++ src/lxc/lxc-create.in | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/doc/lxc-create.sgml.in b/doc/lxc-create.sgml.in index 99bdb7199..bcf0545cb 100644 --- a/doc/lxc-create.sgml.in +++ b/doc/lxc-create.sgml.in @@ -113,6 +113,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA eg. busybox, debian, fedora, ubuntu or sshd. Refer to the examples in @LXCTEMPLATEDIR@ for details of the expected script structure. + Alternatively, the full path to an executable template script + can also be passed as a parameter. diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in index 949edbe2c..e3b50ab62 100644 --- a/src/lxc/lxc-create.in +++ b/src/lxc/lxc-create.in @@ -290,14 +290,20 @@ if [ $backingstore = "lvm" ]; then fi if [ ! -z $lxc_template ]; then + # Allow for a path to be provided as the template name + if [ -x $lxc_template ]; then + template_path=$lxc_template + else + template_path=${templatedir}/lxc-$lxc_template + fi - type ${templatedir}/lxc-$lxc_template 2>/dev/null + type $template_path 2>/dev/null if [ $? -ne 0 ]; then echo "$(basename $0): unknown template '$lxc_template'" >&2 cleanup fi - ${templatedir}/lxc-$lxc_template --path=$lxc_path/$lxc_name --name=$lxc_name $* + $template_path --path=$lxc_path/$lxc_name --name=$lxc_name $* if [ $? -ne 0 ]; then echo "$(basename $0): failed to execute template '$lxc_template'" >&2 cleanup From d7436fc1dfe0323fb5e6905457f342da5ed8ca97 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 13 Nov 2012 17:17:25 -0600 Subject: [PATCH 082/171] Fix reverse check: error out only if mkdir(cgroup/lxc) fails NOT due to -EEXIST Signed-off-by: Serge Hallyn --- src/lxc/cgroup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c index a02ebc2fa..532d63833 100644 --- a/src/lxc/cgroup.c +++ b/src/lxc/cgroup.c @@ -542,7 +542,7 @@ static int lxc_one_cgroup_create(const char *name, /* if cgparent does not exist, create it */ if (access(cgparent, F_OK)) { ret = mkdir(cgparent, 0755); - if (ret == -1 && errno == EEXIST) { + if (ret == -1 && errno != EEXIST) { SYSERROR("failed to create '%s' directory", cgparent); return -1; } From ca0a33644ae47b802ce2ae7a96af70806e7266fe Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 13 Nov 2012 17:19:13 -0600 Subject: [PATCH 083/171] lxc-ssh: fix message about ssh key insertion Signed-off-by: Serge Hallyn --- templates/lxc-sshd.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/lxc-sshd.in b/templates/lxc-sshd.in index 4f8d50407..7ba642d66 100644 --- a/templates/lxc-sshd.in +++ b/templates/lxc-sshd.in @@ -96,7 +96,7 @@ EOF cp $auth_key "$root_u_path/authorized_keys" chown -R 0:0 "$rootfs/$u_path" chmod 700 "$rootfs/$u_path" - echo "Inserted SSH public key from $auth_key into /home/ubuntu/.ssh/authorized_keys" + echo "Inserted SSH public key from $auth_key into $rootfs/$u_path" fi return 0 From 8dff643f44c4c179a3e2657f5641e59739dc4b2c Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 13 Nov 2012 17:49:13 -0600 Subject: [PATCH 084/171] Initialize curtime to silence spurious compiler warning Signed-off-by: Serge Hallyn --- src/lxc/state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/state.c b/src/lxc/state.c index 466dc0a04..be667399d 100644 --- a/src/lxc/state.c +++ b/src/lxc/state.c @@ -210,7 +210,7 @@ extern int lxc_wait(char *lxcname, char *states, int timeout) } for (;;) { - int elapsed_time, curtime; + int elapsed_time, curtime = 0; struct timeval tv; int stop = 0; int retval; From c95cf86f39ca327688781718ddc371b00f232c03 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 13 Nov 2012 17:50:35 -0600 Subject: [PATCH 085/171] Revert "Fix check against LXCROOTFSMOUNT to use strcmp" This reverts commit 5bf2c5ce9ba2539efbf81928ef6ccb96d959bcf8. --- src/lxc/conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 96f2cf792..f3c2334bf 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2576,7 +2576,7 @@ void lxc_conf_free(struct lxc_conf *conf) return; if (conf->console.path) free(conf->console.path); - if (strcmp(conf->rootfs.mount, LXCROOTFSMOUNT) != 0) + if (conf->rootfs.mount != LXCROOTFSMOUNT) free(conf->rootfs.mount); lxc_clear_config_network(conf); #if HAVE_APPARMOR From ae9242c86a1a566d98a2cff4df680f1fcd0ecc48 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 13 Nov 2012 17:54:01 -0600 Subject: [PATCH 086/171] switch use of #define with static char* Signed-off-by: Serge Hallyn --- src/lxc/conf.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index f3c2334bf..8f1cba9c8 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1732,6 +1732,8 @@ static int setup_private_host_hw_addr(char *veth1) return 0; } +static char *default_rootfs_mount = LXCROOTFSMOUNT; + struct lxc_conf *lxc_conf_init(void) { struct lxc_conf *new; @@ -1750,7 +1752,7 @@ struct lxc_conf *lxc_conf_init(void) new->console.master = -1; new->console.slave = -1; new->console.name[0] = '\0'; - new->rootfs.mount = LXCROOTFSMOUNT; + new->rootfs.mount = default_rootfs_mount; lxc_list_init(&new->cgroup); lxc_list_init(&new->network); lxc_list_init(&new->mount_list); @@ -2576,7 +2578,7 @@ void lxc_conf_free(struct lxc_conf *conf) return; if (conf->console.path) free(conf->console.path); - if (conf->rootfs.mount != LXCROOTFSMOUNT) + if (conf->rootfs.mount != default_rootfs_mount) free(conf->rootfs.mount); lxc_clear_config_network(conf); #if HAVE_APPARMOR From 9935be1881cf7cc7292629fcd64768336a869c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 13 Nov 2012 19:35:17 -0500 Subject: [PATCH 087/171] Check return value of all system calls in startone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One of the system() calls in src/tests/startone.c wasn't checked. This was causing a build failure now that -Wall -Werror are set by default. Signed-off-by: Stéphane Graber --- src/tests/startone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/startone.c b/src/tests/startone.c index d5b8d3fd3..73c8f00c7 100644 --- a/src/tests/startone.c +++ b/src/tests/startone.c @@ -180,7 +180,7 @@ int main(int argc, char *argv[]) ret = system("mkdir -p /var/lib/lxc/lxctest1/rootfs//usr/local/libexec/lxc"); if (!ret) - system("mkdir -p /var/lib/lxc/lxctest1/rootfs/usr/lib/lxc/"); + ret = system("mkdir -p /var/lib/lxc/lxctest1/rootfs/usr/lib/lxc/"); if (!ret) ret = system("cp src/lxc/lxc-init /var/lib/lxc/lxctest1/rootfs//usr/local/libexec/lxc"); if (!ret) From 18c266fc4cb73c911d2d0576628873c0ceada6ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 13 Nov 2012 19:36:05 -0500 Subject: [PATCH 088/171] Add the test binaries to .gitignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- .gitignore | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.gitignore b/.gitignore index bdbea4afb..d5b07e3a5 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,17 @@ src/lxc/lxc-unshare src/lxc/lxc-version src/lxc/lxc-wait +src/tests/lxc-test-containertests +src/tests/lxc-test-createtest +src/tests/lxc-test-destroytest +src/tests/lxc-test-get_item +src/tests/lxc-test-getkeys +src/tests/lxc-test-locktests +src/tests/lxc-test-saveconfig +src/tests/lxc-test-shutdowntest +src/tests/lxc-test-startone + + config/compile config/config.guess config/config.sub From f62b344996937459ae5f31b0358cb440ddde421f Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 13 Nov 2012 21:35:51 -0600 Subject: [PATCH 089/171] dont fail on failure to link kmsg Signed-off-by: Serge Hallyn --- src/lxc/conf.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 8f1cba9c8..fe574acca 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2290,10 +2290,8 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf) return -1; } - if (setup_kmsg(&lxc_conf->rootfs, &lxc_conf->console)) { + if (setup_kmsg(&lxc_conf->rootfs, &lxc_conf->console)) // don't fail ERROR("failed to setup kmsg for '%s'", name); - return -1; - } if (setup_tty(&lxc_conf->rootfs, &lxc_conf->tty_info, lxc_conf->ttydir)) { ERROR("failed to setup the ttys for '%s'", name); From f8a59e315293366fd58d632484245a78a3feaa36 Mon Sep 17 00:00:00 2001 From: Frederic Crozat Date: Wed, 14 Nov 2012 16:11:08 +0100 Subject: [PATCH 090/171] ensure btrfs subvolume is removed when container creating fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frederic Crozat Acked-by: Serge E. Hallyn Acked-by: Stéphane Graber --- src/lxc/lxc-create.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in index e3b50ab62..101e48942 100644 --- a/src/lxc/lxc-create.in +++ b/src/lxc/lxc-create.in @@ -249,6 +249,8 @@ cleanup() { if [ $backingstore = "lvm" ]; then umount $rootfs lvremove -f $rootdev + elif [ $backingstore = "btrfs" ]; then + btrfs subvolume delete "$rootfs" fi ${bindir}/lxc-destroy -n $lxc_name echo "$(basename $0): aborted" >&2 From f6a8db2d947018c09389fd6747ae23072c3a8666 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 14 Nov 2012 10:44:35 -0500 Subject: [PATCH 091/171] Fix package name needed for building docs with RPM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested on Oracle Linux 6 and Fedora 17 Signed-off-by: Dwight Engen Acked-by: Stéphane Graber --- lxc.spec.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxc.spec.in b/lxc.spec.in index 22b8b7db6..3f4d5b6ae 100644 --- a/lxc.spec.in +++ b/lxc.spec.in @@ -30,7 +30,7 @@ Group: Applications/System License: LGPL BuildRoot: %{_tmppath}/%{name}-%{version}-build Requires: libcap -BuildRequires: libcap libcap-devel docbook2x +BuildRequires: libcap libcap-devel docbook2X %description From 3114c9824220921bab15cc283907debccde17fa0 Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Thu, 15 Nov 2012 14:52:16 +0100 Subject: [PATCH 092/171] lxc-start: add option -p, --pidfile=FILE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add option to create a pidfile for lxc-start. This is helpful for init scripts and process monitors when running as daemon. Signed-off-by: Natanael Copa Acked-by: Serge E. Hallyn Acked-by: Stéphane Graber --- doc/lxc-start.sgml.in | 12 ++++++++++++ src/lxc/arguments.h | 1 + src/lxc/lxc_start.c | 24 ++++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/doc/lxc-start.sgml.in b/doc/lxc-start.sgml.in index af79bbc6e..e4b90079b 100644 --- a/doc/lxc-start.sgml.in +++ b/doc/lxc-start.sgml.in @@ -53,6 +53,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -f config_file -c console_file -d + -p pid_file -s KEY=VAL -C command @@ -107,6 +108,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + + + + Create a file with the process id. + + + + diff --git a/src/lxc/arguments.h b/src/lxc/arguments.h index 3c9d28fd7..188c4606b 100644 --- a/src/lxc/arguments.h +++ b/src/lxc/arguments.h @@ -45,6 +45,7 @@ struct lxc_arguments { int daemonize; const char *rcfile; const char *console; + const char *pidfile; /* for lxc-checkpoint/restart */ const char *statefile; diff --git a/src/lxc/lxc_start.c b/src/lxc/lxc_start.c index 81a5774c4..ca1cc2a19 100644 --- a/src/lxc/lxc_start.c +++ b/src/lxc/lxc_start.c @@ -62,6 +62,7 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg) case 'f': args->rcfile = arg; break; case 'C': args->close_all_fds = 1; break; case 's': return lxc_config_define_add(&defines, arg); + case 'p': args->pidfile = arg; break; } return 0; } @@ -72,6 +73,7 @@ static const struct option my_longopts[] = { {"define", required_argument, 0, 's'}, {"console", required_argument, 0, 'c'}, {"close-all-fds", no_argument, 0, 'C'}, + {"pidfile", required_argument, 0, 'p'}, LXC_COMMON_OPTIONS }; @@ -85,6 +87,7 @@ lxc-start start COMMAND in specified container NAME\n\ Options :\n\ -n, --name=NAME NAME for name of the container\n\ -d, --daemon daemonize the container\n\ + -p, --pidfile=FILE Create a file with the process id\n\ -f, --rcfile=FILE Load configuration file FILE\n\ -c, --console=FILE Set the file output for the container console\n\ -C, --close-all-fds If any fds are inherited, close them\n\ @@ -95,6 +98,7 @@ Options :\n\ .parser = my_parser, .checker = NULL, .daemonize = 0, + .pidfile = NULL, }; int main(int argc, char *argv[]) @@ -107,6 +111,7 @@ int main(int argc, char *argv[]) "/sbin/init", '\0', }; + FILE *pid_fp = NULL; lxc_list_init(&defines); @@ -199,6 +204,14 @@ int main(int argc, char *argv[]) free(console); } + if (my_args.pidfile != NULL) { + pid_fp = fopen(my_args.pidfile, "w"); + if (pid_fp == NULL) { + SYSERROR("failed to create '%s'", my_args.name); + return err; + } + } + if (my_args.daemonize) { /* do an early check for needed privs, since otherwise the * user won't see the error */ @@ -214,6 +227,14 @@ int main(int argc, char *argv[]) } } + if (pid_fp != NULL) { + if (fprintf(pid_fp, "%d\n", getpid()) < 0) { + SYSERROR("failed to write '%s'", my_args.pidfile); + return err; + } + fclose(pid_fp); + } + if (my_args.close_all_fds) conf->close_all_fds = 1; @@ -230,6 +251,9 @@ int main(int argc, char *argv[]) err = -1; } + if (my_args.pidfile) + unlink(my_args.pidfile); + return err; } From c3752c0b5926c3d5d3361c8127708da83e585928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 15 Nov 2012 10:51:09 -0500 Subject: [PATCH 093/171] Use clearer error message on failure to create pidfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As suggested by Serge Hallyn on lxc-devel. Signed-off-by: Stéphane Graber --- src/lxc/lxc_start.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxc_start.c b/src/lxc/lxc_start.c index ca1cc2a19..a2335d595 100644 --- a/src/lxc/lxc_start.c +++ b/src/lxc/lxc_start.c @@ -207,7 +207,8 @@ int main(int argc, char *argv[]) if (my_args.pidfile != NULL) { pid_fp = fopen(my_args.pidfile, "w"); if (pid_fp == NULL) { - SYSERROR("failed to create '%s'", my_args.name); + SYSERROR("failed to create pidfile '%s' for '%s'", + my_args.pidfile, my_args.name); return err; } } From e60a8164c12d565f70071ff6b32b823dd495df9e Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Fri, 16 Nov 2012 17:01:55 +0100 Subject: [PATCH 094/171] lxc-create: use posix shell instead of bash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - use '[ -x /path/prog ]' instead of 'type /path/prog' - avoid getopt --longoptions - add \ at after && and || when those are at end of line - make sure condition expands to empty string if variable is empty Signed-off-by: Natanael Copa Acked-by: Serge E. Hallyn Acked-by: Stéphane Graber --- src/lxc/lxc-create.in | 70 ++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in index 101e48942..e8056e9e3 100644 --- a/src/lxc/lxc-create.in +++ b/src/lxc/lxc-create.in @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # # lxc: linux Container library @@ -54,16 +54,26 @@ help() { echo " $(basename $0) -t ubuntu -h" >&2 exit 0 fi - type ${templatedir}/lxc-$lxc_template 2>/dev/null - if [ $? -eq 0 ]; then + if [ -x ${templatedir}/lxc-$lxc_template ]; then echo >&2 echo "Template-specific options (TEMPLATE_OPTIONS):" >&2 ${templatedir}/lxc-$lxc_template -h fi } -shortoptions='hn:f:t:B:' -longoptions='help,name:,config:,template:,backingstore:,fstype:,dir:,lvname:,vgname:,fssize:' +usage_err() { + [ -n "$1" ] && echo "$1" >&2 + usage + exit 1 +} + +optarg_check() { + local opt="$1" optarg="$2" + if [ -z "$optarg" ]; then + usage_err "option '$opt' requires an argument" + fi +} + lxc_path=@LXCPATH@ bindir=@BINDIR@ templatedir=@LXCTEMPLATEDIR@ @@ -73,68 +83,69 @@ fssize=500M vgname=lxc custom_rootfs="" -getopt=$(getopt -o $shortoptions --longoptions $longoptions -- "$@") -if [ $? != 0 ]; then - usage - exit 1; -fi - -eval set -- "$getopt" - -while true; do - case "$1" in +while [ $# -gt 0 ]; do + local opt="$1" + shift + case "$opt" in -h|--help) help exit 1 ;; -n|--name) - shift + optarg_check $opt "$1" lxc_name=$1 shift ;; -f|--config) - shift + optarg_check $opt "$1" lxc_config=$1 shift ;; -t|--template) - shift + optarg_check $opt "$1" lxc_template=$1 shift ;; -B|--backingstore) - shift + optarg_check $opt "$1" backingstore=$1 shift ;; --dir) - shift + optarg_check $opt "$1" custom_rootfs=$1 shift ;; --lvname) - shift + optarg_check $opt "$1" lvname=$1 shift ;; --vgname) - shift + optarg_check $opt "$1" vgname=$1 shift ;; --fstype) - shift + optarg_check $opt "$1" fstype=$1 shift ;; --fssize) - shift + optarg_check $opt "$1" fssize=$1 shift ;; --) shift break;; + -?) + usage_err "unknown option '$opt'" + ;; + -*) + # split opts -abc into -a -b -c + set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@" + ;; *) usage exit 1 @@ -200,7 +211,7 @@ rootfs="$lxc_path/$lxc_name/rootfs" if [ "$backingstore" = "_unset" -o "$backingstore" = "btrfs" ]; then # if no backing store was given, then see if btrfs would work - if which btrfs >/dev/null 2>&1 && + if which btrfs >/dev/null 2>&1 && \ btrfs filesystem df "$lxc_path/" >/dev/null 2>&1; then backingstore="btrfs" else @@ -246,10 +257,10 @@ elif [ "$backingstore" = "btrfs" ]; then fi cleanup() { - if [ $backingstore = "lvm" ]; then + if [ "$backingstore" = "lvm" ]; then umount $rootfs lvremove -f $rootdev - elif [ $backingstore = "btrfs" ]; then + elif [ "$backingstore" = "btrfs" ]; then btrfs subvolume delete "$rootfs" fi ${bindir}/lxc-destroy -n $lxc_name @@ -299,8 +310,7 @@ if [ ! -z $lxc_template ]; then template_path=${templatedir}/lxc-$lxc_template fi - type $template_path 2>/dev/null - if [ $? -ne 0 ]; then + if ! [ -x "$template_path" ]; then echo "$(basename $0): unknown template '$lxc_template'" >&2 cleanup fi @@ -314,7 +324,7 @@ if [ ! -z $lxc_template ]; then echo "'$lxc_template' template installed" fi -if [ $backingstore = "lvm" ]; then +if [ "$backingstore" = "lvm" ]; then echo "Unmounting LVM" umount $rootfs From 11cbdf441d57f8151d512677d4b9f12b1cca4a6f Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Thu, 22 Nov 2012 14:16:23 +0100 Subject: [PATCH 095/171] lxc-create: fix passing over first argument to template script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The e60a8164c12d565f70071ff6b32b823dd495df9e introduced a bug that caused first argument passed over to the template script get lost. This patch fixes it. Signed-off-by: Natanael Copa Acked-by: Stéphane Graber --- src/lxc/lxc-create.in | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in index e8056e9e3..26ec6a267 100644 --- a/src/lxc/lxc-create.in +++ b/src/lxc/lxc-create.in @@ -137,7 +137,6 @@ while [ $# -gt 0 ]; do shift ;; --) - shift break;; -?) usage_err "unknown option '$opt'" From 7858afacec2354892dcfbe2d587d2f08bbd79ca3 Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Mon, 26 Nov 2012 12:00:44 +0100 Subject: [PATCH 096/171] lxc-create: do not use 'local' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently 'local' is not POSIX. Don't use it. Signed-off-by: Natanael Copa Acked-by: Stéphane Graber --- src/lxc/lxc-create.in | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in index 26ec6a267..30f0c22d9 100644 --- a/src/lxc/lxc-create.in +++ b/src/lxc/lxc-create.in @@ -68,9 +68,8 @@ usage_err() { } optarg_check() { - local opt="$1" optarg="$2" - if [ -z "$optarg" ]; then - usage_err "option '$opt' requires an argument" + if [ -z "$2" ]; then + usage_err "option '$1' requires an argument" fi } @@ -84,7 +83,7 @@ vgname=lxc custom_rootfs="" while [ $# -gt 0 ]; do - local opt="$1" + opt="$1" shift case "$opt" in -h|--help) From 2a41cf5d682aa4ed8b75132bb224933d0b14a845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 22 Nov 2012 17:35:44 -0500 Subject: [PATCH 097/171] python: Add add_device() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This introduces a new add_devices() call to the python API. Parameters: - path => Mandatory, path to a character or block device on the host - destpath => Optional, alternative path inside the container The function will allow the node in the container's devices cgroup and then create the entry in the container. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/python-lxc/lxc/__init__.py | 61 ++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/python-lxc/lxc/__init__.py b/src/python-lxc/lxc/__init__.py index 183c7789f..907303e33 100644 --- a/src/python-lxc/lxc/__init__.py +++ b/src/python-lxc/lxc/__init__.py @@ -25,6 +25,7 @@ import _lxc import glob import os import subprocess +import stat import tempfile import time import warnings @@ -149,6 +150,66 @@ class Container(_lxc.Container): _lxc.Container.__init__(self, name) self.network = ContainerNetworkList(self) + def add_device(self, path, destpath=None): + """ + Add device to running container. + """ + + if not destpath: + destpath = path + + if not os.path.exists(path): + return False + + # Lookup the source + path_stat = os.stat(path) + mode = stat.S_IMODE(path_stat.st_mode) + + # Lookup the cgroup + cgroup_path = None + with open("/proc/%s/cgroup" % self.init_pid, "r") as fd: + for line in fd: + if ":devices:" in line: + cgroup_path = line.split(":")[-1].strip() + break + else: + return False + + # Lookup the cgroup mount point + cgroup = None + with open("/proc/mounts", "r") as fd: + for line in fd: + mount = line.split() + if (mount[2] == "cgroup" and "devices" in mount[3] + and os.path.exists("%s/%s" % (mount[1], cgroup_path))): + cgroup = "%s/%s" % (mount[1], cgroup_path) + break + + if not os.path.exists(cgroup): + return False + + # Allow the target + with open("%s/devices.allow" % cgroup, "a") as fd: + if stat.S_ISBLK(path_stat.st_mode): + fd.write("b %s:%s rwm" % (int(path_stat.st_rdev / 256), + int(path_stat.st_rdev % 256))) + elif stat.S_ISCHR(path_stat.st_mode): + fd.write("c %s:%s rwm" % (int(path_stat.st_rdev / 256), + int(path_stat.st_rdev % 256))) + + # Create the target + rootfs = "/proc/%s/root/" % self.init_pid + container_path = "%s/%s" % (rootfs, destpath) + + if os.path.exists(container_path): + os.remove(container_path) + + os.mknod(container_path, path_stat.st_mode, path_stat.st_rdev) + os.chmod(container_path, mode) + os.chown(container_path, 0, 0) + + return True + def append_config_item(self, key, value): """ Append 'value' to 'key', assuming 'key' is a list. From 69c478daf1aeaf84941c561d381b26747b614101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 22 Nov 2012 18:01:08 -0500 Subject: [PATCH 098/171] python: Add new lxc-device tool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new lxc-device tool which uses the new add_device() function of the python API and lets you add a new device node to a running container. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/lxc/Makefile.am | 1 + src/lxc/lxc-device | 59 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/lxc/lxc-device diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 7d86ad661..02f6855f5 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -98,6 +98,7 @@ bin_SCRIPTS = \ lxc-destroy if ENABLE_PYTHON + bin_SCRIPTS += lxc-device bin_SCRIPTS += lxc-start-ephemeral endif diff --git a/src/lxc/lxc-device b/src/lxc/lxc-device new file mode 100644 index 000000000..6c91e67fb --- /dev/null +++ b/src/lxc/lxc-device @@ -0,0 +1,59 @@ +#!/usr/bin/python3 +# +# lxc-device: Add devices to a running container +# +# This python implementation is based on the work done in the original +# shell implementation done by Serge Hallyn in Ubuntu (and other contributors) +# +# (C) Copyright Canonical Ltd. 2012 +# +# Authors: +# Stéphane Graber +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# NOTE: To remove once the API is stabilized +import warnings +warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable") + +import argparse +import gettext +import lxc +import sys + +_ = gettext.gettext +gettext.textdomain("lxc-device") + +# Begin parsing the command line +parser = argparse.ArgumentParser(description=_("LXC: Manage devices"), + formatter_class=argparse.RawTextHelpFormatter) + +parser.add_argument("-n", dest="container", metavar="CONTAINER", + help=_("Container to add the device to"), required=True) + +parser.add_argument("--add", action="append", default=[], metavar="DEVICE", + help=_("Add a device"), required=True) + +args = parser.parse_args() + +container = lxc.Container(args.container) +if not container.running: + print("The container must be running.") + sys.exit(1) + +for device in args.add: + container.add_device(device) + print("Added '%s' to '%s'." % (device, container.name)) From c6883f383e587725552f7c71e96ebe1c34ae7c56 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Thu, 1 Nov 2012 22:27:03 +0100 Subject: [PATCH 099/171] Add lxc.autodev Add a container config option to mount and populate /dev in a container. We might want to add options to specify a max size for /dev other than the default 100k, and to specify other devices to create. And maybe someone can think of a better name than autodev. Changelog: Don't error out if we couldn't mknod a /dev/ttyN. Changelog: Describe the option in lxc.conf manpage. Signed-off-by: Serge Hallyn --- doc/lxc.conf.sgml.in | 25 ++++++++++++++ src/lxc/conf.c | 77 ++++++++++++++++++++++++++++++++++++++++++++ src/lxc/conf.h | 1 + src/lxc/confile.c | 12 +++++++ 4 files changed, 115 insertions(+) diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in index 6c3d7b2aa..eed07fc79 100644 --- a/doc/lxc.conf.sgml.in +++ b/doc/lxc.conf.sgml.in @@ -501,6 +501,31 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + /dev directory + + By default, lxc does nothing with the container's + /dev. This allows the container's + /dev to be set up as needed in the container + rootfs. If lxc.autodev is to 1, then after mounting the container's + rootfs LXC will mount a fresh tmpfs under /dev + (limited to 100k) and fill in a minimal set of initial devices. + + + + + + + + + Set this to 1 to have LXC mount and populate a minimal + /dev when starting the container. + + + + + + Mount points diff --git a/src/lxc/conf.c b/src/lxc/conf.c index fe574acca..f1c41f134 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -653,6 +653,15 @@ static int setup_tty(const struct lxc_rootfs *rootfs, return -1; } } else { + /* If we populated /dev, then we need to create /dev/ttyN */ + if (access(path, F_OK)) { + ret = creat(path, 0660); + if (ret==-1) { + SYSERROR("error creating %s\n", path); + /* this isn't fatal, continue */ + } else + close(ret); + } if (mount(pty_info->name, path, "none", MS_BIND, 0)) { WARN("failed to mount '%s'->'%s'", pty_info->name, path); @@ -860,6 +869,67 @@ static int setup_rootfs_pivot_root(const char *rootfs, const char *pivotdir) return 0; } +struct lxc_devs { + char *name; + mode_t mode; + int maj; + int min; +}; + +struct lxc_devs lxc_devs[] = { + { "null", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 3 }, + { "zero", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 5 }, + { "full", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 7 }, + { "urandom", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 9 }, + { "random", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 8 }, + { "tty", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 5, 0 }, + { "console", S_IFCHR | S_IRUSR | S_IWUSR, 5, 1 }, +}; + +/* + * Do we want to add options for max size of /dev and a file to + * specify which devices to create? + */ +static int setup_autodev(char *root) +{ + int ret; + struct lxc_devs *d; + char path[MAXPATHLEN]; + int i; + + INFO("Creating and populating /dev under %s\n", root); + ret = snprintf(path, MAXPATHLEN, "%s/dev", root); + if (ret < 0 || ret > MAXPATHLEN) + return -1; + ret = mount("none", path, "tmpfs", 0, "size=100000"); + if (ret) { + SYSERROR("Failed to mount /dev at %s\n", root); + return -1; + } + for (i = 0; i < sizeof(lxc_devs) / sizeof(lxc_devs[0]); i++) { + d = &lxc_devs[i]; + ret = snprintf(path, MAXPATHLEN, "%s/dev/%s", root, d->name); + if (ret < 0 || ret >= MAXPATHLEN) + return -1; + ret = mknod(path, d->mode, makedev(d->maj, d->min)); + if (ret) { + SYSERROR("Error creating %s\n", d->name); + return -1; + } + } + ret = snprintf(path, MAXPATHLEN, "%s/dev/pts", root); + if (ret < 0 || ret >= MAXPATHLEN) + return -1; + ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + if (ret) { + SYSERROR("Failed to create /dev/pts in container"); + return -1; + } + + INFO("Populated /dev under %s\n", root); + return 0; +} + static int setup_rootfs(const struct lxc_rootfs *rootfs) { if (!rootfs->path) @@ -2265,6 +2335,13 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf) return -1; } + if (lxc_conf->autodev) { + if (setup_autodev(lxc_conf->rootfs.mount)) { + ERROR("failed to set up /dev in the container"); + return -1; + } + } + if (setup_mount(&lxc_conf->rootfs, lxc_conf->fstab, name)) { ERROR("failed to setup the mounts for '%s'", name); return -1; diff --git a/src/lxc/conf.h b/src/lxc/conf.h index dccc17690..76bf19d10 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -237,6 +237,7 @@ struct lxc_conf { #endif char *seccomp; // filename with the seccomp rules int maincmd_fd; + int autodev; // if 1, mount and fill a /dev at start }; int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf); diff --git a/src/lxc/confile.c b/src/lxc/confile.c index cf1c8913a..3d9f36e99 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -79,6 +79,7 @@ static int config_console(const char *, char *, struct lxc_conf *); static int config_seccomp(const char *, char *, struct lxc_conf *); static int config_includefile(const char *, char *, struct lxc_conf *); static int config_network_nic(const char *, char *, struct lxc_conf *); +static int config_autodev(const char *, char *, struct lxc_conf *); static struct lxc_config_t config[] = { @@ -121,6 +122,7 @@ static struct lxc_config_t config[] = { { "lxc.console", config_console }, { "lxc.seccomp", config_seccomp }, { "lxc.include", config_includefile }, + { "lxc.autodev", config_autodev }, }; static const size_t config_size = sizeof(config)/sizeof(struct lxc_config_t); @@ -881,6 +883,16 @@ static int config_aa_profile(const char *key, char *value, struct lxc_conf *lxc_ } #endif +static int config_autodev(const char *key, char *value, + struct lxc_conf *lxc_conf) +{ + int v = atoi(value); + + lxc_conf->autodev = v; + + return 0; +} + static int config_cgroup(const char *key, char *value, struct lxc_conf *lxc_conf) { char *token = "lxc.cgroup."; From ad493d03fed380ac151dacb53ddfd01a23aeacee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 26 Nov 2012 11:45:28 -0500 Subject: [PATCH 100/171] lxc.conf.sgml.in: Fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a typo in the previous lxc.conf.sgml.in change. Signed-off-by: Stéphane Graber --- doc/lxc.conf.sgml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in index eed07fc79..70b806d23 100644 --- a/doc/lxc.conf.sgml.in +++ b/doc/lxc.conf.sgml.in @@ -507,7 +507,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA By default, lxc does nothing with the container's /dev. This allows the container's /dev to be set up as needed in the container - rootfs. If lxc.autodev is to 1, then after mounting the container's + rootfs. If lxc.autodev is set to 1, then after mounting the container's rootfs LXC will mount a fresh tmpfs under /dev (limited to 100k) and fill in a minimal set of initial devices. From c93c7b1a0b0d4548780b9c22fb9ab907783caad1 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 14 Nov 2012 12:03:56 -0500 Subject: [PATCH 101/171] Fix checkconfig to handle kernel memory cgroup name change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kernel config option for the memory cgroup was changed in 3.6 from CONFIG_CGROUP_MEM_RES_CTLR to CONFIG_MEMCG with commit c255a458. Signed-off-by: Dwight Engen Acked-by: Stéphane Graber --- src/lxc/lxc-checkconfig.in | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/lxc/lxc-checkconfig.in b/src/lxc/lxc-checkconfig.in index 8c2b5e517..8263c172f 100644 --- a/src/lxc/lxc-checkconfig.in +++ b/src/lxc/lxc-checkconfig.in @@ -68,6 +68,15 @@ print_cgroups() { } CGROUP_MNT_PATH=`print_cgroups cgroup /proc/self/mounts | head -1` +KVER_MAJOR=$($GREP '^# Linux' $CONFIG | \ + sed -r 's/.* ([0-9])\.[0-9]{1,2}\.[0-9]{1,3}.*/\1/') +if [[ $KVER_MAJOR == 2 ]]; then +KVER_MINOR=$($GREP '^# Linux' $CONFIG | \ + sed -r 's/.* 2.6.([0-9]{2}).*/\1/') +else +KVER_MINOR=$($GREP '^# Linux' $CONFIG | \ + sed -r 's/.* [0-9]\.([0-9]{1,3})\.[0-9]{1,3}.*/\1/') +fi echo -n "Cgroup: " && is_enabled CONFIG_CGROUPS yes @@ -80,22 +89,18 @@ fi echo -n "Cgroup device: " && is_enabled CONFIG_CGROUP_DEVICE echo -n "Cgroup sched: " && is_enabled CONFIG_CGROUP_SCHED echo -n "Cgroup cpu account: " && is_enabled CONFIG_CGROUP_CPUACCT -echo -n "Cgroup memory controller: " && is_enabled CONFIG_CGROUP_MEM_RES_CTLR +echo -n "Cgroup memory controller: " +if [ $KVER_MAJOR -ge 3 -a $KVER_MINOR -ge 6 ]; then + is_enabled CONFIG_MEMCG +else + is_enabled CONFIG_CGROUP_MEM_RES_CTLR +fi is_set CONFIG_SMP && echo -n "Cgroup cpuset: " && is_enabled CONFIG_CPUSETS echo echo "--- Misc ---" echo -n "Veth pair device: " && is_enabled CONFIG_VETH echo -n "Macvlan: " && is_enabled CONFIG_MACVLAN echo -n "Vlan: " && is_enabled CONFIG_VLAN_8021Q -KVER_MAJOR=$($GREP '^# Linux' $CONFIG | \ - sed -r 's/.* ([0-9])\.[0-9]{1,2}\.[0-9]{1,3}.*/\1/') -if [[ $KVER_MAJOR == 2 ]]; then -KVER_MINOR=$($GREP '^# Linux' $CONFIG | \ - sed -r 's/.* 2.6.([0-9]{2}).*/\1/') -else -KVER_MINOR=$($GREP '^# Linux' $CONFIG | \ - sed -r 's/.* [0-9]\.([0-9]{1,3})\.[0-9]{1,3}.*/\1/') -fi echo -n "File capabilities: " && ( [[ ${KVER_MAJOR} == 2 && ${KVER_MINOR} < 33 ]] && is_enabled CONFIG_SECURITY_FILE_CAPABILITIES ) || From f79d43bbe70a01454049b77d6f15f6369744959e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 26 Nov 2012 12:08:13 -0500 Subject: [PATCH 102/171] Remove all trailing whitespaces. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- COPYING | 2 +- TODO | 2 +- config/acinclude.m4 | 16 ++++++++-------- doc/FAQ.txt | 4 ++-- doc/lxc-cgroup.sgml.in | 4 ++-- doc/lxc-console.sgml.in | 6 +++--- doc/lxc-destroy.sgml.in | 4 ++-- doc/lxc-execute.sgml.in | 4 ++-- doc/lxc-freeze.sgml.in | 4 ++-- doc/lxc-ls.sgml.in | 4 ++-- doc/lxc-monitor.sgml.in | 6 +++--- doc/lxc-ps.sgml.in | 10 +++++----- doc/lxc-start.sgml.in | 4 ++-- doc/lxc-stop.sgml.in | 6 +++--- doc/lxc-unfreeze.sgml.in | 4 ++-- doc/lxc-wait.sgml.in | 4 ++-- doc/lxc.conf | 2 +- doc/lxc.conf.sgml.in | 10 +++++----- doc/lxc.sgml.in | 12 ++++++------ src/lxc/af_unix.c | 8 ++++---- src/lxc/genl.c | 4 ++-- src/lxc/genl.h | 4 ++-- src/lxc/lxc-checkconfig.in | 2 +- src/lxc/lxc_monitor.c | 2 +- src/lxc/lxc_start.c | 2 +- src/lxc/mainloop.c | 4 ++-- src/lxc/mainloop.h | 4 ++-- src/lxc/network.h | 2 +- src/lxc/nl.c | 14 +++++++------- src/lxc/nl.h | 32 ++++++++++++++++---------------- src/lxc/rtnl.c | 2 +- src/lxc/rtnl.h | 4 ++-- templates/lxc-debian.in | 2 +- templates/lxc-oracle.in | 2 +- 34 files changed, 98 insertions(+), 98 deletions(-) diff --git a/COPYING b/COPYING index 5ab7695ab..602bfc946 100644 --- a/COPYING +++ b/COPYING @@ -146,7 +146,7 @@ such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. - + 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an diff --git a/TODO b/TODO index 3b6e95638..56d505698 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,6 @@ * create an interactive configuration with the lxc-create command - line, like the 'make menuconfig' of the kernel. + line, like the 'make menuconfig' of the kernel. = lxc-create [-n foo] -m|--menuconfig diff --git a/config/acinclude.m4 b/config/acinclude.m4 index a36fdf3cc..d718b5e76 100644 --- a/config/acinclude.m4 +++ b/config/acinclude.m4 @@ -2,21 +2,21 @@ dnl as-ac-expand.m4 0.2.0 dnl autostars m4 macro for expanding directories using configure's prefix dnl thomas@apestaart.org dnl - + dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR) dnl example dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir) dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local - + AC_DEFUN([AS_AC_EXPAND], [ EXP_VAR=[$1] FROM_VAR=[$2] - + dnl first expand prefix and exec_prefix if necessary prefix_save=$prefix exec_prefix_save=$exec_prefix - + dnl if no prefix given, then use /usr/local, the default prefix if test "x$prefix" = "xNONE"; then prefix="$ac_default_prefix" @@ -25,7 +25,7 @@ AC_DEFUN([AS_AC_EXPAND], if test "x$exec_prefix" = "xNONE"; then exec_prefix=$prefix fi - + full_var="$FROM_VAR" dnl loop until it doesn't change anymore while true; do @@ -33,11 +33,11 @@ AC_DEFUN([AS_AC_EXPAND], if test "x$new_full_var" = "x$full_var"; then break; fi full_var=$new_full_var done - + dnl clean up full_var=$new_full_var AC_SUBST([$1], "$full_var") - + dnl restore prefix and exec_prefix prefix=$prefix_save exec_prefix=$exec_prefix_save @@ -94,7 +94,7 @@ x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"` # Determine the number of characters in A and B. ax_compare_version_len_A=`echo "$A" | awk '{print(length)}'` ax_compare_version_len_B=`echo "$B" | awk '{print(length)}'` - + # Set A to no more than B's length and B to no more than A's length. A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"` B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"` diff --git a/doc/FAQ.txt b/doc/FAQ.txt index fce5eef5d..50238ffde 100644 --- a/doc/FAQ.txt +++ b/doc/FAQ.txt @@ -32,8 +32,8 @@ error when starting a container. "[syserr] lxc_start:96: Invalid argument - failed to fork into a new namespace" -Answer: -------- +Answer: +------- read the lxc man page about kernel version prereq :) most probably your kernel is not configured to support the container options you diff --git a/doc/lxc-cgroup.sgml.in b/doc/lxc-cgroup.sgml.in index b321bc3ac..53e503542 100644 --- a/doc/lxc-cgroup.sgml.in +++ b/doc/lxc-cgroup.sgml.in @@ -1,4 +1,4 @@ - + + +]> + + + + @LXC_GENERATE_DATE@ + + + lxc-ls + 1 + + + + lxc-ls + + + list the containers existing on the system + + + + + + lxc-ls + --active + ls option + + + + + Description + + lxc-ls list the containers existing on the + system. + + + + + Options + + + + + + + + + List active containers. + + + + + + + + + + + The option passed to lxc-ls are the + same as the ls command. + + + + + + + + + + Examples + + + lxc-ls -l + + + list all the container and their permissions. + + + + + + lxc-ls --active -1 + + + list active containers and display the list in one column. + + + + + + + + + See Also + + + + ls + 1 + , + + + + + &seealso; + + + Author + Daniel Lezcano daniel.lezcano@free.fr + + + + + diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index de227c70d..bf675f9d0 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -100,6 +100,8 @@ if ENABLE_PYTHON bin_SCRIPTS += lxc-device bin_SCRIPTS += lxc-ls bin_SCRIPTS += lxc-start-ephemeral +else + bin_SCRIPTS += legacy/lxc-ls endif bin_PROGRAMS = \ diff --git a/src/lxc/legacy/lxc-ls.in b/src/lxc/legacy/lxc-ls.in new file mode 100644 index 000000000..f26572da2 --- /dev/null +++ b/src/lxc/legacy/lxc-ls.in @@ -0,0 +1,94 @@ +#!/bin/bash + +# +# lxc: linux Container library + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +lxc_path=@LXCPATH@ + +usage() +{ + echo "usage: $(basename $0) [--active] [--] [LS_OPTIONS...]" >&2 +} + +help() { + usage + echo >&2 + echo "List containers existing on the system." >&2 + echo >&2 + echo " --active list active containers" >&2 + echo " LS_OPTIONS ls command options (see \`ls --help')" >&2 +} + +get_parent_cgroup() +{ + local hierarchies hierarchy fields subsystems init_cgroup mountpoint + + parent_cgroup="" + + # Obtain a list of hierarchies that contain one or more subsystems + hierarchies=$(tail -n +2 /proc/cgroups | cut -f 2) + + # Iterate through the list until a suitable hierarchy is found + for hierarchy in $hierarchies; do + # Obtain information about the init process in the hierarchy + fields=$(grep -E "^$hierarchy:" /proc/1/cgroup | head -n 1) + if [ -z "$fields" ]; then continue; fi + fields=${fields#*:} + + # Get a comma-separated list of the hierarchy's subsystems + subsystems=${fields%:*} + + # Get the cgroup of the init process in the hierarchy + init_cgroup=${fields#*:} + + # Get the filesystem mountpoint of the hierarchy + mountpoint=$(grep -E "^[^ ]+ [^ ]+ cgroup ([^ ]+,)?$subsystems(,[^ ]+)? " /proc/self/mounts | cut -d ' ' -f 2) + if [ -z "$mountpoint" ]; then continue; fi + + # Return the absolute path to the containers' parent cgroup + # (do not append '/lxc' if the hierarchy contains the 'ns' subsystem) + if [[ ",$subsystems," == *,ns,* ]]; then + parent_cgroup="${mountpoint}${init_cgroup%/}" + else + parent_cgroup="${mountpoint}${init_cgroup%/}/lxc" + fi + break + done +} + +directory=$(readlink -f "$lxc_path") + +for i in "$@"; do + case $i in + --help) + help; exit;; + --active) + get_parent_cgroup; directory="$parent_cgroup"; shift;; + --) + shift; break;; + *) + break;; + esac +done + +containers="" +if [ ! -z "$directory" ]; then + containers=$(find $directory -mindepth 1 -maxdepth 1 -type d -printf "%f\n" 2>/dev/null) +fi + +cd "$directory" +ls -d $@ -- $containers From 4245ba50074f33c4bfa8e8b133598767841e712b Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Thu, 29 Nov 2012 16:24:47 -0500 Subject: [PATCH 138/171] make install should create $LXCPATH directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The $LXCPATH (default /var/lib/lxc) directory was not being created by make install, so unless it gets created by some other means (packaging tools), commands such as lxc-create will fail. Signed-off-by: Dwight Engen Acked-by: Stéphane Graber --- Makefile.am | 3 +++ lxc.spec.in | 1 + 2 files changed, 4 insertions(+) diff --git a/Makefile.am b/Makefile.am index f99ad1ca3..863f8fde0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,6 +9,9 @@ EXTRA_DIST = autogen.sh lxc.spec CONTRIBUTING MAINTAINERS ChangeLog pcdatadir = $(libdir)/pkgconfig pcdata_DATA = lxc.pc +install-data-local: + $(MKDIR_P) $(DESTDIR)$(LXCPATH) + ChangeLog:: @touch ChangeLog diff --git a/lxc.spec.in b/lxc.spec.in index 3f4d5b6ae..bc69d6863 100644 --- a/lxc.spec.in +++ b/lxc.spec.in @@ -97,6 +97,7 @@ rm -rf %{buildroot} %defattr(-,root,root) %{_libdir}/*.so.* %{_libdir}/%{name} +@LXCPATH@ %attr(4555,root,root) %{_libexecdir}/%{name}/lxc-init %files devel From 7b35f3d60a7d007e39b44461181e118bd3942da7 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 4 Dec 2012 12:00:26 -0600 Subject: [PATCH 139/171] rename physical nics at shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a physical nic is being set up, store its ifindex and original name in struct lxc_conf. At reboot, reset the original name. We can't just go over the original network list in lxc_conf at shutdown because that may be tweaked in the meantime through the C api. The saved_nics list is only setup during lxc_spawn(), and restored and freed after lxc_start. Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/1086244 Changelog: remove non-effect change in execute.c Signed-off-by: Serge Hallyn Acked-by: Stéphane Graber --- src/lxc/conf.c | 28 ++++++++++++++++++++++++++++ src/lxc/conf.h | 9 +++++++++ src/lxc/execute.c | 1 - src/lxc/start.c | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 79d96d70f..45e0b31da 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1821,6 +1821,21 @@ static int setup_network(struct lxc_list *network) return 0; } +void lxc_rename_phys_nics_on_shutdown(struct lxc_conf *conf) +{ + int i; + + INFO("running to reset %d nic names", conf->num_savednics); + for (i=0; inum_savednics; i++) { + struct saved_nic *s = &conf->saved_nics[i]; + INFO("resetting nic %d to %s\n", s->ifindex, s->orig_name); + lxc_netdev_rename_by_index(s->ifindex, s->orig_name); + free(s->orig_name); + } + conf->num_savednics = 0; + free(conf->saved_nics); +} + static int setup_private_host_hw_addr(char *veth1) { struct ifreq ifr; @@ -2710,6 +2725,18 @@ int lxc_clear_hooks(struct lxc_conf *c, const char *key) return 0; } +void lxc_clear_saved_nics(struct lxc_conf *conf) +{ + int i; + + if (!conf->num_savednics) + return; + for (i=0; i < conf->num_savednics; i++) + free(conf->saved_nics[i].orig_name); + conf->saved_nics = 0; + free(conf->saved_nics); +} + void lxc_conf_free(struct lxc_conf *conf) { if (!conf) @@ -2737,5 +2764,6 @@ void lxc_conf_free(struct lxc_conf *conf) lxc_clear_cgroups(conf, "lxc.cgroup"); lxc_clear_hooks(conf, "lxc.hook"); lxc_clear_mount_entries(conf); + lxc_clear_saved_nics(conf); free(conf); } diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 694bce48e..3f6181f32 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -211,6 +211,11 @@ enum lxchooks { LXCHOOK_POSTSTOP, NUM_LXC_HOOKS}; extern char *lxchook_names[NUM_LXC_HOOKS]; +struct saved_nic { + int ifindex; + char *orig_name; +}; + struct lxc_conf { char *fstab; int tty; @@ -221,6 +226,8 @@ struct lxc_conf { struct utsname *utsname; struct lxc_list cgroup; struct lxc_list network; + struct saved_nic *saved_nics; + int num_savednics; struct lxc_list mount_list; struct lxc_list caps; struct lxc_tty_info tty_info; @@ -273,4 +280,6 @@ extern int lxc_clear_hooks(struct lxc_conf *c, const char *key); */ extern int lxc_setup(const char *name, struct lxc_conf *lxc_conf); + +extern void lxc_rename_phys_nics_on_shutdown(struct lxc_conf *conf); #endif diff --git a/src/lxc/execute.c b/src/lxc/execute.c index 487765ff9..99800d084 100644 --- a/src/lxc/execute.c +++ b/src/lxc/execute.c @@ -27,7 +27,6 @@ #include #include - #include "log.h" #include "start.h" diff --git a/src/lxc/start.c b/src/lxc/start.c index 3e26b27be..7320d74a8 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -579,6 +579,37 @@ out_warn_father: return -1; } +int save_phys_nics(struct lxc_conf *conf) +{ + struct lxc_list *iterator; + + lxc_list_for_each(iterator, &conf->network) { + struct lxc_netdev *netdev = iterator->elem; + + if (netdev->type != LXC_NET_PHYS) + continue; + conf->saved_nics = realloc(conf->saved_nics, + (conf->num_savednics+1)*sizeof(struct saved_nic)); + if (!conf->saved_nics) { + SYSERROR("failed to allocate memory"); + return -1; + } + conf->saved_nics[conf->num_savednics].ifindex = netdev->ifindex; + conf->saved_nics[conf->num_savednics].orig_name = strdup(netdev->link); + if (!conf->saved_nics[conf->num_savednics].orig_name) { + SYSERROR("failed to allocate memory"); + return -1; + } + INFO("stored saved_nic #%d idx %d name %s\n", conf->num_savednics, + conf->saved_nics[conf->num_savednics].ifindex, + conf->saved_nics[conf->num_savednics].orig_name); + conf->num_savednics++; + } + + return 0; +} + + int lxc_spawn(struct lxc_handler *handler) { int failed_before_rename = 0; @@ -613,6 +644,11 @@ int lxc_spawn(struct lxc_handler *handler) } } + if (save_phys_nics(handler->conf)) { + ERROR("failed to save physical nic info"); + goto out_abort; + } + /* * if the rootfs is not a blockdev, prevent the container from * marking it readonly. @@ -739,6 +775,8 @@ int __lxc_start(const char *name, struct lxc_conf *conf, } } + lxc_rename_phys_nics_on_shutdown(handler->conf); + err = lxc_error_set_and_log(handler->pid, status); out_fini: lxc_delete_network(handler); From c0b5f522feab43871f0e116eadd9ed992d5fb194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 4 Dec 2012 16:17:07 -0500 Subject: [PATCH 140/171] lxc-start-ephemeral: Use argparse errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use argparse's error function instead of our own print + exit. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/lxc/lxc-start-ephemeral.in | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/lxc/lxc-start-ephemeral.in b/src/lxc/lxc-start-ephemeral.in index ccf60591a..e11919fb4 100644 --- a/src/lxc/lxc-start-ephemeral.in +++ b/src/lxc/lxc-start-ephemeral.in @@ -96,20 +96,17 @@ args = parser.parse_args() # Basic requirements check ## Check that -d and CMD aren't used at the same time if args.command and args.daemon: - print(_("You can't use -d and a command at the same time.")) - sys.exit(1) + parser.error(_("You can't use -d and a command at the same time.")) ## The user needs to be uid 0 if not os.geteuid() == 0: - print(_("You must be root to run this script. Try running: sudo %s" % - (sys.argv[0]))) - sys.exit(1) + parser.error(_("You must be root to run this script. Try running: sudo %s" + % (sys.argv[0]))) # Load the orig container orig = lxc.Container(args.orig) if not orig.defined: - print(_("Source container '%s' doesn't exist." % args.orig)) - sys.exit(1) + parser.error(_("Source container '%s' doesn't exist." % args.orig)) # Create the new container paths dest_path = tempfile.mkdtemp(prefix="%s-" % args.orig, dir="@LXCPATH@") From daf04e4ce912839383d649109a6aac66c2b48709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 4 Dec 2012 16:17:08 -0500 Subject: [PATCH 141/171] lxc-ls: Show a simple error message when non-root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of returning a python stacktrace, check what the current euid is and show an argparse error message similar to that used in lxc-start-ephemeral. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/lxc/lxc-ls | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lxc/lxc-ls b/src/lxc/lxc-ls index 8a1d1ed88..2ad1f7f0c 100644 --- a/src/lxc/lxc-ls +++ b/src/lxc/lxc-ls @@ -32,6 +32,7 @@ warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable") import argparse import gettext import lxc +import os import re import sys @@ -115,6 +116,12 @@ parser.add_argument("filter", metavar='FILTER', type=str, nargs="?", args = parser.parse_args() +# Basic checks +## The user needs to be uid 0 +if not os.geteuid() == 0: + parser.error(_("You must be root to run this script. Try running: sudo %s" + % (sys.argv[0]))) + # --active is the same as --running --frozen if args.active: if not args.state: From b1eafd4aeb778fbf86751b2b032f8fb1834e9099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 4 Dec 2012 16:17:09 -0500 Subject: [PATCH 142/171] lxc-device: Show an error message when non-root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of returning a python stacktrace, check what the current euid is and show an argparse error message similar to that used in lxc-start-ephemeral. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/lxc/lxc-device | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lxc/lxc-device b/src/lxc/lxc-device index 6c91e67fb..467df17c0 100644 --- a/src/lxc/lxc-device +++ b/src/lxc/lxc-device @@ -32,6 +32,7 @@ warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable") import argparse import gettext import lxc +import os import sys _ = gettext.gettext @@ -49,6 +50,11 @@ parser.add_argument("--add", action="append", default=[], metavar="DEVICE", args = parser.parse_args() +# The user needs to be uid 0 +if not os.geteuid() == 0: + parser.error(_("You must be root to run this script. Try running: sudo %s" + % (sys.argv[0]))) + container = lxc.Container(args.container) if not container.running: print("The container must be running.") From 20cf2e976bf31494e64f05f3d7a2cd5130971502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 4 Dec 2012 17:30:13 -0500 Subject: [PATCH 143/171] python: Update to the device related functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit does the following changes to the python API: - Rename the add_device API call to add_device_node - Adds an extra check that the container is running to add_device_node - Introduces a new add_device_net function And the following changes to the lxc-device tool: - Change parser setup to better cope with variable number of arguments - Add support for network devices (currently auto-detected) - Support for different names on the host and in the container Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/lxc/lxc-device | 48 +++++++++++++++++++++++++++------- src/python-lxc/lxc/__init__.py | 26 ++++++++++++++++-- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/lxc/lxc-device b/src/lxc/lxc-device index 467df17c0..db9399d2a 100644 --- a/src/lxc/lxc-device +++ b/src/lxc/lxc-device @@ -42,24 +42,54 @@ gettext.textdomain("lxc-device") parser = argparse.ArgumentParser(description=_("LXC: Manage devices"), formatter_class=argparse.RawTextHelpFormatter) +# Global arguments parser.add_argument("-n", dest="container", metavar="CONTAINER", - help=_("Container to add the device to"), required=True) + help=_("Name of the container to add the device to"), + required=True) -parser.add_argument("--add", action="append", default=[], metavar="DEVICE", - help=_("Add a device"), required=True) +# Commands +subparsers = parser.add_subparsers() +subparser_add = subparsers.add_parser('add', help=_('Add a device')) +subparser_add.set_defaults(action="add") + +subparser_add.add_argument(dest="device", metavar="DEVICE", + help=_("Add a device " + "(path to a node or interface name)")) + +subparser_add.add_argument(dest="name", metavar="NAME", nargs="?", + help=_("Use an alternative path or name " + "in the container")) args = parser.parse_args() -# The user needs to be uid 0 +# Some basic checks +if not hasattr(args, "action"): + parser.error(_("You must specify an action.")) + +## The user needs to be uid 0 if not os.geteuid() == 0: parser.error(_("You must be root to run this script. Try running: sudo %s" % (sys.argv[0]))) +## Don't rename if no alternative name +if not args.name: + args.name = args.device + +## Check that the container is ready container = lxc.Container(args.container) if not container.running: - print("The container must be running.") - sys.exit(1) + parser.error("The container must be running.") -for device in args.add: - container.add_device(device) - print("Added '%s' to '%s'." % (device, container.name)) +# Do the work +if args.action == "add": + if os.path.exists("/sys/class/net/%s/" % args.device): + ret = container.add_device_net(args.device, args.name) + else: + ret = container.add_device_node(args.device, args.name) + + if ret: + print("Added '%s' to '%s' as '%s'." % + (args.device, container.name, args.name)) + else: + print("Failed to add '%s' to '%s' as '%s'." % + (args.device, container.name, args.name)) diff --git a/src/python-lxc/lxc/__init__.py b/src/python-lxc/lxc/__init__.py index 78852ec07..cde4fd1e6 100644 --- a/src/python-lxc/lxc/__init__.py +++ b/src/python-lxc/lxc/__init__.py @@ -154,11 +154,14 @@ class Container(_lxc.Container): _lxc.Container.__init__(self, name) self.network = ContainerNetworkList(self) - def add_device(self, path, destpath=None): + def add_device_node(self, path, destpath=None): """ - Add device to running container. + Add block/char device to running container. """ + if not self.running: + return False + if not destpath: destpath = path @@ -214,6 +217,25 @@ class Container(_lxc.Container): return True + def add_device_net(self, name, destname=None): + """ + Add network device to running container. + """ + + if not self.running: + return False + + if not destname: + destname = name + + if not os.path.exists("/sys/class/net/%s/" % name): + return False + + return subprocess.call(['ip', 'link', 'set', + 'dev', name, + 'netns', str(self.init_pid), + 'name', destname]) == 0 + def append_config_item(self, key, value): """ Append 'value' to 'key', assuming 'key' is a list. From 0749e740608de0621fbbd9bd89946f5fbecb8d72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 4 Dec 2012 17:42:46 -0500 Subject: [PATCH 144/171] lxc-ls: Update code to allow non-root listing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-arrange the code so that we only grab the container object when doing something more than building a simple list of existing containers. This means that now the following calls can run unprivileged: - lxc-ls - lxc-ls -1 Everything else will still require root privileges. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/lxc/lxc-ls | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/lxc/lxc-ls b/src/lxc/lxc-ls index 2ad1f7f0c..98b786167 100644 --- a/src/lxc/lxc-ls +++ b/src/lxc/lxc-ls @@ -116,12 +116,6 @@ parser.add_argument("filter", metavar='FILTER', type=str, nargs="?", args = parser.parse_args() -# Basic checks -## The user needs to be uid 0 -if not os.geteuid() == 0: - parser.error(_("You must be root to run this script. Try running: sudo %s" - % (sys.argv[0]))) - # --active is the same as --running --frozen if args.active: if not args.state: @@ -135,20 +129,34 @@ if not sys.stdout.isatty(): # Turn args.fancy_format into a list args.fancy_format = args.fancy_format.strip().split(",") +# Basic checks +## The user needs to be uid 0 +if not os.geteuid() == 0 and (args.fancy or args.state): + parser.error(_("You must be root to access advanced container properties. " + "Try running: sudo %s" + % (sys.argv[0]))) + # List of containers, stored as dictionaries containers = [] -for container in lxc.list_containers(as_object=True): +for container_name in lxc.list_containers(): + entry = {} + entry['name'] = container_name + + # Apply filter + if args.filter and not re.match(args.filter, container_name): + continue + + # Return before grabbing the object (non-root) + if not args.state and not args.fancy: + containers.append(entry) + continue + + container = lxc.Container(container_name) + # Filter by status if args.state and container.state not in args.state: continue - # Apply filter - if args.filter and not re.match(args.filter, container.name): - continue - - entry = {} - entry['name'] = container.name - # Nothing more is needed if we're not printing some fancy output if not args.fancy: containers.append(entry) From f99c7aa5de9aa1aa7d624380235cbd295a4764b8 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Wed, 5 Dec 2012 10:38:07 -0600 Subject: [PATCH 145/171] lxc-create: refuse to use a custom rootfs (--dir) which already exists. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Serge Hallyn Acked-by: Stéphane Graber --- src/lxc/lxc-create.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in index 650a245a3..3c66bfa7b 100644 --- a/src/lxc/lxc-create.in +++ b/src/lxc/lxc-create.in @@ -313,6 +313,10 @@ if [ -n "$custom_rootfs" ]; then echo "configuration file already specifies a lxc.rootfs" exit 1 fi + if [ -d "$custom_rootfs" ]; then + echo "specified rootfs ($custom_rootfs) already exists. Bailing." + exit 1 + fi echo "lxc.rootfs = $custom_rootfs" >> $lxc_path/$lxc_name/config fi From e29bf450cafa2ce2564aeb0b64d2014c17228407 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 5 Dec 2012 12:33:16 -0500 Subject: [PATCH 146/171] Use LXCPATH and LOCALSTATEDIR instead of hardcoded /var MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dwight Engen Acked-by: Stéphane Graber --- src/lxc/conf.c | 4 ++-- src/tests/containertests.c | 2 +- src/tests/saveconfig.c | 2 +- src/tests/startone.c | 16 ++++++++-------- templates/lxc-altlinux.in | 10 +++++----- templates/lxc-fedora.in | 12 ++++++------ templates/lxc-opensuse.in | 10 +++++----- templates/lxc-ubuntu-cloud.in | 6 +++--- 8 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 45e0b31da..a58f18d80 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1369,8 +1369,8 @@ static int mount_entry_on_absolute_rootfs(struct mntent *mntent, } /* if rootfs->path is a blockdev path, allow container fstab to - * use /var/lib/lxc/CN/rootfs as the target prefix */ - r = snprintf(path, MAXPATHLEN, "/var/lib/lxc/%s/rootfs", lxc_name); + * use $LXCPATH/CN/rootfs as the target prefix */ + r = snprintf(path, MAXPATHLEN, LXCPATH "/%s/rootfs", lxc_name); if (r < 0 || r >= MAXPATHLEN) goto skipvarlib; diff --git a/src/tests/containertests.c b/src/tests/containertests.c index db6942fa2..3ac0fc574 100644 --- a/src/tests/containertests.c +++ b/src/tests/containertests.c @@ -122,7 +122,7 @@ int main(int argc, char *argv[]) goto out; } str = c->config_file_name(c); -#define CONFIGFNAM "/var/lib/lxc/" MYNAME "/config" +#define CONFIGFNAM LXCPATH "/" MYNAME "/config" if (!str || strcmp(str, CONFIGFNAM)) { fprintf(stderr, "%d: got wrong config file name (%s, not %s)\n", __LINE__, str, CONFIGFNAM); goto out; diff --git a/src/tests/saveconfig.c b/src/tests/saveconfig.c index 20afdc52c..21450b288 100644 --- a/src/tests/saveconfig.c +++ b/src/tests/saveconfig.c @@ -92,7 +92,7 @@ int main(int argc, char *argv[]) fprintf(stderr, "%d: failed writing config file /tmp/lxctest1\n", __LINE__); goto out; } - rename("/var/lib/lxc/" MYNAME "/config", "/var/lib/lxc/" MYNAME "/config.bak"); + rename(LXCPATH "/" MYNAME "/config", LXCPATH "/" MYNAME "/config.bak"); if (!c->save_config(c, NULL)) { fprintf(stderr, "%d: failed writing config file\n", __LINE__); goto out; diff --git a/src/tests/startone.c b/src/tests/startone.c index 73c8f00c7..81a6bfbec 100644 --- a/src/tests/startone.c +++ b/src/tests/startone.c @@ -178,21 +178,21 @@ int main(int argc, char *argv[]) goto out; c->stop(c); - ret = system("mkdir -p /var/lib/lxc/lxctest1/rootfs//usr/local/libexec/lxc"); + ret = system("mkdir -p " LXCPATH "/lxctest1/rootfs//usr/local/libexec/lxc"); if (!ret) - ret = system("mkdir -p /var/lib/lxc/lxctest1/rootfs/usr/lib/lxc/"); + ret = system("mkdir -p " LXCPATH "/lxctest1/rootfs/usr/lib/lxc/"); if (!ret) - ret = system("cp src/lxc/lxc-init /var/lib/lxc/lxctest1/rootfs//usr/local/libexec/lxc"); + ret = system("cp src/lxc/lxc-init " LXCPATH "/lxctest1/rootfs//usr/local/libexec/lxc"); if (!ret) - ret = system("cp src/lxc/liblxc.so /var/lib/lxc/lxctest1/rootfs/usr/lib/lxc"); + ret = system("cp src/lxc/liblxc.so " LXCPATH "/lxctest1/rootfs/usr/lib/lxc"); if (!ret) - ret = system("cp src/lxc/liblxc.so /var/lib/lxc/lxctest1/rootfs/usr/lib/lxc/liblxc.so.0"); + ret = system("cp src/lxc/liblxc.so " LXCPATH "/lxctest1/rootfs/usr/lib/lxc/liblxc.so.0"); if (!ret) - ret = system("cp src/lxc/liblxc.so /var/lib/lxc/lxctest1/rootfs/usr/lib"); + ret = system("cp src/lxc/liblxc.so " LXCPATH "/lxctest1/rootfs/usr/lib"); if (!ret) - ret = system("mkdir -p /var/lib/lxc/lxctest1/rootfs/dev/shm"); + ret = system("mkdir -p " LXCPATH "/lxctest1/rootfs/dev/shm"); if (!ret) - ret = system("chroot /var/lib/lxc/lxctest1/rootfs apt-get install --no-install-recommends lxc"); + ret = system("chroot " LXCPATH "/lxctest1/rootfs apt-get install --no-install-recommends lxc"); if (ret) { fprintf(stderr, "%d: failed to installing lxc-init in container\n", __LINE__); goto out; diff --git a/templates/lxc-altlinux.in b/templates/lxc-altlinux.in index abdd3d121..0f5c9010b 100644 --- a/templates/lxc-altlinux.in +++ b/templates/lxc-altlinux.in @@ -26,7 +26,7 @@ #Configurations arch=$(arch) -cache_base=/var/cache/lxc/altlinux/$arch +cache_base=@LOCALSTATEDIR@/cache/lxc/altlinux/$arch default_path=@LXCPATH@ default_profile=default profile_dir=/etc/lxc/profiles @@ -196,7 +196,7 @@ update_altlinux() install_altlinux() { - mkdir -p /var/lock/subsys/ + mkdir -p @LOCALSTATEDIR@/lock/subsys/ ( flock -x 200 if [ $? -ne 0 ]; then @@ -230,7 +230,7 @@ install_altlinux() return 0 - ) 200>/var/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc return $? } @@ -328,7 +328,7 @@ clean() rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 exit 0 - ) 200>/var/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc } usage() @@ -345,7 +345,7 @@ usage: Mandatory args: -n,--name container name, used to as an identifier for that container from now on Optional args: - -p,--path path to where the container rootfs will be created, defaults to /var/lib/lxc. The container config will go under /var/lib/lxc in and case + -p,--path path to where the container rootfs will be created, defaults to @LXCPATH@. The container config will go under @LXCPATH@ in that case -c,--clean clean the cache -R,--release ALTLinux release for the new container. if the host is ALTLinux, then it will defaultto the host's release. -4,--ipv4 specify the ipv4 address to assign to the virtualized interface, eg. 192.168.1.123/24 diff --git a/templates/lxc-fedora.in b/templates/lxc-fedora.in index fe013c5d6..47c703e55 100644 --- a/templates/lxc-fedora.in +++ b/templates/lxc-fedora.in @@ -27,8 +27,8 @@ #Configurations arch=$(arch) -cache_base=/var/cache/lxc/fedora/$arch -default_path=/var/lib/lxc +cache_base=@LOCALSTATEDIR@/cache/lxc/fedora/$arch +default_path=@LXCPATH@ root_password=root # is this fedora? @@ -200,7 +200,7 @@ update_fedora() install_fedora() { - mkdir -p /var/lock/subsys/ + mkdir -p @LOCALSTATEDIR@/lock/subsys/ ( flock -x 200 if [ $? -ne 0 ]; then @@ -234,7 +234,7 @@ install_fedora() return 0 - ) 200>/var/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc return $? } @@ -303,7 +303,7 @@ clean() rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 exit 0 - ) 200>/var/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc } usage() @@ -316,7 +316,7 @@ usage: Mandatory args: -n,--name container name, used to as an identifier for that container from now on Optional args: - -p,--path path to where the container rootfs will be created, defaults to /var/lib/lxc. The container config will go under /var/lib/lxc in that case + -p,--path path to where the container rootfs will be created, defaults to @LXCPATH@. The container config will go under @LXCPATH@ in that case -c,--clean clean the cache -R,--release Fedora release for the new container. if the host is Fedora, then it will defaultto the host's release. -A,--arch NOT USED YET. Define what arch the container will be [i686,x86_64] diff --git a/templates/lxc-opensuse.in b/templates/lxc-opensuse.in index a7b8465c4..4f18526fd 100644 --- a/templates/lxc-opensuse.in +++ b/templates/lxc-opensuse.in @@ -213,9 +213,9 @@ copy_opensuse() install_opensuse() { - cache="/var/cache/lxc/opensuse" + cache="@LOCALSTATEDIR@/cache/lxc/opensuse" rootfs=$1 - mkdir -p /var/lock/subsys/ + mkdir -p @LOCALSTATEDIR@/lock/subsys/ ( flock -x 200 if [ $? -ne 0 ]; then @@ -243,7 +243,7 @@ install_opensuse() return 0 - ) 200>/var/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc return $? } @@ -298,7 +298,7 @@ EOF clean() { - cache="/var/cache/lxc/opensuse" + cache="@LOCALSTATEDIR@/cache/lxc/opensuse" if [ ! -e $cache ]; then exit 0 @@ -316,7 +316,7 @@ clean() rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 exit 0 - ) 200>/var/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc } usage() diff --git a/templates/lxc-ubuntu-cloud.in b/templates/lxc-ubuntu-cloud.in index 300b47dd5..e1e74315c 100644 --- a/templates/lxc-ubuntu-cloud.in +++ b/templates/lxc-ubuntu-cloud.in @@ -262,7 +262,7 @@ type wget # determine the url, tarball, and directory names # download if needed -cache="/var/cache/lxc/cloud-$release" +cache="@LOCALSTATEDIR@/cache/lxc/cloud-$release" mkdir -p $cache @@ -318,7 +318,7 @@ build_root_tgz() trap SIGTERM } -mkdir -p /var/lock/subsys/ +mkdir -p @LOCALSTATEDIR@/lock/subsys/ ( flock -x 200 @@ -395,7 +395,7 @@ EOF echo "If you do not have a meta-data service, this container will likely be useless." fi -) 200>/var/lock/subsys/lxc-ubucloud +) 200>@LOCALSTATEDIR@/lock/subsys/lxc-ubucloud copy_configuration $path $rootfs $name $arch $release From f8ddeaa5be7c883a3d14f977879f6dacaf7a6236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 5 Dec 2012 13:33:03 -0500 Subject: [PATCH 147/171] lxc-archlinux.in: Apply same LXCPATH/LOCALSTATEDIR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lxc-archlinux was apparently left out of the last change, apply the same modification as the other templates. Signed-off-by: Stéphane Graber --- templates/lxc-archlinux.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/lxc-archlinux.in b/templates/lxc-archlinux.in index 639d81a38..0c529ecba 100644 --- a/templates/lxc-archlinux.in +++ b/templates/lxc-archlinux.in @@ -26,10 +26,10 @@ # defaults arch=$(arch) -cache=/var/cache/lxc/arch/${arch} +cache=@LOCALSTATEDIR@/lxc/arch/${arch} lxc_network_type="veth" lxc_network_link="br0" -default_path=/var/lib/lxc +default_path=@LXCPATH@ default_rc_locale="en-US.UTF-8" default_rc_timezone="UTC" host_mirror="http://mirrors.kernel.org/archlinux/\$repo/os/$arch" From f1a3a3ab8feb7374aed232f2d635edc51d2be792 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 5 Dec 2012 15:05:02 -0500 Subject: [PATCH 148/171] make install should create /var/cache/lxc directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dwight Engen Acked-by: Stéphane Graber --- Makefile.am | 1 + lxc.spec.in | 1 + 2 files changed, 2 insertions(+) diff --git a/Makefile.am b/Makefile.am index 863f8fde0..7b3232646 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,6 +11,7 @@ pcdata_DATA = lxc.pc install-data-local: $(MKDIR_P) $(DESTDIR)$(LXCPATH) + $(MKDIR_P) $(DESTDIR)$(localstatedir)/cache/lxc ChangeLog:: @touch ChangeLog diff --git a/lxc.spec.in b/lxc.spec.in index bc69d6863..094c34d5b 100644 --- a/lxc.spec.in +++ b/lxc.spec.in @@ -98,6 +98,7 @@ rm -rf %{buildroot} %{_libdir}/*.so.* %{_libdir}/%{name} @LXCPATH@ +%{_localstatedir}/cache/lxc %attr(4555,root,root) %{_libexecdir}/%{name}/lxc-init %files devel From 483d21ff46cda0dc4f53672c02e48404535d56df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 5 Dec 2012 17:07:01 -0500 Subject: [PATCH 149/171] oracle template: fixes when using fedora host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let oracle template work when host is fedora or oracle and the lsb_release command is not present. Verify the arch given is valid. Don't add lxc.network section again if already present. Signed-off-by: Dwight Engen Acked-by: Stéphane Graber --- templates/lxc-oracle.in | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in index 1720379af..94961389a 100644 --- a/templates/lxc-oracle.in +++ b/templates/lxc-oracle.in @@ -270,8 +270,8 @@ EOF # see if the network settings were already specified lxc_network_type=`grep '^lxc.network.type' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` if [ -z "$lxc_network_type" -a \ - $host_distribution = "OracleServer" -o \ - $host_distribution = "Fedora" ]; then + \( $host_distribution = "OracleServer" -o \ + $host_distribution = "Fedora" \) ]; then echo "lxc.network.type = veth" >>$cfg_dir/config echo "lxc.network.flags = up" >>$cfg_dir/config echo "lxc.network.link = virbr0" >>$cfg_dir/config @@ -439,7 +439,7 @@ container_rootfs_create() usage() { cat < architecture (ie. i686, x86_64) + -a|--arch= architecture (ie. i386, x86_64) -R|--release= release to download for the new container -u|--url= replace yum repo url (ie. local yum mirror) -t|--templatefs= copy/clone rootfs at path instead of downloading @@ -496,6 +496,12 @@ if [ "$arch" = "i686" ]; then basearch="i386" fi +if [ "$arch" != "i386" -a "$arch" != "x86_64" ]; then + echo "Bad architecture given, check lxc-create" + usage + exit 1 +fi + container_rootfs="$cfg_dir/rootfs" if [ -n "$template_rootfs" ]; then @@ -514,8 +520,20 @@ if which lsb_release >/dev/null 2>&1; then host_release_major=`echo $host_release_version |awk -F '.' '{print $1}'` host_release_minor=`echo $host_release_version |awk -F '.' '{print $2}'` else - echo "Unable to determine host distribution, ensure lsb_release is installed" - exit 1 + if [ -f /etc/fedora-release ]; then + host_distribution="Fedora" + host_release_version=`cat /etc/fedora-release |awk '{print $3}'` + host_release_major=$host_release_version + host_release_minor=0 + elif [ -f /etc/oracle-release ]; then + host_distribution="OracleServer" + host_release_version=`cat /etc/oracle-release |awk '{print $5}'` + host_release_major=`echo $host_release_version |awk -F '.' '{print $1}'` + host_release_minor=`echo $host_release_version |awk -F '.' '{print $2}'` + else + echo "Unable to determine host distribution, ensure lsb_release is installed" + exit 1 + fi fi echo "Host is $host_distribution $host_release_version" From e1483a0275273911b08f8183e31c0fd0565cd11d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 5 Dec 2012 16:47:16 -0500 Subject: [PATCH 150/171] Update README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a section about the staging branch to the README and updates the list of supported architectures. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- README | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README b/README index b81e7a923..d7dacfd7a 100644 --- a/README +++ b/README @@ -29,6 +29,11 @@ Downloading the current source code: You can browse the up to the minute source code and change history online. http://lxc.git.sourceforge.net + For an even more bleeding edge experience, you may want to look at the + staging branch where all changes aimed at the next release land before + getting pulled into the master branch. + http://github.com/lxc/lxc + For detailed build instruction refer to INSTALL and man lxc man page but a short command line should work: ./autogen.sh && ./configure && make && sudo make install && sudo lxc-setcap @@ -48,7 +53,9 @@ Portability: lxc is developed and tested on Linux since kernel mainline version 2.6.27 (without network) and 2.6.29 with network isolation. - is compiled with gcc, and supports i686, x86_64, ppc, ppc64, S390 archi. + It's compiled with gcc, and should work on most architectures as long as the + required kernel features are available. This includes (but isn't limited to): + i686, x86_64, ppc, ppc64, S390, armel and armhf. AUTHOR Daniel Lezcano From 836676caba579f934523aae1c09f2b426f0e45f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 5 Dec 2012 16:47:17 -0500 Subject: [PATCH 151/171] Minor documentation updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update COPYING to the current copy of the LPGL-2.1 license from common-licences (only difference is some indentation). - Remove mixed tabs/spaces in CONTRIBUTING - Make INSTALL fit on 79 cols. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- CONTRIBUTING | 7 +++---- COPYING | 14 ++++++-------- INSTALL | 4 ++-- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/CONTRIBUTING b/CONTRIBUTING index 10c66b1fc..f6447c12c 100644 --- a/CONTRIBUTING +++ b/CONTRIBUTING @@ -1,5 +1,5 @@ - Contributing to this project + Contributing to this project ---------------------------- @@ -81,7 +81,6 @@ By making a contribution to this project, I certify that: then you just add a line saying - Signed-off-by: Random J Developer + Signed-off-by: Random J Developer -using your real name (sorry, no pseudonyms or anonymous -contributions.) \ No newline at end of file +using your real name (sorry, no pseudonyms or anonymous contributions.) diff --git a/COPYING b/COPYING index 602bfc946..4362b4915 100644 --- a/COPYING +++ b/COPYING @@ -1,5 +1,5 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -10,7 +10,7 @@ as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] - Preamble + Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public @@ -112,7 +112,7 @@ modification follow. Pay close attention to the difference between a former contains code derived from the library, whereas the latter must be combined with the library in order to run. - GNU LESSER GENERAL PUBLIC LICENSE + GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other @@ -432,7 +432,7 @@ decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - NO WARRANTY + NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. @@ -455,7 +455,7 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - END OF TERMS AND CONDITIONS + END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries @@ -500,5 +500,3 @@ necessary. Here is a sample; alter the names: Ty Coon, President of Vice That's all there is to it! - - diff --git a/INSTALL b/INSTALL index 6970e369b..aad607199 100644 --- a/INSTALL +++ b/INSTALL @@ -10,8 +10,8 @@ unlimited permission to copy, distribute and modify it. Basic Installation ================== -Briefly, the shell commands `./autogen.sh; ./configure; make; make install' should -configure, build, and install this package. The following +Briefly, the shell commands `./autogen.sh; ./configure; make; make install' +should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. From eba7df9ee0a1963984ef212e7ddfc0e0835af288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 5 Dec 2012 16:47:18 -0500 Subject: [PATCH 152/171] templates: Make generated config consistent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This updates all the templates and the configuration files to consistently use "key = value" everywhere. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- config/lxc.conf.libvirt | 6 +++--- config/lxc.conf.ubuntu | 6 +++--- config/lxc.conf.unknown | 2 +- templates/lxc-altlinux.in | 4 ++-- templates/lxc-archlinux.in | 18 +++++++++--------- templates/lxc-busybox.in | 6 +++--- templates/lxc-debian.in | 4 ++-- templates/lxc-fedora.in | 2 +- templates/lxc-lenny.in | 4 ++-- templates/lxc-opensuse.in | 2 +- templates/lxc-oracle.in | 2 +- templates/lxc-sshd.in | 18 +++++++++--------- templates/lxc-ubuntu | 2 +- templates/lxc-ubuntu-cloud.in | 2 +- 14 files changed, 39 insertions(+), 39 deletions(-) diff --git a/config/lxc.conf.libvirt b/config/lxc.conf.libvirt index 71286190e..6950dca9d 100644 --- a/config/lxc.conf.libvirt +++ b/config/lxc.conf.libvirt @@ -1,3 +1,3 @@ -lxc.network.type=veth -lxc.network.link=virbr0 -lxc.network.flags=up +lxc.network.type = veth +lxc.network.link = virbr0 +lxc.network.flags = up diff --git a/config/lxc.conf.ubuntu b/config/lxc.conf.ubuntu index d2ac1678a..0a5ac711f 100644 --- a/config/lxc.conf.ubuntu +++ b/config/lxc.conf.ubuntu @@ -1,3 +1,3 @@ -lxc.network.type=veth -lxc.network.link=lxcbr0 -lxc.network.flags=up +lxc.network.type = veth +lxc.network.link = lxcbr0 +lxc.network.flags = up diff --git a/config/lxc.conf.unknown b/config/lxc.conf.unknown index 16fa9d662..6c880103f 100644 --- a/config/lxc.conf.unknown +++ b/config/lxc.conf.unknown @@ -1 +1 @@ -lxc.network.type=empty +lxc.network.type = empty diff --git a/templates/lxc-altlinux.in b/templates/lxc-altlinux.in index 0f5c9010b..174af6abe 100644 --- a/templates/lxc-altlinux.in +++ b/templates/lxc-altlinux.in @@ -244,7 +244,7 @@ copy_configuration() lxc.utsname = $name lxc.tty = 4 lxc.pts = 1024 -lxc.mount = $config_path/fstab +lxc.mount = $config_path/fstab # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined @@ -461,4 +461,4 @@ if [ ! -z $clean ]; then exit 0 fi echo "container rootfs and config created" -echo "container is configured for lxc.network.type=veth and lxc.network.link=virbr0 (which is default if you have libvirt runnig)" +echo "network configured as $lxc_network_type in the $lxc_network_link" diff --git a/templates/lxc-archlinux.in b/templates/lxc-archlinux.in index 0c529ecba..5db5b8526 100644 --- a/templates/lxc-archlinux.in +++ b/templates/lxc-archlinux.in @@ -220,20 +220,20 @@ function copy_configuration { mkdir -p "${config_path}" grep -q "^lxc.rootfs" "${config_path}/config" 2>/dev/null || echo "lxc.rootfs=${rootfs_path}" >> "${config_path}/config" cat > "${config_path}/config" << EOF -lxc.utsname=${name} -lxc.tty=4 -lxc.pts=1024 -lxc.mount=${config_path}/fstab +lxc.utsname = ${name} +lxc.tty = 4 +lxc.pts = 1024 +lxc.mount = ${config_path}/fstab # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined #networking -lxc.network.type=${lxc_network_type} -lxc.network.flags=up -lxc.network.link=${lxc_network_link} -lxc.network.name=eth0 -lxc.network.mtu=1500 +lxc.network.type = ${lxc_network_type} +lxc.network.flags = up +lxc.network.link = ${lxc_network_link} +lxc.network.name = eth0 +lxc.network.mtu = 1500 #cgroups lxc.cgroup.devices.deny = a # /dev/null and zero diff --git a/templates/lxc-busybox.in b/templates/lxc-busybox.in index 91095a391..a8af4b23e 100644 --- a/templates/lxc-busybox.in +++ b/templates/lxc-busybox.in @@ -211,8 +211,8 @@ EOF if [ -d "$rootfs/lib" ]; then cat <> $path/config -lxc.mount.entry=/lib $rootfs/lib none ro,bind 0 0 -lxc.mount.entry=/usr/lib $rootfs/usr/lib none ro,bind 0 0 +lxc.mount.entry = /lib $rootfs/lib none ro,bind 0 0 +lxc.mount.entry = /usr/lib $rootfs/usr/lib none ro,bind 0 0 EOF fi @@ -224,7 +224,7 @@ fi for dir in $libdirs; do if [ -d "/$dir" ] && [ -d "$rootfs/$dir" ]; then - echo "lxc.mount.entry=/$dir $dir none ro,bind 0 0" >> $path/config + echo "lxc.mount.entry = /$dir $dir none ro,bind 0 0" >> $path/config fi done } diff --git a/templates/lxc-debian.in b/templates/lxc-debian.in index 889852f98..56279a8b9 100644 --- a/templates/lxc-debian.in +++ b/templates/lxc-debian.in @@ -229,8 +229,8 @@ lxc.cgroup.devices.allow = c 5:2 rwm lxc.cgroup.devices.allow = c 254:0 rwm # mounts point -lxc.mount.entry=proc proc proc nodev,noexec,nosuid 0 0 -lxc.mount.entry=sysfs sys sysfs defaults 0 0 +lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0 +lxc.mount.entry = sysfs sys sysfs defaults 0 0 EOF if [ $? -ne 0 ]; then diff --git a/templates/lxc-fedora.in b/templates/lxc-fedora.in index 47c703e55..f93edc9b0 100644 --- a/templates/lxc-fedora.in +++ b/templates/lxc-fedora.in @@ -248,7 +248,7 @@ copy_configuration() lxc.utsname = $name lxc.tty = 4 lxc.pts = 1024 -lxc.mount = $config_path/fstab +lxc.mount = $config_path/fstab # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined diff --git a/templates/lxc-lenny.in b/templates/lxc-lenny.in index 46de1eaeb..179272601 100644 --- a/templates/lxc-lenny.in +++ b/templates/lxc-lenny.in @@ -204,8 +204,8 @@ lxc.cgroup.devices.allow = c 5:2 rwm lxc.cgroup.devices.allow = c 254:0 rwm # mounts point -lxc.mount.entry=proc proc proc nodev,noexec,nosuid 0 0 -lxc.mount.entry=sysfs sys sysfs defaults 0 0 +lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0 +lxc.mount.entry = sysfs sys sysfs defaults 0 0 EOF if [ $? -ne 0 ]; then diff --git a/templates/lxc-opensuse.in b/templates/lxc-opensuse.in index 4f18526fd..f6bc31c3c 100644 --- a/templates/lxc-opensuse.in +++ b/templates/lxc-opensuse.in @@ -260,7 +260,7 @@ lxc.utsname = $name lxc.tty = 4 lxc.pts = 1024 -lxc.mount = $path/fstab +lxc.mount = $path/fstab # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in index 94961389a..914a80ea3 100644 --- a/templates/lxc-oracle.in +++ b/templates/lxc-oracle.in @@ -263,7 +263,7 @@ lxc.devttydir = lxc lxc.tty = 4 lxc.pts = 1024 lxc.rootfs = $container_rootfs -lxc.mount = $cfg_dir/fstab +lxc.mount = $cfg_dir/fstab # Networking EOF diff --git a/templates/lxc-sshd.in b/templates/lxc-sshd.in index 7ba642d66..ba64cbf29 100644 --- a/templates/lxc-sshd.in +++ b/templates/lxc-sshd.in @@ -116,14 +116,14 @@ lxc.pts = 1024 # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined -lxc.mount.entry=/dev dev none ro,bind 0 0 -lxc.mount.entry=/lib lib none ro,bind 0 0 -lxc.mount.entry=/bin bin none ro,bind 0 0 -lxc.mount.entry=/usr usr none ro,bind 0 0 -lxc.mount.entry=/sbin sbin none ro,bind 0 0 -lxc.mount.entry=tmpfs var/run/sshd tmpfs mode=0644 0 0 -lxc.mount.entry=@LXCTEMPLATEDIR@/lxc-sshd sbin/init none bind 0 0 -lxc.mount.entry=proc $rootfs/proc proc nodev,noexec,nosuid 0 0 +lxc.mount.entry = /dev dev none ro,bind 0 0 +lxc.mount.entry = /lib lib none ro,bind 0 0 +lxc.mount.entry = /bin bin none ro,bind 0 0 +lxc.mount.entry = /usr usr none ro,bind 0 0 +lxc.mount.entry = /sbin sbin none ro,bind 0 0 +lxc.mount.entry = tmpfs var/run/sshd tmpfs mode=0644 0 0 +lxc.mount.entry = @LXCTEMPLATEDIR@/lxc-sshd sbin/init none bind 0 0 +lxc.mount.entry = proc $rootfs/proc proc nodev,noexec,nosuid 0 0 EOF # if no .ipv4 section in config, then have the container run dhcp @@ -131,7 +131,7 @@ EOF if [ "$(uname -m)" = "x86_64" ]; then cat <> $path/config -lxc.mount.entry=/lib64 lib64 none ro,bind 0 0 +lxc.mount.entry = /lib64 lib64 none ro,bind 0 0 EOF fi } diff --git a/templates/lxc-ubuntu b/templates/lxc-ubuntu index 0b55c0356..4ccb01512 100644 --- a/templates/lxc-ubuntu +++ b/templates/lxc-ubuntu @@ -331,7 +331,7 @@ lxc.utsname = $name lxc.devttydir =$ttydir lxc.tty = 4 lxc.pts = 1024 -lxc.mount = $path/fstab +lxc.mount = $path/fstab lxc.arch = $arch lxc.cap.drop = sys_module mac_admin mac_override lxc.pivotdir = lxc_putold diff --git a/templates/lxc-ubuntu-cloud.in b/templates/lxc-ubuntu-cloud.in index e1e74315c..4ef5f8de5 100644 --- a/templates/lxc-ubuntu-cloud.in +++ b/templates/lxc-ubuntu-cloud.in @@ -52,7 +52,7 @@ lxc.utsname = $name lxc.tty = 4 lxc.pts = 1024 -lxc.mount = $path/fstab +lxc.mount = $path/fstab lxc.arch = $arch lxc.cap.drop = sys_module mac_admin lxc.pivotdir = lxc_putold From 14d9c0f09d1a55d124ef210a4b4e205c9fe7596c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 5 Dec 2012 16:47:19 -0500 Subject: [PATCH 153/171] Update for consistent indent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit updates all scripts using mixed indent to a consistent 4 spaces indent. In the past quite a few of those scripts used tabs to instead of 8 spaces or instead of 4 spaces, sometimes mixing those in the same line and sometimes changing the tab width within the same file. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- README | 4 +- runapitests.sh | 18 +-- src/lxc/lxc-checkconfig.in | 12 +- src/lxc/lxc-clone.in | 10 +- src/lxc/lxc-destroy.in | 81 +++++------ src/lxc/lxc-netstat.in | 116 +++++++-------- src/lxc/lxc-ps.in | 149 ++++++++++---------- src/lxc/lxc-setcap.in | 26 ++-- src/lxc/lxc-setuid.in | 30 ++-- templates/lxc-altlinux.in | 107 +++++++------- templates/lxc-busybox.in | 66 +++++---- templates/lxc-debian.in | 98 ++++++------- templates/lxc-fedora.in | 92 ++++++------ templates/lxc-lenny.in | 96 +++++++------ templates/lxc-opensuse.in | 90 ++++++------ templates/lxc-oracle.in | 280 ++++++++++++++++++------------------- templates/lxc-sshd.in | 10 +- 17 files changed, 637 insertions(+), 648 deletions(-) diff --git a/README b/README index d7dacfd7a..9d4360c36 100644 --- a/README +++ b/README @@ -76,10 +76,10 @@ cat > seccomp.full << EOF whitelist EOF for i in `seq 0 300`; do - echo $i >> secomp.full + echo $i >> secomp.full done for i in `seq 1024 1079`; do - echo $i >> seccomp.full + echo $i >> seccomp.full done -- Serge Hallyn Fri, 27 Jul 2012 15:47:02 +0600 diff --git a/runapitests.sh b/runapitests.sh index 65bee6cad..116938719 100644 --- a/runapitests.sh +++ b/runapitests.sh @@ -6,8 +6,8 @@ cleanup() { } if [ `id -u` -ne 0 ]; then - echo "Run as root" - exit 1 + echo "Run as root" + exit 1 fi cat > /etc/lxc/test-busybox.conf << EOF @@ -20,13 +20,13 @@ EOF export LD_LIBRARY_PATH=. TESTS="lxc-test-containertests lxc-test-locktests lxc-test-startone" for curtest in $TESTS; do - echo "running $curtest" - ./src/tests/$curtest - if [ $? -ne 0 ]; then - echo "Test $curtest failed. Stopping" - cleanup - exit 1 - fi + echo "running $curtest" + ./src/tests/$curtest + if [ $? -ne 0 ]; then + echo "Test $curtest failed. Stopping" + cleanup + exit 1 + fi done echo "All tests passed" cleanup diff --git a/src/lxc/lxc-checkconfig.in b/src/lxc/lxc-checkconfig.in index d17bb95ac..13dbf3bf6 100644 --- a/src/lxc/lxc-checkconfig.in +++ b/src/lxc/lxc-checkconfig.in @@ -21,13 +21,13 @@ is_enabled() { RES=$? if [ $RES -eq 0 ]; then - $SETCOLOR_SUCCESS && echo "enabled" && $SETCOLOR_NORMAL + $SETCOLOR_SUCCESS && echo "enabled" && $SETCOLOR_NORMAL else - if [ ! -z "$mandatory" -a "$mandatory" = yes ]; then - $SETCOLOR_FAILURE && echo "required" && $SETCOLOR_NORMAL - else - $SETCOLOR_WARNING && echo "missing" && $SETCOLOR_NORMAL - fi + if [ ! -z "$mandatory" -a "$mandatory" = yes ]; then + $SETCOLOR_FAILURE && echo "required" && $SETCOLOR_NORMAL + else + $SETCOLOR_WARNING && echo "missing" && $SETCOLOR_NORMAL + fi fi } diff --git a/src/lxc/lxc-clone.in b/src/lxc/lxc-clone.in index 7c649a6c7..c9cc5c788 100644 --- a/src/lxc/lxc-clone.in +++ b/src/lxc/lxc-clone.in @@ -272,11 +272,11 @@ c=$lxc_path/$lxc_new/config mv ${c} ${c}.old ( while read line; do - if [ "${line:0:18}" = "lxc.network.hwaddr" ]; then - echo "lxc.network.hwaddr= 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" - else - echo "$line" - fi + if [ "${line:0:18}" = "lxc.network.hwaddr" ]; then + echo "lxc.network.hwaddr= 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" + else + echo "$line" + fi done ) < ${c}.old > ${c} rm -f ${c}.old diff --git a/src/lxc/lxc-destroy.in b/src/lxc/lxc-destroy.in index ecff08325..c72f18a33 100644 --- a/src/lxc/lxc-destroy.in +++ b/src/lxc/lxc-destroy.in @@ -54,26 +54,27 @@ eval set -- "$getopt" while true; do case "$1" in - -h|--help) - help - exit 1 - ;; - -n|--name) - shift - lxc_name=$1 - shift - ;; - -f) - force=1 - shift - ;; + -h|--help) + help + exit 1 + ;; + -n|--name) + shift + lxc_name=$1 + shift + ;; + -f) + force=1 + shift + ;; --) - shift - break;; + shift + break + ;; *) - usage - exit 1 - ;; + usage + exit 1 + ;; esac done @@ -96,13 +97,13 @@ fi # make sure the container isn't running lxc-info -n $lxc_name 2>/dev/null | grep -q RUNNING if [ $? -eq 0 ]; then - if [ $force -eq 1 ]; then - lxc-stop -n $lxc_name - lxc-wait -n $lxc_name -s STOPPED - else - echo "$(basename $0): '$lxc_name' is running; aborted" >&2 - exit 1 - fi + if [ $force -eq 1 ]; then + lxc-stop -n $lxc_name + lxc-wait -n $lxc_name -s STOPPED + else + echo "$(basename $0): '$lxc_name' is running; aborted" >&2 + exit 1 + fi fi # Deduce the type of rootfs @@ -110,21 +111,21 @@ fi # else, ignore it. We'll support deletion of others later. rootdev=`grep lxc.rootfs $lxc_path/$lxc_name/config 2>/dev/null | sed -e 's/^[^/]*/\//'` if [ -n "$rootdev" ]; then - if [ -b "$rootdev" -o -h "$rootdev" ]; then - lvdisplay $rootdev > /dev/null 2>&1 - if [ $? -eq 0 ]; then - echo "removing backing store: $rootdev" - lvremove -f $rootdev - fi - elif [ -h "$rootdev" -o -d "$rootdev" ]; then - if which btrfs >/dev/null 2>&1 && - btrfs subvolume list "$rootdev" >/dev/null 2>&1; then - btrfs subvolume delete "$rootdev" - else - # In case rootfs is not under $lxc_path/$lxc_name, remove it - rm -rf --one-file-system --preserve-root $rootdev - fi - fi + if [ -b "$rootdev" -o -h "$rootdev" ]; then + lvdisplay $rootdev > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "removing backing store: $rootdev" + lvremove -f $rootdev + fi + elif [ -h "$rootdev" -o -d "$rootdev" ]; then + if which btrfs >/dev/null 2>&1 && + btrfs subvolume list "$rootdev" >/dev/null 2>&1; then + btrfs subvolume delete "$rootdev" + else + # In case rootfs is not under $lxc_path/$lxc_name, remove it + rm -rf --one-file-system --preserve-root $rootdev + fi + fi fi # recursively remove the container to remove old container configuration diff --git a/src/lxc/lxc-netstat.in b/src/lxc/lxc-netstat.in index 4abe25f82..df18620a7 100644 --- a/src/lxc/lxc-netstat.in +++ b/src/lxc/lxc-netstat.in @@ -18,110 +18,110 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA usage() { - echo "usage: $(basename $0) -n|--name -- [netstat_options]" >&2 + echo "usage: $(basename $0) -n|--name -- [netstat_options]" >&2 } help() { - usage - echo >&2 - echo "Execute 'netstat' for the specified container." >&2 - echo >&2 - echo " --name NAME specify the container name" >&2 - echo " NETSTAT_OPTIONS netstat command options (see \`netstat --help')" >&2 + usage + echo >&2 + echo "Execute 'netstat' for the specified container." >&2 + echo >&2 + echo " --name NAME specify the container name" >&2 + echo " NETSTAT_OPTIONS netstat command options (see \`netstat --help')" >&2 } get_parent_cgroup() { - local hierarchies hierarchy fields subsystems init_cgroup mountpoint + local hierarchies hierarchy fields subsystems init_cgroup mountpoint - parent_cgroup="" + parent_cgroup="" - # Obtain a list of hierarchies that contain one or more subsystems - hierarchies=$(tail -n +2 /proc/cgroups | cut -f 2) + # Obtain a list of hierarchies that contain one or more subsystems + hierarchies=$(tail -n +2 /proc/cgroups | cut -f 2) - # Iterate through the list until a suitable hierarchy is found - for hierarchy in $hierarchies; do - # Obtain information about the init process in the hierarchy - fields=$(grep -E "^$hierarchy:" /proc/1/cgroup | head -n 1) - if [ -z "$fields" ]; then continue; fi - fields=${fields#*:} + # Iterate through the list until a suitable hierarchy is found + for hierarchy in $hierarchies; do + # Obtain information about the init process in the hierarchy + fields=$(grep -E "^$hierarchy:" /proc/1/cgroup | head -n 1) + if [ -z "$fields" ]; then continue; fi + fields=${fields#*:} - # Get a comma-separated list of the hierarchy's subsystems - subsystems=${fields%:*} + # Get a comma-separated list of the hierarchy's subsystems + subsystems=${fields%:*} - # Get the cgroup of the init process in the hierarchy - init_cgroup=${fields#*:} + # Get the cgroup of the init process in the hierarchy + init_cgroup=${fields#*:} - # Get the filesystem mountpoint of the hierarchy - mountpoint=$(grep -E "^cgroup [^ ]+ [^ ]+ ([^ ]+,)?$subsystems(,[^ ]+)? " /proc/self/mounts | cut -d ' ' -f 2) - if [ -z "$mountpoint" ]; then continue; fi + # Get the filesystem mountpoint of the hierarchy + mountpoint=$(grep -E "^cgroup [^ ]+ [^ ]+ ([^ ]+,)?$subsystems(,[^ ]+)? " /proc/self/mounts | cut -d ' ' -f 2) + if [ -z "$mountpoint" ]; then continue; fi - # Return the absolute path to the containers' parent cgroup - # (do not append '/lxc' if the hierarchy contains the 'ns' subsystem) - if [[ ",$subsystems," == *,ns,* ]]; then - parent_cgroup="${mountpoint}${init_cgroup%/}" - else - parent_cgroup="${mountpoint}${init_cgroup%/}/lxc" - fi - break - done + # Return the absolute path to the containers' parent cgroup + # (do not append '/lxc' if the hierarchy contains the 'ns' subsystem) + if [[ ",$subsystems," == *,ns,* ]]; then + parent_cgroup="${mountpoint}${init_cgroup%/}" + else + parent_cgroup="${mountpoint}${init_cgroup%/}/lxc" + fi + break + done } exec="" while true; do - case $1 in - -h|--help) - help; exit 1;; - -n|--name) - name=$2; shift 2;; - --exec) - exec="exec"; shift;; - --) - shift; break;; - *) - break;; - esac + case $1 in + -h|--help) + help; exit 1;; + -n|--name) + name=$2; shift 2;; + --exec) + exec="exec"; shift;; + --) + shift; break;; + *) + break;; + esac done if [ "$(id -u)" != "0" ]; then - echo "$(basename $0): must be run as root" >&2 - exit 1 + echo "$(basename $0): must be run as root" >&2 + exit 1 fi if [ -z "$name" ]; then - usage - exit 1 + usage + exit 1 fi if [ -z "$exec" ]; then - exec @BINDIR@/lxc-unshare -s MOUNT -- $0 -n $name --exec "$@" + exec @BINDIR@/lxc-unshare -s MOUNT -- $0 -n $name --exec "$@" fi lxc-info -n $name 2>&1 | grep -q 'STOPPED' if [ $? -eq 0 ]; then - echo "$(basename $0): container '$name' is not running" >&2 - exit 1 + echo "$(basename $0): container '$name' is not running" >&2 + exit 1 fi get_parent_cgroup if [ ! -d "$parent_cgroup" ]; then - echo "$(basename $0): no cgroup mount point found" >&2 - exit 1 + echo "$(basename $0): no cgroup mount point found" >&2 + exit 1 fi pid=$(head -1 $parent_cgroup/$name/tasks) if [ -z "$pid" ]; then - echo "$(basename $0): no process found for '$name'" >&2 - exit 1 + echo "$(basename $0): no process found for '$name'" >&2 + exit 1 fi tmpdir=$(mktemp -d) if [ -z "$tmpdir" -o ! -d "$tmpdir" ]; then - echo "$(basename $0): unable to create temporary directory" >&2 - exit 1 + echo "$(basename $0): unable to create temporary directory" >&2 + exit 1 fi # Bind mount /proc/$pid/net onto /proc/net before calling 'netstat'. diff --git a/src/lxc/lxc-ps.in b/src/lxc/lxc-ps.in index cf3f1e933..1f45044b4 100644 --- a/src/lxc/lxc-ps.in +++ b/src/lxc/lxc-ps.in @@ -19,125 +19,124 @@ usage() { - echo "usage: $(basename $0) [--lxc | --name NAME] [--] [PS_OPTIONS...]" >&2 + echo "usage: $(basename $0) [--lxc | --name NAME] [--] [PS_OPTIONS...]" >&2 } help() { - usage - echo >&2 - echo "List current processes with container names." >&2 - echo >&2 - echo " --lxc show processes in all containers" >&2 - echo " --name NAME show processes in the specified container" >&2 - echo " (multiple containers can be separated by commas)" >&2 - echo " PS_OPTIONS ps command options (see \`ps --help')" >&2 + usage + echo >&2 + echo "List current processes with container names." >&2 + echo >&2 + echo " --lxc show processes in all containers" >&2 + echo " --name NAME show processes in the specified container" >&2 + echo " (multiple containers can be separated by commas)" >&2 + echo " PS_OPTIONS ps command options (see \`ps --help')" >&2 } get_parent_cgroup() { - local hierarchies hierarchy fields subsystems init_cgroup mountpoint + local hierarchies hierarchy fields subsystems init_cgroup mountpoint - parent_cgroup="" + parent_cgroup="" - # Obtain a list of hierarchies that contain one or more subsystems - hierarchies=$(tail -n +2 /proc/cgroups | cut -f 2) + # Obtain a list of hierarchies that contain one or more subsystems + hierarchies=$(tail -n +2 /proc/cgroups | cut -f 2) - # Iterate through the list until a suitable hierarchy is found - for hierarchy in $hierarchies; do - # Obtain information about the init process in the hierarchy - fields=$(grep -E "^$hierarchy:" /proc/1/cgroup | head -n 1) - if [ -z "$fields" ]; then continue; fi - fields=${fields#*:} + # Iterate through the list until a suitable hierarchy is found + for hierarchy in $hierarchies; do + # Obtain information about the init process in the hierarchy + fields=$(grep -E "^$hierarchy:" /proc/1/cgroup | head -n 1) + if [ -z "$fields" ]; then continue; fi + fields=${fields#*:} - # Get a comma-separated list of the hierarchy's subsystems - subsystems=${fields%:*} + # Get a comma-separated list of the hierarchy's subsystems + subsystems=${fields%:*} - # Get the cgroup of the init process in the hierarchy - init_cgroup=${fields#*:} + # Get the cgroup of the init process in the hierarchy + init_cgroup=${fields#*:} - # Get the filesystem mountpoint of the hierarchy - mountpoint=$(grep -E "^cgroup [^ ]+ [^ ]+ ([^ ]+,)?$subsystems(,[^ ]+)? " /proc/self/mounts | cut -d ' ' -f 2) - if [ -z "$mountpoint" ]; then continue; fi + # Get the filesystem mountpoint of the hierarchy + mountpoint=$(grep -E "^cgroup [^ ]+ [^ ]+ ([^ ]+,)?$subsystems(,[^ ]+)? " /proc/self/mounts | cut -d ' ' -f 2) + if [ -z "$mountpoint" ]; then continue; fi - # Return the absolute path to the containers' parent cgroup - # (do not append '/lxc' if the hierarchy contains the 'ns' subsystem) - if [[ ",$subsystems," == *,ns,* ]]; then - parent_cgroup="${mountpoint}${init_cgroup%/}" - else - parent_cgroup="${mountpoint}${init_cgroup%/}/lxc" - fi - break - done + # Return the absolute path to the containers' parent cgroup + # (do not append '/lxc' if the hierarchy contains the 'ns' subsystem) + if [[ ",$subsystems," == *,ns,* ]]; then + parent_cgroup="${mountpoint}${init_cgroup%/}" + else + parent_cgroup="${mountpoint}${init_cgroup%/}/lxc" + fi + break + done } containers="" list_container_processes=0 while true; do - case $1 in - -h|--help) - help; exit 1;; - -n|--name) - containers=$2; list_container_processes=1; shift 2;; - --lxc) - list_container_processes=1; shift;; - --) - shift; break;; - *) - break;; + case $1 in + -h|--help) + help; exit 1;; + -n|--name) + containers=$2; list_container_processes=1; shift 2;; + --lxc) + list_container_processes=1; shift;; + --) + shift; break;; + *) + break;; esac done if [ "$list_container_processes" -eq "1" ]; then - set -- -e $@ + set -- -e $@ fi get_parent_cgroup if [ ! -d "$parent_cgroup" ]; then - echo "$(basename $0): no cgroup mount point found" >&2 - exit 1 + echo "$(basename $0): no cgroup mount point found" >&2 + exit 1 fi declare -a container_of_pid container_field_width=9 IFS="," if [ -z "$containers" ]; then - containers=( $(find $parent_cgroup -mindepth 1 -maxdepth 1 -type d -printf "%f," 2>/dev/null) ) + containers=( $(find $parent_cgroup -mindepth 1 -maxdepth 1 -type d -printf "%f," 2>/dev/null) ) else - containers=( $containers ) + containers=( $containers ) fi declare -i pid IFS=$'\n' for container in ${containers[@]}; do - if [ "${#container}" -gt "$container_field_width" ]; then - container_field_width=${#container} - fi + if [ "${#container}" -gt "$container_field_width" ]; then + container_field_width=${#container} + fi - if [ -f "$parent_cgroup/$container/tasks" ]; then - while read pid; do - container_of_pid[$pid]=$container - done < "$parent_cgroup/$container/tasks" - fi + if [ -f "$parent_cgroup/$container/tasks" ]; then + while read pid; do + container_of_pid[$pid]=$container + done < "$parent_cgroup/$container/tasks" + fi done declare -i line_pid_end_position while read line; do - if [ -z "$line_pid_end_position" ]; then - if [[ "$line" != *" PID"* ]]; then - echo "$(basename $0): no PID column found in \`ps' output" >&2 - exit 1 - fi + if [ -z "$line_pid_end_position" ]; then + if [[ "$line" != *" PID"* ]]; then + echo "$(basename $0): no PID column found in \`ps' output" >&2 + exit 1 + fi - buffer=${line%" PID"*} - let line_pid_end_position=${#buffer}+4 - printf "%-${container_field_width}s %s\n" "CONTAINER" "$line" - continue - fi + buffer=${line%" PID"*} + let line_pid_end_position=${#buffer}+4 + printf "%-${container_field_width}s %s\n" "CONTAINER" "$line" + continue + fi - buffer=${line:0:$line_pid_end_position} - pid=${buffer##* } - if [ "$list_container_processes" -eq "0" -o ! -z "${container_of_pid[pid]}" ]; then - printf "%-${container_field_width}s %s\n" "${container_of_pid[pid]}" "$line" - fi + buffer=${line:0:$line_pid_end_position} + pid=${buffer##* } + if [ "$list_container_processes" -eq "0" -o ! -z "${container_of_pid[pid]}" ]; then + printf "%-${container_field_width}s %s\n" "${container_of_pid[pid]}" "$line" + fi done < <(ps "$@") - diff --git a/src/lxc/lxc-setcap.in b/src/lxc/lxc-setcap.in index 02c1e0916..fcbe2be31 100644 --- a/src/lxc/lxc-setcap.in +++ b/src/lxc/lxc-setcap.in @@ -97,16 +97,16 @@ while [ $# -gt 0 ]; do opt="$1" shift case "$opt" in - -d) - LXC_DROP_CAPS="yes" - ;; - -h|--help) - help - exit 0 - ;; - --) - break - ;; + -d) + LXC_DROP_CAPS="yes" + ;; + -h|--help) + help + exit 0 + ;; + --) + break + ;; -?) usage_err "unknown option '$opt'" ;; @@ -115,9 +115,9 @@ while [ $# -gt 0 ]; do set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@" ;; *) - usage - exit 1 - ;; + usage + exit 1 + ;; esac done; diff --git a/src/lxc/lxc-setuid.in b/src/lxc/lxc-setuid.in index e6a7b968d..4e92bb0b6 100644 --- a/src/lxc/lxc-setuid.in +++ b/src/lxc/lxc-setuid.in @@ -41,9 +41,9 @@ help() { setuid() { if [ "$1" = "-r" ]; then - chmod -s $2 + chmod -s $2 else - chmod +s $1 + chmod +s $1 fi } @@ -94,16 +94,16 @@ while [ $# -gt 0 ]; do opt="$1" shift case "$opt" in - -d) - LXC_DROP_CAPS="yes" - ;; - -h|--help) - help - exit 0 - ;; - --) - break - ;; + -d) + LXC_DROP_CAPS="yes" + ;; + -h|--help) + help + exit 0 + ;; + --) + break + ;; -?) usage_err "unknown option '$opt'" ;; @@ -112,9 +112,9 @@ while [ $# -gt 0 ]; do set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@" ;; *) - usage - exit 1 - ;; + usage + exit 1 + ;; esac done; diff --git a/templates/lxc-altlinux.in b/templates/lxc-altlinux.in index 174af6abe..eab473c26 100644 --- a/templates/lxc-altlinux.in +++ b/templates/lxc-altlinux.in @@ -17,7 +17,7 @@ # This library 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 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public @@ -151,8 +151,8 @@ download_altlinux() INSTALL_ROOT=$cache/partial mkdir -p $INSTALL_ROOT if [ $? -ne 0 ]; then - echo "Failed to create '$INSTALL_ROOT' directory" - return 1 + echo "Failed to create '$INSTALL_ROOT' directory" + return 1 fi # download a mini altlinux into a cache @@ -166,8 +166,8 @@ download_altlinux() $APT_GET install $PKG_LIST if [ $? -ne 0 ]; then - echo "Failed to download the rootfs, aborting." - return 1 + echo "Failed to download the rootfs, aborting." + return 1 fi mv "$INSTALL_ROOT" "$cache/rootfs" @@ -198,39 +198,37 @@ install_altlinux() { mkdir -p @LOCALSTATEDIR@/lock/subsys/ ( - flock -x 200 - if [ $? -ne 0 ]; then - echo "Cache repository is busy." - return 1 - fi + flock -x 200 + if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 + fi - echo "Checking cache download in $cache/rootfs ... " - if [ ! -e "$cache/rootfs" ]; then - download_altlinux - if [ $? -ne 0 ]; then - echo "Failed to download 'altlinux base'" - return 1 - fi + echo "Checking cache download in $cache/rootfs ... " + if [ ! -e "$cache/rootfs" ]; then + download_altlinux + if [ $? -ne 0 ]; then + echo "Failed to download 'altlinux base'" + return 1 + fi else - echo "Cache found. Updating..." + echo "Cache found. Updating..." update_altlinux - if [ $? -ne 0 ]; then - echo "Failed to update 'altlinux base', continuing with last known good cache" + if [ $? -ne 0 ]; then + echo "Failed to update 'altlinux base', continuing with last known good cache" else echo "Update finished" - fi - fi + fi + fi - echo "Copy $cache/rootfs to $rootfs_path ... " - copy_altlinux - if [ $? -ne 0 ]; then - echo "Failed to copy rootfs" - return 1 - fi - - return 0 - - ) 200>@LOCALSTATEDIR@/lock/subsys/lxc + echo "Copy $cache/rootfs to $rootfs_path ... " + copy_altlinux + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi + return 0 + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc return $? } @@ -302,8 +300,8 @@ sysfs $rootfs_path/sys sysfs defaults 0 0 EOF if [ $? -ne 0 ]; then - echo "Failed to add configuration" - return 1 + echo "Failed to add configuration" + return 1 fi return 0 @@ -313,21 +311,20 @@ clean() { if [ ! -e $cache ]; then - exit 0 + exit 0 fi # lock, so we won't purge while someone is creating a repository ( - flock -x 200 - if [ $? != 0 ]; then - echo "Cache repository is busy." - exit 1 - fi - - echo -n "Purging the download cache for ALTLinux-$release..." - rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 - exit 0 + flock -x 200 + if [ $? != 0 ]; then + echo "Cache repository is busy." + exit 1 + fi + echo -n "Purging the download cache for ALTLinux-$release..." + rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 + exit 0 ) 200>@LOCALSTATEDIR@/lock/subsys/lxc } @@ -370,17 +367,17 @@ eval set -- "$options" while true do case "$1" in - -h|--help) usage $0 && exit 0;; - -p|--path) path=$2; shift 2;; - -n|--name) name=$2; shift 2;; - -P|--profile) profile=$2; shift 2;; - -c|--clean) clean=$2; shift 2;; - -R|--release) release=$2; shift 2;; - -4|--ipv4) ipv4=$2; shift 2;; - -6|--ipv6) ipv6=$2; shift 2;; - -g|--gw) gw=$2; shift 2;; - -d|--dns) dns=$2; shift 2;; - --) shift 1; break ;; + -h|--help) usage $0 && exit 0;; + -p|--path) path=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -P|--profile) profile=$2; shift 2;; + -c|--clean) clean=$2; shift 2;; + -R|--release) release=$2; shift 2;; + -4|--ipv4) ipv4=$2; shift 2;; + -6|--ipv6) ipv6=$2; shift 2;; + -g|--gw) gw=$2; shift 2;; + -d|--dns) dns=$2; shift 2;; + --) shift 1; break ;; *) break ;; esac done diff --git a/templates/lxc-busybox.in b/templates/lxc-busybox.in index a8af4b23e..f2751d8fa 100644 --- a/templates/lxc-busybox.in +++ b/templates/lxc-busybox.in @@ -108,36 +108,34 @@ EOF cat <> $rootfs/usr/share/udhcpc/default.script #!/bin/sh - case "\$1" in - deconfig) - ip addr flush dev \$interface - ;; + deconfig) + ip addr flush dev \$interface + ;; - renew|bound) + renew|bound) + # flush all the routes + if [ -n "\$router" ]; then + ip route del default 2> /dev/null + fi - # flush all the routes - if [ -n "\$router" ]; then - ip route del default 2> /dev/null - fi + # check broadcast + if [ -n "\$broadcast" ]; then + broadcast="broadcast \$broadcast" + fi - # check broadcast - if [ -n "\$broadcast" ]; then - broadcast="broadcast \$broadcast" - fi + # add a new ip address + ip addr add \$ip/\$mask \$broadcast dev \$interface - # add a new ip address - ip addr add \$ip/\$mask \$broadcast dev \$interface + if [ -n "\$router" ]; then + ip route add default via \$router dev \$interface + fi - if [ -n "\$router" ]; then - ip route add default via \$router dev \$interface - fi - - [ -n "\$domain" ] && echo search \$domain > /etc/resolv.conf - for i in \$dns ; do - echo nameserver \$i >> /etc/resolv.conf - done - ;; + [ -n "\$domain" ] && echo search \$domain > /etc/resolv.conf + for i in \$dns ; do + echo nameserver \$i >> /etc/resolv.conf + done + ;; esac exit 0 EOF @@ -154,22 +152,22 @@ configure_busybox() type busybox >/dev/null if [ $? -ne 0 ]; then - echo "busybox executable is not accessible" - return 1 + echo "busybox executable is not accessible" + return 1 fi file $(which busybox) | grep -q "statically linked" if [ $? -ne 0 ]; then - echo "warning : busybox is not statically linked." - echo "warning : The template script may not correctly" - echo "warning : setup the container environment." + echo "warning : busybox is not statically linked." + echo "warning : The template script may not correctly" + echo "warning : setup the container environment." fi # copy busybox in the rootfs cp $(which busybox) $rootfs/bin if [ $? -ne 0 ]; then - echo "failed to copy busybox in the rootfs" - return 1 + echo "failed to copy busybox in the rootfs" + return 1 fi # symlink busybox for the commands it supports @@ -239,8 +237,8 @@ EOF options=$(getopt -o hp:n: -l help,path:,name: -- "$@") if [ $? -ne 0 ]; then - usage $(basename $0) - exit 1 + usage $(basename $0) + exit 1 fi eval set -- "$options" @@ -249,7 +247,7 @@ do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; - -n|--name) name=$2; shift 2;; + -n|--name) name=$2; shift 2;; --) shift 1; break ;; *) break ;; esac diff --git a/templates/lxc-debian.in b/templates/lxc-debian.in index 56279a8b9..6b5f2f95e 100644 --- a/templates/lxc-debian.in +++ b/templates/lxc-debian.in @@ -13,7 +13,7 @@ # This library 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 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public @@ -31,9 +31,9 @@ configure_debian() # squeeze only has /dev/tty and /dev/tty0 by default, # therefore creating missing device nodes for tty1-4. for tty in $(seq 1 4); do - if [ ! -e $rootfs/dev/tty$tty ]; then - mknod $rootfs/dev/tty$tty c 4 $tty - fi + if [ ! -e $rootfs/dev/tty$tty ]; then + mknod $rootfs/dev/tty$tty c 4 $tty + fi done # configure the inittab @@ -78,11 +78,11 @@ EOF # reconfigure some services if [ -z "$LANG" ]; then - chroot $rootfs locale-gen en_US.UTF-8 UTF-8 - chroot $rootfs update-locale LANG=en_US.UTF-8 + chroot $rootfs locale-gen en_US.UTF-8 UTF-8 + chroot $rootfs update-locale LANG=en_US.UTF-8 else - chroot $rootfs locale-gen $LANG $(echo $LANG | cut -d. -f2) - chroot $rootfs update-locale LANG=$LANG + chroot $rootfs locale-gen $LANG $(echo $LANG | cut -d. -f2) + chroot $rootfs update-locale LANG=$LANG fi # remove pointless services in a container @@ -123,18 +123,18 @@ openssh-server # check the mini debian was not already downloaded mkdir -p "$cache/partial-$SUITE-$arch" if [ $? -ne 0 ]; then - echo "Failed to create '$cache/partial-$SUITE-$arch' directory" - return 1 + echo "Failed to create '$cache/partial-$SUITE-$arch' directory" + return 1 fi # download a mini debian into a cache echo "Downloading debian minimal ..." debootstrap --verbose --variant=minbase --arch=$arch \ - --include=$packages \ - "$SUITE" "$cache/partial-$SUITE-$arch" $MIRROR + --include=$packages \ + "$SUITE" "$cache/partial-$SUITE-$arch" $MIRROR if [ $? -ne 0 ]; then - echo "Failed to download the rootfs, aborting." - return 1 + echo "Failed to download the rootfs, aborting." + return 1 fi mv "$1/partial-$SUITE-$arch" "$1/rootfs-$SUITE-$arch" @@ -166,32 +166,32 @@ install_debian() rootfs=$1 mkdir -p @LOCALSTATEDIR@/lock/subsys/ ( - flock -x 200 - if [ $? -ne 0 ]; then - echo "Cache repository is busy." - return 1 - fi + flock -x 200 + if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 + fi - arch=$(dpkg --print-architecture) + arch=$(dpkg --print-architecture) - echo "Checking cache download in $cache/rootfs-$SUITE-$arch ... " - if [ ! -e "$cache/rootfs-$SUITE-$arch" ]; then - download_debian $cache $arch - if [ $? -ne 0 ]; then - echo "Failed to download 'debian base'" - return 1 - fi - fi + echo "Checking cache download in $cache/rootfs-$SUITE-$arch ... " + if [ ! -e "$cache/rootfs-$SUITE-$arch" ]; then + download_debian $cache $arch + if [ $? -ne 0 ]; then + echo "Failed to download 'debian base'" + return 1 + fi + fi - copy_debian $cache $arch $rootfs - if [ $? -ne 0 ]; then - echo "Failed to copy rootfs" - return 1 - fi + copy_debian $cache $arch $rootfs + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi - return 0 + return 0 - ) 200>@LOCALSTATEDIR@/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc return $? } @@ -234,8 +234,8 @@ lxc.mount.entry = sysfs sys sysfs defaults 0 0 EOF if [ $? -ne 0 ]; then - echo "Failed to add configuration" - return 1 + echo "Failed to add configuration" + return 1 fi return 0 @@ -246,20 +246,20 @@ clean() cache="@LOCALSTATEDIR@/cache/lxc/debian" if [ ! -e $cache ]; then - exit 0 + exit 0 fi # lock, so we won't purge while someone is creating a repository ( - flock -x 200 - if [ $? != 0 ]; then - echo "Cache repository is busy." - exit 1 - fi + flock -x 200 + if [ $? != 0 ]; then + echo "Cache repository is busy." + exit 1 + fi - echo -n "Purging the download cache..." - rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 - exit 0 + echo -n "Purging the download cache..." + rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 + exit 0 ) 200>@LOCALSTATEDIR@/lock/subsys/lxc } @@ -275,7 +275,7 @@ EOF options=$(getopt -o hp:n:c -l help,path:,name:,clean -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) - exit 1 + exit 1 fi eval set -- "$options" @@ -284,8 +284,8 @@ do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; - -n|--name) name=$2; shift 2;; - -c|--clean) clean=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -c|--clean) clean=$2; shift 2;; --) shift 1; break ;; *) break ;; esac diff --git a/templates/lxc-fedora.in b/templates/lxc-fedora.in index f93edc9b0..bc4b264a7 100644 --- a/templates/lxc-fedora.in +++ b/templates/lxc-fedora.in @@ -131,8 +131,8 @@ download_fedora() INSTALL_ROOT=$cache/partial mkdir -p $INSTALL_ROOT if [ $? -ne 0 ]; then - echo "Failed to create '$INSTALL_ROOT' directory" - return 1 + echo "Failed to create '$INSTALL_ROOT' directory" + return 1 fi # download a mini fedora into a cache @@ -170,8 +170,8 @@ download_fedora() $YUM install $PKG_LIST if [ $? -ne 0 ]; then - echo "Failed to download the rootfs, aborting." - return 1 + echo "Failed to download the rootfs, aborting." + return 1 fi mv "$INSTALL_ROOT" "$cache/rootfs" @@ -202,39 +202,38 @@ install_fedora() { mkdir -p @LOCALSTATEDIR@/lock/subsys/ ( - flock -x 200 - if [ $? -ne 0 ]; then - echo "Cache repository is busy." - return 1 - fi + flock -x 200 + if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 + fi - echo "Checking cache download in $cache/rootfs ... " - if [ ! -e "$cache/rootfs" ]; then - download_fedora - if [ $? -ne 0 ]; then - echo "Failed to download 'fedora base'" - return 1 - fi + echo "Checking cache download in $cache/rootfs ... " + if [ ! -e "$cache/rootfs" ]; then + download_fedora + if [ $? -ne 0 ]; then + echo "Failed to download 'fedora base'" + return 1 + fi else - echo "Cache found. Updating..." + echo "Cache found. Updating..." update_fedora - if [ $? -ne 0 ]; then - echo "Failed to update 'fedora base', continuing with last known good cache" + if [ $? -ne 0 ]; then + echo "Failed to update 'fedora base', continuing with last known good cache" else echo "Update finished" - fi - fi + fi + fi - echo "Copy $cache/rootfs to $rootfs_path ... " - copy_fedora - if [ $? -ne 0 ]; then - echo "Failed to copy rootfs" - return 1 - fi + echo "Copy $cache/rootfs to $rootfs_path ... " + copy_fedora + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi - return 0 - - ) 200>@LOCALSTATEDIR@/lock/subsys/lxc + return 0 + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc return $? } @@ -277,8 +276,8 @@ proc proc proc nodev,noexec,nosuid 0 0 sysfs sys sysfs defaults 0 0 EOF if [ $? -ne 0 ]; then - echo "Failed to add configuration" - return 1 + echo "Failed to add configuration" + return 1 fi return 0 @@ -288,21 +287,20 @@ clean() { if [ ! -e $cache ]; then - exit 0 + exit 0 fi # lock, so we won't purge while someone is creating a repository ( - flock -x 200 - if [ $? != 0 ]; then - echo "Cache repository is busy." - exit 1 - fi - - echo -n "Purging the download cache for Fedora-$release..." - rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 - exit 0 + flock -x 200 + if [ $? != 0 ]; then + echo "Cache repository is busy." + exit 1 + fi + echo -n "Purging the download cache for Fedora-$release..." + rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 + exit 0 ) 200>@LOCALSTATEDIR@/lock/subsys/lxc } @@ -335,12 +333,12 @@ eval set -- "$options" while true do case "$1" in - -h|--help) usage $0 && exit 0;; - -p|--path) path=$2; shift 2;; - -n|--name) name=$2; shift 2;; - -c|--clean) clean=$2; shift 2;; + -h|--help) usage $0 && exit 0;; + -p|--path) path=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -c|--clean) clean=$2; shift 2;; -R|--release) release=$2; shift 2;; - --) shift 1; break ;; + --) shift 1; break ;; *) break ;; esac done diff --git a/templates/lxc-lenny.in b/templates/lxc-lenny.in index 179272601..afc93214c 100644 --- a/templates/lxc-lenny.in +++ b/templates/lxc-lenny.in @@ -67,11 +67,11 @@ EOF # reconfigure some services if [ -z "$LANG" ]; then - chroot $rootfs locale-gen en_US.UTF-8 - chroot $rootfs update-locale LANG=en_US.UTF-8 + chroot $rootfs locale-gen en_US.UTF-8 + chroot $rootfs update-locale LANG=en_US.UTF-8 else - chroot $rootfs locale-gen $LANG - chroot $rootfs update-locale LANG=$LANG + chroot $rootfs locale-gen $LANG + chroot $rootfs update-locale LANG=$LANG fi # remove pointless services in a container @@ -104,18 +104,19 @@ openssh-server # check the mini debian was not already downloaded mkdir -p "$cache/partial-$SUITE-$arch" if [ $? -ne 0 ]; then - echo "Failed to create '$cache/partial-$SUITE-$arch' directory" - return 1 + echo "Failed to create '$cache/partial-$SUITE-$arch' directory" + return 1 fi # download a mini debian into a cache echo "Downloading debian minimal ..." debootstrap --verbose --variant=minbase --arch=$arch \ - --include $packages \ - "$SUITE" "$cache/partial-$SUITE-$arch" http://ftp.debian.org/debian + --include $packages \ + "$SUITE" "$cache/partial-$SUITE-$arch" \ + http://ftp.debian.org/debian if [ $? -ne 0 ]; then - echo "Failed to download the rootfs, aborting." - return 1 + echo "Failed to download the rootfs, aborting." + return 1 fi mv "$1/partial-$SUITE-$arch" "$1/rootfs-$SUITE-$arch" @@ -142,32 +143,30 @@ install_debian() rootfs=$1 mkdir -p @LOCALSTATEDIR@/lock/subsys/ ( - flock -x 200 - if [ $? -ne 0 ]; then - echo "Cache repository is busy." - return 1 - fi + flock -x 200 + if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 + fi - arch=$(dpkg --print-architecture) + arch=$(dpkg --print-architecture) - echo "Checking cache download in $cache/rootfs-$SUITE-$arch ... " - if [ ! -e "$cache/rootfs-$SUITE-$arch" ]; then - download_debian $cache $arch - if [ $? -ne 0 ]; then - echo "Failed to download 'debian base'" - return 1 - fi - fi + echo "Checking cache download in $cache/rootfs-$SUITE-$arch ... " + if [ ! -e "$cache/rootfs-$SUITE-$arch" ]; then + download_debian $cache $arch + if [ $? -ne 0 ]; then + echo "Failed to download 'debian base'" + return 1 + fi + fi - copy_debian $cache $arch $rootfs - if [ $? -ne 0 ]; then - echo "Failed to copy rootfs" - return 1 - fi - - return 0 - - ) 200>@LOCALSTATEDIR@/lock/subsys/lxc + copy_debian $cache $arch $rootfs + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi + return 0 + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc return $? } @@ -209,8 +208,8 @@ lxc.mount.entry = sysfs sys sysfs defaults 0 0 EOF if [ $? -ne 0 ]; then - echo "Failed to add configuration" - return 1 + echo "Failed to add configuration" + return 1 fi return 0 @@ -221,21 +220,20 @@ clean() cache="@LOCALSTATEDIR@/cache/lxc/$SUITE" if [ ! -e $cache ]; then - exit 0 + exit 0 fi # lock, so we won't purge while someone is creating a repository ( - flock -x 200 - if [ $? != 0 ]; then - echo "Cache repository is busy." - exit 1 - fi - - echo -n "Purging the download cache..." - rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 - exit 0 + flock -x 200 + if [ $? != 0 ]; then + echo "Cache repository is busy." + exit 1 + fi + echo -n "Purging the download cache..." + rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 + exit 0 ) 200>@LOCALSTATEDIR@/lock/subsys/lxc } @@ -249,8 +247,8 @@ EOF options=$(getopt -o hp:n:c -l help,path:,name:,clean -- "$@") if [ $? -ne 0 ]; then - usage $(basename $0) - exit 1 + usage $(basename $0) + exit 1 fi eval set -- "$options" @@ -259,8 +257,8 @@ do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; - -n|--name) name=$2; shift 2;; - -c|--clean) clean=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -c|--clean) clean=$2; shift 2;; --) shift 1; break ;; *) break ;; esac diff --git a/templates/lxc-opensuse.in b/templates/lxc-opensuse.in index f6bc31c3c..e0f8e0091 100644 --- a/templates/lxc-opensuse.in +++ b/templates/lxc-opensuse.in @@ -18,7 +18,7 @@ # This library 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 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public @@ -142,8 +142,8 @@ download_opensuse() mkdir -p "$cache/partial-$arch" if [ $? -ne 0 ]; then - echo "Failed to create '$cache/partial-$arch' directory" - return 1 + echo "Failed to create '$cache/partial-$arch' directory" + return 1 fi # download a mini opensuse into a cache @@ -187,8 +187,8 @@ EOF rm -f $cache/partial-$arch/etc/mtab ln -sf /proc/self/mounts $cache/partial-$arch/etc/mtab if [ $? -ne 0 ]; then - echo "Failed to download the rootfs, aborting." - return 1 + echo "Failed to download the rootfs, aborting." + return 1 fi rm -fr "$cache/partial-$arch-packages" @@ -217,33 +217,32 @@ install_opensuse() rootfs=$1 mkdir -p @LOCALSTATEDIR@/lock/subsys/ ( - flock -x 200 - if [ $? -ne 0 ]; then - echo "Cache repository is busy." - return 1 - fi + flock -x 200 + if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 + fi - arch=$(arch) + arch=$(arch) - echo "Checking cache download in $cache/rootfs-$arch ... " - if [ ! -e "$cache/rootfs-$arch" ]; then - download_opensuse $cache $arch - if [ $? -ne 0 ]; then - echo "Failed to download 'opensuse base'" - return 1 - fi - fi + echo "Checking cache download in $cache/rootfs-$arch ... " + if [ ! -e "$cache/rootfs-$arch" ]; then + download_opensuse $cache $arch + if [ $? -ne 0 ]; then + echo "Failed to download 'opensuse base'" + return 1 + fi + fi - echo "Copy $cache/rootfs-$arch to $rootfs ... " - copy_opensuse $cache $arch $rootfs - if [ $? -ne 0 ]; then - echo "Failed to copy rootfs" - return 1 - fi + echo "Copy $cache/rootfs-$arch to $rootfs ... " + copy_opensuse $cache $arch $rootfs + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi - return 0 - - ) 200>@LOCALSTATEDIR@/lock/subsys/lxc + return 0 + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc return $? } @@ -289,8 +288,8 @@ sysfs sys sysfs defaults 0 0 EOF if [ $? -ne 0 ]; then - echo "Failed to add configuration" - return 1 + echo "Failed to add configuration" + return 1 fi return 0 @@ -301,21 +300,20 @@ clean() cache="@LOCALSTATEDIR@/cache/lxc/opensuse" if [ ! -e $cache ]; then - exit 0 + exit 0 fi # lock, so we won't purge while someone is creating a repository ( - flock -x 200 - if [ $? != 0 ]; then - echo "Cache repository is busy." - exit 1 - fi - - echo -n "Purging the download cache..." - rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 - exit 0 + flock -x 200 + if [ $? != 0 ]; then + echo "Cache repository is busy." + exit 1 + fi + echo -n "Purging the download cache..." + rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 + exit 0 ) 200>@LOCALSTATEDIR@/lock/subsys/lxc } @@ -337,12 +335,12 @@ eval set -- "$options" while true do case "$1" in - -h|--help) usage $0 && exit 0;; - -p|--path) path=$2; shift 2;; - -n|--name) name=$2; shift 2;; - -c|--clean) clean=$2; shift 2;; - --) shift 1; break ;; - *) break ;; + -h|--help) usage $0 && exit 0;; + -p|--path) path=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -c|--clean) clean=$2; shift 2;; + --) shift 1; break ;; + *) break ;; esac done diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in index 914a80ea3..243f0e490 100644 --- a/templates/lxc-oracle.in +++ b/templates/lxc-oracle.in @@ -41,7 +41,7 @@ is_btrfs_subvolume() { if which btrfs >/dev/null 2>&1 && \ btrfs subvolume list "$1" >/dev/null 2>&1; then - return 0 + return 0 fi return 1 } @@ -59,9 +59,9 @@ container_rootfs_configure() mkdir -p $container_rootfs/selinux echo 0 > $container_rootfs/selinux/enforce if [ -e $container_rootfs/etc/selinux/config ]; then - sed -i 's|SELINUX=enforcing|SELINUX=disabled|' $container_rootfs/etc/selinux/config + sed -i 's|SELINUX=enforcing|SELINUX=disabled|' $container_rootfs/etc/selinux/config else - echo "SELINUX=disabled" >$container_rootfs/etc/selinux/config + echo "SELINUX=disabled" >$container_rootfs/etc/selinux/config fi sed -i 's|session[ ]*required[ ]*pam_selinux.so[ ]*close|#session required pam_selinux.so close|' $container_rootfs/etc/pam.d/login sed -i 's|session[ ]*required[ ]*pam_selinux.so[ ]*open|#session required pam_selinux.so open|' $container_rootfs/etc/pam.d/login @@ -108,15 +108,15 @@ EOF # remove module stuff for iptables it just shows errors that are not # relevant in a container if [ -f "$container_rootfs/etc/sysconfig/iptables-config" ]; then - sed -i 's|IPTABLES_MODULES=".*|IPTABLES_MODULES=""|' $container_rootfs/etc/sysconfig/iptables-config - sed -i 's|IPTABLES_MODULES_UNLOAD=".*|IPTABLES_MODULES_UNLOAD="no"|' $container_rootfs/etc/sysconfig/iptables-config + sed -i 's|IPTABLES_MODULES=".*|IPTABLES_MODULES=""|' $container_rootfs/etc/sysconfig/iptables-config + sed -i 's|IPTABLES_MODULES_UNLOAD=".*|IPTABLES_MODULES_UNLOAD="no"|' $container_rootfs/etc/sysconfig/iptables-config fi # disable readahead in the container if [ $container_release_major = "6" -a -e $container_rootfs/etc/sysconfig/readahead ]; then - rm -f $container_rootfs/etc/init/readahead-collector.conf - rm -f $container_rootfs/etc/init/readahead-disable-services.conf - sed -i 's|READAHEAD="yes"|READAHEAD="no"|' $container_rootfs/etc/sysconfig/readahead + rm -f $container_rootfs/etc/init/readahead-collector.conf + rm -f $container_rootfs/etc/init/readahead-disable-services.conf + sed -i 's|READAHEAD="yes"|READAHEAD="no"|' $container_rootfs/etc/sysconfig/readahead fi # disable udev in the container @@ -125,13 +125,13 @@ EOF # disable nash raidautorun in the container since no /dev/md* if [ $container_release_major = "5" ]; then - sed -i 's|echo "raidautorun /dev/md0"|echo ""|' $container_rootfs/etc/rc.sysinit - sed -i 's|echo "raidautorun /dev/md0"|echo ""|' $container_rootfs/etc/rc.d/rc.sysinit + sed -i 's|echo "raidautorun /dev/md0"|echo ""|' $container_rootfs/etc/rc.sysinit + sed -i 's|echo "raidautorun /dev/md0"|echo ""|' $container_rootfs/etc/rc.d/rc.sysinit fi # prevent rc.sysinit from attempting to loadkeys if [ $container_release_major = "5" -a -e $container_rootfs/etc/sysconfig/keyboard ]; then - rm $container_rootfs/etc/sysconfig/keyboard + rm $container_rootfs/etc/sysconfig/keyboard fi # dont try to sync the hwclock at shutdown @@ -169,13 +169,13 @@ EOF # start a getty on /dev/console, /dev/tty[1-4] if [ $container_release_major = "5" ]; then - sed -i '/1:2345:respawn/i cns:2345:respawn:/sbin/mingetty console' $container_rootfs/etc/inittab - sed -i '/5:2345:respawn/d' $container_rootfs/etc/inittab - sed -i '/6:2345:respawn/d' $container_rootfs/etc/inittab + sed -i '/1:2345:respawn/i cns:2345:respawn:/sbin/mingetty console' $container_rootfs/etc/inittab + sed -i '/5:2345:respawn/d' $container_rootfs/etc/inittab + sed -i '/6:2345:respawn/d' $container_rootfs/etc/inittab fi if [ $container_release_major = "6" ]; then - cat < $container_rootfs/etc/init/console.conf + cat < $container_rootfs/etc/init/console.conf # console - getty # # This service maintains a getty on the console from the point the system is @@ -192,18 +192,18 @@ EOF # there might be other services that are useless but the below set is a good start # some of these might not exist in the image, so we silence chkconfig complaining # about the service file not being found - for service in \ - acpid auditd autofs cpuspeed dund gpm haldaemon hidd \ - ip6tables irqbalance iscsi iscsid isdn kdump kudzu \ - lm_sensors lvm2-monitor mdmonitor microcode_ctl \ - ntpd postfix sendmail udev-post ; + for service in \ + acpid auditd autofs cpuspeed dund gpm haldaemon hidd \ + ip6tables irqbalance iscsi iscsid isdn kdump kudzu \ + lm_sensors lvm2-monitor mdmonitor microcode_ctl \ + ntpd postfix sendmail udev-post ; do - chroot $container_rootfs chkconfig 2>/dev/null $service off + chroot $container_rootfs chkconfig 2>/dev/null $service off done for service in rsyslog ; do - chroot $container_rootfs chkconfig 2>/dev/null $service on + chroot $container_rootfs chkconfig 2>/dev/null $service on done # create required devices. note that /dev/console will be created by lxc @@ -211,8 +211,8 @@ EOF # take care to not nuke /dev in case $container_rootfs isn't set dev_path="$container_rootfs/dev" if [ $container_rootfs != "/" -a -d $dev_path ]; then - rm -rf $dev_path - mkdir -p $dev_path + rm -rf $dev_path + mkdir -p $dev_path fi mknod -m 666 $dev_path/null c 1 3 mknod -m 666 $dev_path/zero c 1 5 @@ -252,8 +252,8 @@ container_config_create() # generate a hwaddr for the container with a high mac address # see http://sourceforge.net/tracker/?func=detail&aid=3411497&group_id=163076&atid=826303 local hwaddr="fe:`dd if=/dev/urandom bs=8 count=1 2>/dev/null |od -t x8 | \ - head -1 |awk '{print $2}' | cut -c1-10 |\ - sed 's/\(..\)/\1:/g; s/.$//'`" + head -1 |awk '{print $2}' | cut -c1-10 |\ + sed 's/\(..\)/\1:/g; s/.$//'`" mkdir -p $cfg_dir || die "unable to create config dir $cfg_dir" cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" # Container configuration for Oracle Linux $release_major.$release_minor @@ -272,9 +272,9 @@ EOF if [ -z "$lxc_network_type" -a \ \( $host_distribution = "OracleServer" -o \ $host_distribution = "Fedora" \) ]; then - echo "lxc.network.type = veth" >>$cfg_dir/config - echo "lxc.network.flags = up" >>$cfg_dir/config - echo "lxc.network.link = virbr0" >>$cfg_dir/config + echo "lxc.network.type = veth" >>$cfg_dir/config + echo "lxc.network.flags = up" >>$cfg_dir/config + echo "lxc.network.link = virbr0" >>$cfg_dir/config fi cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" @@ -304,13 +304,13 @@ EOF container_rootfs_clone() { if is_btrfs_subvolume $template_rootfs; then - # lxc-create already made $container_rootfs a btrfs subvolume, but - # in this case we want to snapshot the original subvolume so we we - # have to delete the one that lxc-create made - btrfs subvolume delete $container_rootfs - btrfs subvolume snapshot $template_rootfs $container_rootfs || die "btrfs clone template" + # lxc-create already made $container_rootfs a btrfs subvolume, but + # in this case we want to snapshot the original subvolume so we we + # have to delete the one that lxc-create made + btrfs subvolume delete $container_rootfs + btrfs subvolume snapshot $template_rootfs $container_rootfs || die "btrfs clone template" else - cp -ax $template_rootfs $container_rootfs || die "copy template" + cp -ax $template_rootfs $container_rootfs || die "copy template" fi } @@ -318,121 +318,121 @@ container_rootfs_create() { cmds="rpm wget yum" if [ $release_major = "5" ]; then - if [ $host_distribution = "Ubuntu" ]; then - db_dump_cmd="db5.1_dump" - db_load_cmd="db4.3_load" - fi - if [ $host_distribution = "OracleServer" -o \ - $host_distribution = "Fedora" ]; then - db_dump_cmd="db_dump" - db_load_cmd="db43_load" - fi + if [ $host_distribution = "Ubuntu" ]; then + db_dump_cmd="db5.1_dump" + db_load_cmd="db4.3_load" + fi + if [ $host_distribution = "OracleServer" -o \ + $host_distribution = "Fedora" ]; then + db_dump_cmd="db_dump" + db_load_cmd="db43_load" + fi - cmds="$cmds $db_dump_cmd $db_load_cmd file" + cmds="$cmds $db_dump_cmd $db_load_cmd file" fi for cmd in $cmds; do - which $cmd >/dev/null 2>&1 - if [ $? -ne 0 ]; then - die "The $cmd command is required, please install it" - fi + which $cmd >/dev/null 2>&1 + if [ $? -ne 0 ]; then + die "The $cmd command is required, please install it" + fi done mkdir -p /var/lock/subsys/ ( - flock -x 200 - if [ $? -ne 0 ]; then - die "The template is busy." - fi + flock -x 200 + if [ $? -ne 0 ]; then + die "The template is busy." + fi - echo "Downloading release $release_major.$release_minor for $basearch" + echo "Downloading release $release_major.$release_minor for $basearch" - # get yum repo file - public_yum_url=http://public-yum.oracle.com - if [ $release_major = "5" ]; then - repofile=public-yum-el5.repo - elif [ $release_major = "6" ]; then - repofile=public-yum-ol6.repo - else - die "Unsupported release $release_major" - fi - mkdir -p $container_rootfs/etc/yum.repos.d - wget -q $public_yum_url/$repofile -O $container_rootfs/etc/yum.repos.d/$repofile - if [ $? -ne 0 ]; then - die "Failed to download repo file $public_yum_url/$repofile" - fi + # get yum repo file + public_yum_url=http://public-yum.oracle.com + if [ $release_major = "5" ]; then + repofile=public-yum-el5.repo + elif [ $release_major = "6" ]; then + repofile=public-yum-ol6.repo + else + die "Unsupported release $release_major" + fi + mkdir -p $container_rootfs/etc/yum.repos.d + wget -q $public_yum_url/$repofile -O $container_rootfs/etc/yum.repos.d/$repofile + if [ $? -ne 0 ]; then + die "Failed to download repo file $public_yum_url/$repofile" + fi - # yum will take $basearch from host, so force the arch we want - sed -i "s|\$basearch|$basearch|" $container_rootfs/etc/yum.repos.d/$repofile + # yum will take $basearch from host, so force the arch we want + sed -i "s|\$basearch|$basearch|" $container_rootfs/etc/yum.repos.d/$repofile - # replace url if they specified one - if [ -n "$repourl" ]; then - sed -i "s|baseurl=http://public-yum.oracle.com/repo|baseurl=$repourl/repo|" $container_rootfs/etc/yum.repos.d/$repofile - sed -i "s|gpgkey=http://public-yum.oracle.com|gpgkey=$repourl|" $container_rootfs/etc/yum.repos.d/$repofile - fi + # replace url if they specified one + if [ -n "$repourl" ]; then + sed -i "s|baseurl=http://public-yum.oracle.com/repo|baseurl=$repourl/repo|" $container_rootfs/etc/yum.repos.d/$repofile + sed -i "s|gpgkey=http://public-yum.oracle.com|gpgkey=$repourl|" $container_rootfs/etc/yum.repos.d/$repofile + fi - # disable all repos, then enable the repo for the version we are installing. - if [ $release_minor = "latest" ]; then - if [ $release_major = "5" ]; then - repo="el"$release_major"_"$release_minor - else - repo="ol"$release_major"_"$release_minor - fi - elif [ $release_minor = "0" ]; then - repo="ol"$release_major"_ga_base" - else - repo="ol"$release_major"_u"$release_minor"_base" - fi - sed -i "s|enabled=1|enabled=0|" $container_rootfs/etc/yum.repos.d/$repofile - sed -i "/\[$repo\]/,/\[/ s/enabled=0/enabled=1/" $container_rootfs/etc/yum.repos.d/$repofile + # disable all repos, then enable the repo for the version we are installing. + if [ $release_minor = "latest" ]; then + if [ $release_major = "5" ]; then + repo="el"$release_major"_"$release_minor + else + repo="ol"$release_major"_"$release_minor + fi + elif [ $release_minor = "0" ]; then + repo="ol"$release_major"_ga_base" + else + repo="ol"$release_major"_u"$release_minor"_base" + fi + sed -i "s|enabled=1|enabled=0|" $container_rootfs/etc/yum.repos.d/$repofile + sed -i "/\[$repo\]/,/\[/ s/enabled=0/enabled=1/" $container_rootfs/etc/yum.repos.d/$repofile - # create rpm db, download and yum install minimal packages - mkdir -p $container_rootfs/var/lib/rpm - rpm --root $container_rootfs --initdb - yum_cmd="yum --installroot $container_rootfs --disablerepo=* --enablerepo=$repo -y --nogpgcheck" - min_pkgs="yum initscripts passwd rsyslog vim-minimal openssh-server dhclient chkconfig rootfiles policycoreutils oraclelinux-release" + # create rpm db, download and yum install minimal packages + mkdir -p $container_rootfs/var/lib/rpm + rpm --root $container_rootfs --initdb + yum_cmd="yum --installroot $container_rootfs --disablerepo=* --enablerepo=$repo -y --nogpgcheck" + min_pkgs="yum initscripts passwd rsyslog vim-minimal openssh-server dhclient chkconfig rootfiles policycoreutils oraclelinux-release" - $yum_cmd install $min_pkgs - if [ $? -ne 0 ]; then - die "Failed to download and install the rootfs, aborting." - fi + $yum_cmd install $min_pkgs + if [ $? -ne 0 ]; then + die "Failed to download and install the rootfs, aborting." + fi - # rsyslog and pam depend on coreutils for some common commands in - # their POSTIN scriptlets, but coreutils wasn't installed yet. now - # that coreutils is installed, reinstall the packages so their POSTIN - # runs right. similarly, libutempter depends on libselinux.so.1 when - # it runs /usr/sbin/groupadd, so reinstall it too - if [ $release_major = "5" ]; then - rpm --root $container_rootfs --nodeps -e rsyslog pam libutempter - $yum_cmd install rsyslog pam libutempter - if [ $? -ne 0 ]; then - die "Unable to reinstall packages" - fi - fi + # rsyslog and pam depend on coreutils for some common commands in + # their POSTIN scriptlets, but coreutils wasn't installed yet. now + # that coreutils is installed, reinstall the packages so their POSTIN + # runs right. similarly, libutempter depends on libselinux.so.1 when + # it runs /usr/sbin/groupadd, so reinstall it too + if [ $release_major = "5" ]; then + rpm --root $container_rootfs --nodeps -e rsyslog pam libutempter + $yum_cmd install rsyslog pam libutempter + if [ $? -ne 0 ]; then + die "Unable to reinstall packages" + fi + fi - # these distributions put the rpm database in a place the guest is - # not expecting it, so move it - if [ $host_distribution = "Ubuntu" ]; then - mv $container_rootfs/root/.rpmdb/* $container_rootfs/var/lib/rpm - fi + # these distributions put the rpm database in a place the guest is + # not expecting it, so move it + if [ $host_distribution = "Ubuntu" ]; then + mv $container_rootfs/root/.rpmdb/* $container_rootfs/var/lib/rpm + fi - # if the native rpm created the db with Hash version 9, we need to - # downgrade it to Hash version 8 for use with OL5.x - db_version=`file $container_rootfs/var/lib/rpm/Packages | \ - grep -o 'version [0-9]*' |awk '{print $2}'` - if [ $release_major = "5" -a $db_version != "8" ]; then - echo "Fixing (downgrading) rpm database from version $db_version" - rm -f $container_rootfs/var/lib/rpm/__db* - for db in $container_rootfs/var/lib/rpm/* ; do - $db_dump_cmd $db |$db_load_cmd $db.new - mv $db.new $db - done - fi + # if the native rpm created the db with Hash version 9, we need to + # downgrade it to Hash version 8 for use with OL5.x + db_version=`file $container_rootfs/var/lib/rpm/Packages | \ + grep -o 'version [0-9]*' |awk '{print $2}'` + if [ $release_major = "5" -a $db_version != "8" ]; then + echo "Fixing (downgrading) rpm database from version $db_version" + rm -f $container_rootfs/var/lib/rpm/__db* + for db in $container_rootfs/var/lib/rpm/* ; do + $db_dump_cmd $db |$db_load_cmd $db.new + mv $db.new $db + done + fi - # the host rpm may not be the same as the guest, rebuild the db with - # the guest rpm version - echo "Rebuilding rpm database" - rm -f $container_rootfs/var/lib/rpm/__db* - chroot $container_rootfs rpm --rebuilddb >/dev/null 2>&1 + # the host rpm may not be the same as the guest, rebuild the db with + # the guest rpm version + echo "Rebuilding rpm database" + rm -f $container_rootfs/var/lib/rpm/__db* + chroot $container_rootfs rpm --rebuilddb >/dev/null 2>&1 ) 200>/var/lock/subsys/lxc-oracle-$name } @@ -461,14 +461,14 @@ eval set -- "$options" while true do case "$1" in - -h|--help) usage $0 && exit 0;; - -p|--path) cfg_dir=$2; shift 2;; - -n|--name) name=$2; shift 2;; - -a|--arch) arch=$2; shift 2;; - -R|--release) release_version=$2; shift 2;; - -u|--url) repourl=$2; shift;; - -t|--templatefs) template_rootfs=$2; shift 2;; - --) shift 1; break ;; + -h|--help) usage $0 && exit 0;; + -p|--path) cfg_dir=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -a|--arch) arch=$2; shift 2;; + -R|--release) release_version=$2; shift 2;; + -u|--url) repourl=$2; shift;; + -t|--templatefs) template_rootfs=$2; shift 2;; + --) shift 1; break ;; *) break ;; esac done diff --git a/templates/lxc-sshd.in b/templates/lxc-sshd.in index ba64cbf29..b704723b4 100644 --- a/templates/lxc-sshd.in +++ b/templates/lxc-sshd.in @@ -43,7 +43,7 @@ $rootfs/lib64" mkdir -p $tree if [ $? -ne 0 ]; then - return 1 + return 1 fi return 0 @@ -172,14 +172,14 @@ if [ $0 == "/sbin/init" ]; then type @LXCINITDIR@/lxc-init if [ $? -ne 0 ]; then - echo "'lxc-init is not accessible on the system" - exit 1 + echo "'lxc-init is not accessible on the system" + exit 1 fi type sshd if [ $? -ne 0 ]; then - echo "'sshd' is not accessible on the system " - exit 1 + echo "'sshd' is not accessible on the system " + exit 1 fi # run dhcp? From 7c382572263726e0d90b9550bc8cf4c2ac014efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 5 Dec 2012 18:51:07 -0500 Subject: [PATCH 154/171] lxc-ubuntu: Don't hardcode path to cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use LOCALSTATEDIR to generate the path to the cache. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- configure.ac | 1 + templates/{lxc-ubuntu => lxc-ubuntu.in} | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) rename templates/{lxc-ubuntu => lxc-ubuntu.in} (99%) diff --git a/configure.ac b/configure.ac index 626d20244..893e179ec 100644 --- a/configure.ac +++ b/configure.ac @@ -245,6 +245,7 @@ AC_CONFIG_FILES([ templates/Makefile templates/lxc-lenny templates/lxc-debian + templates/lxc-ubuntu templates/lxc-ubuntu-cloud templates/lxc-opensuse templates/lxc-busybox diff --git a/templates/lxc-ubuntu b/templates/lxc-ubuntu.in similarity index 99% rename from templates/lxc-ubuntu rename to templates/lxc-ubuntu.in index 4ccb01512..19de99139 100644 --- a/templates/lxc-ubuntu +++ b/templates/lxc-ubuntu.in @@ -258,7 +258,7 @@ install_ubuntu() rootfs=$1 release=$2 flushcache=$3 - cache="/var/cache/lxc/$release" + cache="@LOCALSTATEDIR@/cache/lxc/$release" mkdir -p /var/lock/subsys/ ( From 75350ec8c77acad383eea8e36b2dc3faeea34460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 5 Dec 2012 18:51:08 -0500 Subject: [PATCH 155/171] lxc-archlinux: Don't hardcode /var/lib/lxc in help MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- templates/lxc-archlinux.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/lxc-archlinux.in b/templates/lxc-archlinux.in index 5db5b8526..cf274d4a0 100644 --- a/templates/lxc-archlinux.in +++ b/templates/lxc-archlinux.in @@ -375,7 +375,7 @@ usage: Mandatory args: -n,--name container name, used to as an identifier for that container from now on Optional args: - -p,--path path to where the container rootfs will be created, defaults to /var/lib/lxc. The container config will go under /var/lib/lxc in that case + -p,--path path to where the container rootfs will be created, defaults to $default_path. The container config will go under $default_path in that case -P,--packages preinstall additional packages, comma-separated list -h,--help print this help EOF From fe253caa8b98854445aaf6ee253545ee1f13beb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 5 Dec 2012 18:51:09 -0500 Subject: [PATCH 156/171] templates: Consistent use of locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move to per-template lock (except for oracle that's per-container). Also ensure that the path used for the lock is relative to LOCALSTATEDIR. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- templates/lxc-altlinux.in | 4 ++-- templates/lxc-debian.in | 4 ++-- templates/lxc-fedora.in | 4 ++-- templates/lxc-lenny.in | 4 ++-- templates/lxc-opensuse.in | 4 ++-- templates/lxc-oracle.in | 4 ++-- templates/lxc-ubuntu-cloud.in | 2 +- templates/lxc-ubuntu.in | 4 ++-- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/templates/lxc-altlinux.in b/templates/lxc-altlinux.in index eab473c26..fac545cc3 100644 --- a/templates/lxc-altlinux.in +++ b/templates/lxc-altlinux.in @@ -228,7 +228,7 @@ install_altlinux() return 1 fi return 0 - ) 200>@LOCALSTATEDIR@/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc-altlinux return $? } @@ -325,7 +325,7 @@ clean() echo -n "Purging the download cache for ALTLinux-$release..." rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 exit 0 - ) 200>@LOCALSTATEDIR@/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc-altlinux } usage() diff --git a/templates/lxc-debian.in b/templates/lxc-debian.in index 6b5f2f95e..7bbc46b94 100644 --- a/templates/lxc-debian.in +++ b/templates/lxc-debian.in @@ -191,7 +191,7 @@ install_debian() return 0 - ) 200>@LOCALSTATEDIR@/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc-debian return $? } @@ -261,7 +261,7 @@ clean() rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 exit 0 - ) 200>@LOCALSTATEDIR@/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc-debian } usage() diff --git a/templates/lxc-fedora.in b/templates/lxc-fedora.in index bc4b264a7..366e776c1 100644 --- a/templates/lxc-fedora.in +++ b/templates/lxc-fedora.in @@ -233,7 +233,7 @@ install_fedora() fi return 0 - ) 200>@LOCALSTATEDIR@/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc-fedora return $? } @@ -301,7 +301,7 @@ clean() echo -n "Purging the download cache for Fedora-$release..." rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 exit 0 - ) 200>@LOCALSTATEDIR@/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc-fedora } usage() diff --git a/templates/lxc-lenny.in b/templates/lxc-lenny.in index afc93214c..cb938536c 100644 --- a/templates/lxc-lenny.in +++ b/templates/lxc-lenny.in @@ -166,7 +166,7 @@ install_debian() return 1 fi return 0 - ) 200>@LOCALSTATEDIR@/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc-lenny return $? } @@ -234,7 +234,7 @@ clean() echo -n "Purging the download cache..." rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 exit 0 - ) 200>@LOCALSTATEDIR@/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc-lenny } usage() diff --git a/templates/lxc-opensuse.in b/templates/lxc-opensuse.in index e0f8e0091..65fb7b0f8 100644 --- a/templates/lxc-opensuse.in +++ b/templates/lxc-opensuse.in @@ -242,7 +242,7 @@ install_opensuse() fi return 0 - ) 200>@LOCALSTATEDIR@/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc-opensuse return $? } @@ -314,7 +314,7 @@ clean() echo -n "Purging the download cache..." rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 exit 0 - ) 200>@LOCALSTATEDIR@/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc-opensuse } usage() diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in index 243f0e490..f3252820a 100644 --- a/templates/lxc-oracle.in +++ b/templates/lxc-oracle.in @@ -337,7 +337,7 @@ container_rootfs_create() fi done - mkdir -p /var/lock/subsys/ + mkdir -p @LOCALSTATEDIR@/lock/subsys/lxc ( flock -x 200 if [ $? -ne 0 ]; then @@ -433,7 +433,7 @@ container_rootfs_create() echo "Rebuilding rpm database" rm -f $container_rootfs/var/lib/rpm/__db* chroot $container_rootfs rpm --rebuilddb >/dev/null 2>&1 - ) 200>/var/lock/subsys/lxc-oracle-$name + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc-oracle-$name } usage() diff --git a/templates/lxc-ubuntu-cloud.in b/templates/lxc-ubuntu-cloud.in index 4ef5f8de5..de9f3c31d 100644 --- a/templates/lxc-ubuntu-cloud.in +++ b/templates/lxc-ubuntu-cloud.in @@ -395,7 +395,7 @@ EOF echo "If you do not have a meta-data service, this container will likely be useless." fi -) 200>@LOCALSTATEDIR@/lock/subsys/lxc-ubucloud +) 200>@LOCALSTATEDIR@/lock/subsys/lxc-ubuntu-cloud copy_configuration $path $rootfs $name $arch $release diff --git a/templates/lxc-ubuntu.in b/templates/lxc-ubuntu.in index 19de99139..0ed8808c7 100644 --- a/templates/lxc-ubuntu.in +++ b/templates/lxc-ubuntu.in @@ -259,7 +259,7 @@ install_ubuntu() release=$2 flushcache=$3 cache="@LOCALSTATEDIR@/cache/lxc/$release" - mkdir -p /var/lock/subsys/ + mkdir -p @LOCALSTATEDIR@/lock/subsys/ ( flock -x 200 @@ -293,7 +293,7 @@ install_ubuntu() return 0 - ) 200>/var/lock/subsys/lxc + ) 200>@LOCALSTATEDIR@/lock/subsys/lxc-ubuntu return $? } From 2495cc911b6600521fd2dc735edba15f6fbb9081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 5 Dec 2012 18:51:10 -0500 Subject: [PATCH 157/171] python: Remove hardcoded LXCPATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch the python scripts to using @LXCPATH@. According to grep, this was the last occurence of a /var/*/lxc path in the code. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- configure.ac | 2 ++ src/python-lxc/examples/{api_test.py => api_test.py.in} | 2 +- src/python-lxc/lxc/{__init__.py => __init__.py.in} | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) rename src/python-lxc/examples/{api_test.py => api_test.py.in} (99%) rename src/python-lxc/lxc/{__init__.py => __init__.py.in} (99%) diff --git a/configure.ac b/configure.ac index 893e179ec..ef321ce47 100644 --- a/configure.ac +++ b/configure.ac @@ -271,6 +271,8 @@ AC_CONFIG_FILES([ src/lxc/legacy/lxc-ls src/python-lxc/Makefile + src/python-lxc/lxc/__init__.py + src/python-lxc/examples/api_test.py src/tests/Makefile diff --git a/src/python-lxc/examples/api_test.py b/src/python-lxc/examples/api_test.py.in similarity index 99% rename from src/python-lxc/examples/api_test.py rename to src/python-lxc/examples/api_test.py.in index 3ca02677b..0b17bd6ab 100644 --- a/src/python-lxc/examples/api_test.py +++ b/src/python-lxc/examples/api_test.py.in @@ -30,7 +30,7 @@ import uuid import sys # Some constants -LXC_PATH_LIB = "/var/lib/lxc" +LXC_PATH_LIB = "@LXCPATH@" LXC_TEMPLATE = "ubuntu" # Let's pick a random name, avoiding clashes diff --git a/src/python-lxc/lxc/__init__.py b/src/python-lxc/lxc/__init__.py.in similarity index 99% rename from src/python-lxc/lxc/__init__.py rename to src/python-lxc/lxc/__init__.py.in index cde4fd1e6..f42b9447e 100644 --- a/src/python-lxc/lxc/__init__.py +++ b/src/python-lxc/lxc/__init__.py.in @@ -465,7 +465,7 @@ def list_containers(as_object=False): List the containers on the system. """ containers = [] - for entry in glob.glob("/var/lib/lxc/*/config"): + for entry in glob.glob("@LXCPATH@/*/config"): if as_object: containers.append(Container(entry.split("/")[-2])) else: From 1c6085cdd97cf947237e361245b35c6cdab90357 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Thu, 6 Dec 2012 09:58:21 -0500 Subject: [PATCH 158/171] lxc.spec: add openssl and rsync as Required since both are used in lxc-clone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dwight Engen Acked-by: Stéphane Graber --- lxc.spec.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxc.spec.in b/lxc.spec.in index 094c34d5b..51997b5ab 100644 --- a/lxc.spec.in +++ b/lxc.spec.in @@ -29,7 +29,7 @@ Summary: %{name} : Linux Container Group: Applications/System License: LGPL BuildRoot: %{_tmppath}/%{name}-%{version}-build -Requires: libcap +Requires: libcap openssl rsync BuildRequires: libcap libcap-devel docbook2X %description From ab4a1501761241e1aa97c3eee6742040dc81a049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 7 Dec 2012 10:41:10 -0500 Subject: [PATCH 159/171] lxc-create: Allow for empty or unset template name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This restores an old behaviour where lxc-create can be called without a template. In such case, only a minimal configuration is built and no rootfs is created. However the various backingstore code is still used. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/lxc/lxc-create.in | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in index 3c66bfa7b..5cd9fdba5 100644 --- a/src/lxc/lxc-create.in +++ b/src/lxc/lxc-create.in @@ -284,19 +284,19 @@ if [ ! -r "$lxc_config" ]; then exit 1 fi -# Allow for a path to be provided as the template name -if [ -x "$lxc_template" ]; then - template_path=$lxc_template -else - template_path=${templatedir}/lxc-$lxc_template -fi - -if ! [ -x "$template_path" ]; then - echo "$(basename $0): unknown template '$lxc_template'" >&2 - cleanup -fi - if [ ! -z "$lxc_template" ]; then + # Allow for a path to be provided as the template name + if [ -x "$lxc_template" ]; then + template_path=$lxc_template + else + template_path=${templatedir}/lxc-$lxc_template + fi + + if ! [ -x "$template_path" ]; then + echo "$(basename $0): unknown template '$lxc_template'" >&2 + cleanup + fi + sum=$(sha1sum $template_path | cut -d ' ' -f1) echo "# Template used to create this container: $lxc_template" >> $lxc_path/$lxc_name/config if [ -n "$*" ]; then From 794dd12099da53adec33e8291f0f470629f8b8f6 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Thu, 6 Dec 2012 18:41:15 -0600 Subject: [PATCH 160/171] api: add set_cgroup_item and get_cgroup_item (to c api) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit set_cgroup_item takes a pointer to a running container, a cgroup subsystem name, and a char *value and it mimicks 'lxc-cgroup -n containername subsys value' get_cgroup_item takes a pointer to a running container, a a cgroup subsystem name, a destination value * and the length of the value being sent in, and returns the length of what was read from the cgroup file. If a 0 len is passed in, then the length of the file is returned. So you can do len = c->get_cgroup_item(c, "devices.list", NULL, 0); v = malloc(len+1); ret = c->get_cgroup_item(c, "devices.list", v, len); to read the whole file. This patch also disables the lxc-init part of the startone test, which was failing because lxc-init has been moved due to multiarch issues. The test is salvagable, but saving it was beyond this effort. Signed-off-by: Serge Hallyn Acked-by: Stéphane Graber --- src/lxc/cgroup.c | 20 +++++++++++++++- src/lxc/lxccontainer.c | 52 ++++++++++++++++++++++++++++++++++++++++++ src/lxc/lxccontainer.h | 9 ++++++++ src/tests/startone.c | 47 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 1 deletion(-) diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c index 532d63833..b6c948b91 100644 --- a/src/lxc/cgroup.c +++ b/src/lxc/cgroup.c @@ -789,6 +789,13 @@ out: return ret; } +/* + * If you pass in NULL value or 0 len, then you are asking for the size + * of the file. Note that we can't get the file size quickly through stat + * or lseek. Therefore if you pass in len > 0 but less than the file size, + * your only indication will be that the return value will be equal to the + * passed-in ret. We will not return the actual full file size. + */ int lxc_cgroup_get(const char *name, const char *filename, char *value, size_t len) { @@ -813,7 +820,18 @@ int lxc_cgroup_get(const char *name, const char *filename, return -1; } - ret = read(fd, value, len); + if (!len || !value) { + char buf[100]; + int count = 0; + while ((ret = read(fd, buf, 100)) > 0) + count += ret; + if (ret >= 0) + ret = count; + } else { + memset(value, 0, len); + ret = read(fd, value, len); + } + if (ret < 0) ERROR("read %s : %s", path, strerror(errno)); diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index d25b8486d..1345ab55b 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -163,6 +163,13 @@ static const char *lxcapi_state(struct lxc_container *c) return ret; } +static bool is_stopped_nolock(struct lxc_container *c) +{ + lxc_state_t s; + s = lxc_getstate(c->name); + return (s == STOPPED); +} + static bool lxcapi_is_running(struct lxc_container *c) { const char *s; @@ -850,6 +857,49 @@ static char *lxcapi_config_file_name(struct lxc_container *c) return strdup(c->configfile); } +static bool lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys, const char *value) +{ + int ret; + bool b = false; + + if (!c) + return false; + + if (lxclock(c->privlock, 0)) + return false; + + if (is_stopped_nolock(c)) + goto err; + + ret = lxc_cgroup_set(c->name, subsys, value); + if (!ret) + b = true; +err: + lxcunlock(c->privlock); + return b; +} + +static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, char *retv, int inlen) +{ + int ret = -1; + + if (!c || !c->lxc_conf) + return -1; + + if (lxclock(c->privlock, 0)) + return -1; + + if (is_stopped_nolock(c)) + goto out; + + ret = lxc_cgroup_get(c->name, subsys, retv, inlen); + +out: + lxcunlock(c->privlock); + return ret; +} + + struct lxc_container *lxc_container_new(const char *name) { struct lxc_container *c; @@ -920,6 +970,8 @@ struct lxc_container *lxc_container_new(const char *name) c->shutdown = lxcapi_shutdown; c->clear_config_item = lxcapi_clear_config_item; c->get_config_item = lxcapi_get_config_item; + c->get_cgroup_item = lxcapi_get_cgroup_item; + c->set_cgroup_item = lxcapi_set_cgroup_item; /* we'll allow the caller to update these later */ if (lxc_log_init(NULL, NULL, "lxc_container", 0)) { diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index 9c9296c16..a6fdb2ba6 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -48,6 +48,15 @@ struct lxc_container { * the length which was our would be printed. */ int (*get_config_item)(struct lxc_container *c, const char *key, char *retv, int inlen); int (*get_keys)(struct lxc_container *c, const char *key, char *retv, int inlen); + /* + * get_cgroup_item returns the number of bytes read, or an error (<0). + * If retv NULL or inlen 0 is passed in, then the length of the cgroup + * file will be returned. * Otherwise it will return the # of bytes read. + * If inlen is less than the number of bytes available, then the returned + * value will be inlen, not the full available size of the file. + */ + int (*get_cgroup_item)(struct lxc_container *c, const char *subsys, char *retv, int inlen); + bool (*set_cgroup_item)(struct lxc_container *c, const char *subsys, const char *value); #if 0 bool (*commit_cgroups)(struct lxc_container *c); diff --git a/src/tests/startone.c b/src/tests/startone.c index 81a6bfbec..325942e95 100644 --- a/src/tests/startone.c +++ b/src/tests/startone.c @@ -98,6 +98,8 @@ int main(int argc, char *argv[]) int ret = 0; const char *s; bool b; + char buf[201]; + int len; ret = 1; /* test a real container */ @@ -125,6 +127,19 @@ int main(int argc, char *argv[]) goto out; } + len = c->get_cgroup_item(c, "cpuset.cpus", buf, 200); + if (len >= 0) { + fprintf(stderr, "%d: %s not running but had cgroup settings\n", __LINE__, MYNAME); + goto out; + } + + sprintf(buf, "0"); + b = c->set_cgroup_item(c, "cpuset.cpus", buf); + if (b) { + fprintf(stderr, "%d: %s not running but coudl set cgroup settings\n", __LINE__, MYNAME); + goto out; + } + s = c->state(c); if (!s || strcmp(s, "STOPPED")) { fprintf(stderr, "%d: %s is in state %s, not in STOPPED.\n", __LINE__, c->name, s ? s : "undefined"); @@ -172,12 +187,43 @@ int main(int argc, char *argv[]) goto out; } + len = c->get_cgroup_item(c, "cpuset.cpus", buf, 0); + if (len <= 0) { + fprintf(stderr, "%d: not able to get length of cpuset.cpus (ret %d)\n", __LINE__, len); + goto out; + } + + len = c->get_cgroup_item(c, "cpuset.cpus", buf, 200); + if (len <= 0 || strcmp(buf, "0\n")) { + fprintf(stderr, "%d: not able to get cpuset.cpus (len %d buf %s)\n", __LINE__, len, buf); + goto out; + } + + sprintf(buf, "FROZEN"); + b = c->set_cgroup_item(c, "freezer.state", buf); + if (!b) { + fprintf(stderr, "%d: not able to set freezer.state.\n", __LINE__); + goto out; + } + + sprintf(buf, "XXX"); + len = c->get_cgroup_item(c, "freezer.state", buf, 200); + if (len <= 0 || (strcmp(buf, "FREEZING\n") && strcmp(buf, "FROZEN\n"))) { + fprintf(stderr, "%d: not able to get freezer.state (len %d buf %s)\n", __LINE__, len, buf); + goto out; + } + + c->set_cgroup_item(c, "freezer.state", "THAWED"); + printf("hit return to finish"); ret = scanf("%c", &mychar); if (ret < 0) goto out; c->stop(c); + /* feh - multilib has moved the lxc-init crap */ + goto ok; + ret = system("mkdir -p " LXCPATH "/lxctest1/rootfs//usr/local/libexec/lxc"); if (!ret) ret = system("mkdir -p " LXCPATH "/lxctest1/rootfs/usr/lib/lxc/"); @@ -204,6 +250,7 @@ int main(int argc, char *argv[]) } // auto-check result? ('bobo' is printed on stdout) +ok: fprintf(stderr, "all lxc_container tests passed for %s\n", c->name); ret = 0; From d4f6fa926d92803d8b8217468be483ac2f7e270e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 7 Dec 2012 12:24:23 -0500 Subject: [PATCH 161/171] python3-lxc: Fix build prefix/destdir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/python-lxc/Makefile.am | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/python-lxc/Makefile.am b/src/python-lxc/Makefile.am index 15c61eaa5..9d775c3ec 100644 --- a/src/python-lxc/Makefile.am +++ b/src/python-lxc/Makefile.am @@ -10,7 +10,11 @@ all: CFLAGS="$(CFLAGS) -I ../../src -L../../src/lxc/" $(PYTHON) setup.py build install: - python3 setup.py install --root=$(DESTDIR) --prefix=$(PREFIX) --no-compile $(DISTSETUPOPTS) + if [ "$(DESTDIR)" = "" ]; then \ + $(PYTHON) setup.py install --prefix=$(prefix) --no-compile $(DISTSETUPOPTS); \ + else \ + $(PYTHON) setup.py install --root=$(DESTDIR) --prefix=$(prefix) --no-compile $(DISTSETUPOPTS); \ + fi clean: rm -rf build From f3c7020ad851481d3c95c6deb384326c6cc1a29f Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 7 Dec 2012 14:16:54 -0600 Subject: [PATCH 162/171] dont save loglevel if it is unset Signed-off-by: Serge Hallyn --- src/lxc/confile.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lxc/confile.c b/src/lxc/confile.c index a64ae0940..1d87227b8 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -1674,7 +1674,8 @@ void write_config(FILE *fout, struct lxc_conf *c) if (c->aa_profile) fprintf(fout, "lxc.aa_profile = %s\n", c->aa_profile); #endif - fprintf(fout, "lxc.loglevel = %s\n", lxc_log_priority_to_string(c->loglevel)); + if (c->loglevel != LXC_LOG_PRIORITY_NOTSET) + fprintf(fout, "lxc.loglevel = %s\n", lxc_log_priority_to_string(c->loglevel)); if (c->logfile) fprintf(fout, "lxc.logfile = %s\n", c->logfile); lxc_list_for_each(it, &c->cgroup) { From 703c562d2ee8572097135003964c7ca7931567c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 7 Dec 2012 15:47:10 -0500 Subject: [PATCH 163/171] python: get_keys() doesn't require a path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The python binding was forcing the user to pass a base path to get_keys() even though the C binding doesn't require it. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/python-lxc/lxc/__init__.py.in | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/python-lxc/lxc/__init__.py.in b/src/python-lxc/lxc/__init__.py.in index f42b9447e..9ef3459f1 100644 --- a/src/python-lxc/lxc/__init__.py.in +++ b/src/python-lxc/lxc/__init__.py.in @@ -400,11 +400,14 @@ class Container(_lxc.Container): os.remove(path) return ips - def get_keys(self, key): + def get_keys(self, key=None): """ Returns a list of valid sub-keys. """ - value = _lxc.Container.get_keys(self, key) + if key: + value = _lxc.Container.get_keys(self, key) + else: + value = _lxc.Container.get_keys(self) if value is False: return False From f4d3a9fddbacdd5279f042cc352425a0c01da2b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 7 Dec 2012 15:47:11 -0500 Subject: [PATCH 164/171] python: Add binding for {get|set}_cgroup_item MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates the binding for the two new functions. This also fixes some problems with the argument checking of get_config_item that'd otherwise lead to a segfault. The python binding for set_cgroup_item and get_cgroup_item are pretty raw as lxc has little control over the cgroup entries. That means that we don't try to interpret lists as we do for the config entries. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/python-lxc/examples/api_test.py.in | 7 ++++ src/python-lxc/lxc.c | 55 +++++++++++++++++++++++++- src/python-lxc/lxc/__init__.py.in | 12 ++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/python-lxc/examples/api_test.py.in b/src/python-lxc/examples/api_test.py.in index 0b17bd6ab..7711291ee 100644 --- a/src/python-lxc/examples/api_test.py.in +++ b/src/python-lxc/examples/api_test.py.in @@ -97,6 +97,13 @@ container.attach("NETWORK|UTSNAME", "/sbin/ifconfig", "eth0") # A few basic checks of the current state assert(len(ips) > 0) +## Testing cgroups a bit +print("Testing cgroup API") +max_mem = container.get_cgroup_item("memory.max_usage_in_bytes") +current_limit = container.get_cgroup_item("memory.limit_in_bytes") +assert(container.set_cgroup_item("memory.limit_in_bytes", max_mem)) +assert(container.get_cgroup_item("memory.limit_in_bytes") != current_limit) + ## Freezing the container print("Freezing the container") container.freeze() diff --git a/src/python-lxc/lxc.c b/src/python-lxc/lxc.c index b489079b5..83e265926 100644 --- a/src/python-lxc/lxc.c +++ b/src/python-lxc/lxc.c @@ -203,6 +203,31 @@ Container_freeze(Container *self, PyObject *args, PyObject *kwds) Py_RETURN_FALSE; } +static PyObject * +Container_get_cgroup_item(Container *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", NULL}; + char* key = NULL; + int len = 0; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, + &key)) + Py_RETURN_FALSE; + + len = self->container->get_cgroup_item(self->container, key, NULL, 0); + + if (len <= 0) { + Py_RETURN_FALSE; + } + + char* value = (char*) malloc(sizeof(char)*len + 1); + if (self->container->get_cgroup_item(self->container, key, value, len + 1) != len) { + Py_RETURN_FALSE; + } + + return PyUnicode_FromString(value); +} + static PyObject * Container_get_config_item(Container *self, PyObject *args, PyObject *kwds) { @@ -210,7 +235,7 @@ Container_get_config_item(Container *self, PyObject *args, PyObject *kwds) char* key = NULL; int len = 0; - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, + if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, &key)) Py_RETURN_FALSE; @@ -287,6 +312,24 @@ Container_save_config(Container *self, PyObject *args, PyObject *kwds) Py_RETURN_FALSE; } +static PyObject * +Container_set_cgroup_item(Container *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", "value", NULL}; + char *key = NULL; + char *value = NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss|", kwlist, + &key, &value)) + Py_RETURN_FALSE; + + if (self->container->set_cgroup_item(self->container, key, value)) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + static PyObject * Container_set_config_item(Container *self, PyObject *args, PyObject *kwds) { @@ -441,6 +484,11 @@ static PyMethodDef Container_methods[] = { "\n" "Freezes the container and returns its return code." }, + {"get_cgroup_item", (PyCFunction)Container_get_cgroup_item, METH_VARARGS | METH_KEYWORDS, + "get_cgroup_item(key) -> string\n" + "\n" + "Get the current value of a cgroup entry." + }, {"get_config_item", (PyCFunction)Container_get_config_item, METH_VARARGS | METH_KEYWORDS, "get_config_item(key) -> string\n" "\n" @@ -463,6 +511,11 @@ static PyMethodDef Container_methods[] = { "Save the container configuration to its default " "location or to an alternative location if provided." }, + {"set_cgroup_item", (PyCFunction)Container_set_cgroup_item, METH_VARARGS | METH_KEYWORDS, + "set_cgroup_item(key, value) -> boolean\n" + "\n" + "Set a cgroup entry to the provided value." + }, {"set_config_item", (PyCFunction)Container_set_config_item, METH_VARARGS | METH_KEYWORDS, "set_config_item(key, value) -> boolean\n" "\n" diff --git a/src/python-lxc/lxc/__init__.py.in b/src/python-lxc/lxc/__init__.py.in index 9ef3459f1..91a59edfa 100644 --- a/src/python-lxc/lxc/__init__.py.in +++ b/src/python-lxc/lxc/__init__.py.in @@ -318,6 +318,18 @@ class Container(_lxc.Container): return False return True + def get_cgroup_item(self, key): + """ + Returns the value for a given cgroup entry. + A list is returned when multiple values are set. + """ + value = _lxc.Container.get_cgroup_item(self, key) + + if value is False: + return False + else: + return value.rstrip("\n") + def get_config_item(self, key): """ Returns the value for a given config key. From f2924f7898d6bfbdb30f8f02c87b86880afd79e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 7 Dec 2012 15:47:12 -0500 Subject: [PATCH 165/171] gitignore: Update for python files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 79dd77af9..a7667162b 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,9 @@ src/lxc/lxc-version src/lxc/lxc-wait src/lxc/legacy/lxc-ls +src/python-lxc/build/ +src/python-lxc/examples/api_test.py +src/python-lxc/lxc/__init__.py src/python-lxc/lxc/__pycache__/ src/tests/lxc-test-containertests From d8521cc3754a740a547b12aa81990d117b5d99ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Sun, 9 Dec 2012 23:36:10 -0500 Subject: [PATCH 166/171] python: Update add_device_node to use the new API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update add_device_node to use the new set_cgroup_item call instead of having to figure out the cgroup paths and update the entries manually. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/python-lxc/lxc/__init__.py.in | 40 ++++++++----------------------- 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/src/python-lxc/lxc/__init__.py.in b/src/python-lxc/lxc/__init__.py.in index 91a59edfa..07c956bd6 100644 --- a/src/python-lxc/lxc/__init__.py.in +++ b/src/python-lxc/lxc/__init__.py.in @@ -172,37 +172,17 @@ class Container(_lxc.Container): path_stat = os.stat(path) mode = stat.S_IMODE(path_stat.st_mode) - # Lookup the cgroup - cgroup_path = None - with open("/proc/%s/cgroup" % self.init_pid, "r") as fd: - for line in fd: - if ":devices:" in line: - cgroup_path = line.split(":")[-1].strip() - break - else: - return False - - # Lookup the cgroup mount point - cgroup = None - with open("/proc/mounts", "r") as fd: - for line in fd: - mount = line.split() - if (mount[2] == "cgroup" and "devices" in mount[3] - and os.path.exists("%s/%s" % (mount[1], cgroup_path))): - cgroup = "%s/%s" % (mount[1], cgroup_path) - break - - if not os.path.exists(cgroup): - return False - # Allow the target - with open("%s/devices.allow" % cgroup, "a") as fd: - if stat.S_ISBLK(path_stat.st_mode): - fd.write("b %s:%s rwm" % (int(path_stat.st_rdev / 256), - int(path_stat.st_rdev % 256))) - elif stat.S_ISCHR(path_stat.st_mode): - fd.write("c %s:%s rwm" % (int(path_stat.st_rdev / 256), - int(path_stat.st_rdev % 256))) + if stat.S_ISBLK(path_stat.st_mode): + self.set_cgroup_item("devices.allow", + "b %s:%s rwm" % + (int(path_stat.st_rdev / 256), + int(path_stat.st_rdev % 256))) + elif stat.S_ISCHR(path_stat.st_mode): + self.set_cgroup_item("devices.allow", + "c %s:%s rwm" % + (int(path_stat.st_rdev / 256), + int(path_stat.st_rdev % 256))) # Create the target rootfs = "/proc/%s/root/" % self.init_pid From 7323456ec3efe23bb7a84164d3e71d4293998f2c Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Thu, 6 Dec 2012 13:29:51 -0500 Subject: [PATCH 167/171] assume LXCPATH took on default localstatedir based value in configure Signed-off-by: Dwight Engen Acked-by: Michael H. Warfield --- lxc.spec.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lxc.spec.in b/lxc.spec.in index 51997b5ab..c7470b855 100644 --- a/lxc.spec.in +++ b/lxc.spec.in @@ -97,8 +97,7 @@ rm -rf %{buildroot} %defattr(-,root,root) %{_libdir}/*.so.* %{_libdir}/%{name} -@LXCPATH@ -%{_localstatedir}/cache/lxc +%{_localstatedir}/* %attr(4555,root,root) %{_libexecdir}/%{name}/lxc-init %files devel From ff918b1832307204b106ca7bcaea21ff57370d7a Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 11 Dec 2012 11:08:09 -0600 Subject: [PATCH 168/171] seccomp: free conf->seccomp (filename char *) Signed-off-by: Serge Hallyn --- src/lxc/conf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index a58f18d80..5173aaf60 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2760,6 +2760,8 @@ void lxc_conf_free(struct lxc_conf *conf) if (conf->aa_profile) free(conf->aa_profile); #endif + if (conf->seccomp) + free(conf->seccomp); lxc_clear_config_caps(conf); lxc_clear_cgroups(conf, "lxc.cgroup"); lxc_clear_hooks(conf, "lxc.hook"); From a02264fb1ef8abab2f7aff2d347638e6e831156e Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 11 Dec 2012 11:39:26 -0600 Subject: [PATCH 169/171] README: fix typo in example script Signed-off-by: Serge Hallyn --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 9d4360c36..cedb50d50 100644 --- a/README +++ b/README @@ -76,7 +76,7 @@ cat > seccomp.full << EOF whitelist EOF for i in `seq 0 300`; do - echo $i >> secomp.full + echo $i >> seccomp.full done for i in `seq 1024 1079`; do echo $i >> seccomp.full From 769872f9f2c994d8bfd6de906562df64bcd92600 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 11 Dec 2012 11:40:02 -0600 Subject: [PATCH 170/171] support new libseccomp api Detect the new api by existence in seccomp.h of the scmp_filter_ctx type in configure.ac. Signed-off-by: Serge Hallyn --- configure.ac | 3 +++ src/lxc/conf.c | 5 +++-- src/lxc/conf.h | 7 +++++++ src/lxc/lxcseccomp.h | 8 ++++++++ src/lxc/seccomp.c | 48 +++++++++++++++++++++++++++++++++++++------- 5 files changed, 62 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index ef321ce47..bdfcacf39 100644 --- a/configure.ac +++ b/configure.ac @@ -115,6 +115,9 @@ AM_COND_IF([ENABLE_SECCOMP], AC_CHECK_LIB([seccomp], [seccomp_init],[],[AC_MSG_ERROR([You must install the seccomp development package in order to compile lxc])]) AC_SUBST([SECCOMP_LIBS], [-lseccomp])]) +# HAVE_SCMP_FILTER_CTX=1 will tell us we have libseccomp api >= 1.0.0 +AC_CHECK_TYPES([scmp_filter_ctx], [], [], [#include ]) + AM_CONDITIONAL([ENABLE_DOCBOOK], [test "x$db2xman" != "x"]) AC_ARG_ENABLE([examples], diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 5173aaf60..7e44c3ce3 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -66,6 +66,8 @@ #include #endif +#include "lxcseccomp.h" + lxc_log_define(lxc_conf, lxc); #define MAXHWLEN 18 @@ -2760,8 +2762,7 @@ void lxc_conf_free(struct lxc_conf *conf) if (conf->aa_profile) free(conf->aa_profile); #endif - if (conf->seccomp) - free(conf->seccomp); + lxc_seccomp_free(conf); lxc_clear_config_caps(conf); lxc_clear_cgroups(conf, "lxc.cgroup"); lxc_clear_hooks(conf, "lxc.hook"); diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 3f6181f32..ca4dbc211 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -32,6 +32,10 @@ #include /* for lxc_handler */ +#if HAVE_SCMP_FILTER_CTX +typedef void * scmp_filter_ctx; +#endif + enum { LXC_NET_EMPTY, LXC_NET_VETH, @@ -246,6 +250,9 @@ struct lxc_conf { int lsm_umount_proc; #endif char *seccomp; // filename with the seccomp rules +#if HAVE_SCMP_FILTER_CTX + scmp_filter_ctx *seccomp_ctx; +#endif int maincmd_fd; int autodev; // if 1, mount and fill a /dev at start }; diff --git a/src/lxc/lxcseccomp.h b/src/lxc/lxcseccomp.h index 00262a52c..4f146dd2b 100644 --- a/src/lxc/lxcseccomp.h +++ b/src/lxc/lxcseccomp.h @@ -28,6 +28,7 @@ #ifdef HAVE_SECCOMP int lxc_seccomp_load(struct lxc_conf *conf); int lxc_read_seccomp_config(struct lxc_conf *conf); +void lxc_seccomp_free(struct lxc_conf *conf); #else static inline int lxc_seccomp_load(struct lxc_conf *conf) { return 0; @@ -36,6 +37,13 @@ static inline int lxc_seccomp_load(struct lxc_conf *conf) { static inline int lxc_read_seccomp_config(struct lxc_conf *conf) { return 0; } + +static inline void lxc_seccomp_free(struct lxc_conf *conf) { + if (conf->seccomp) { + free(conf->seccomp); + conf->seccomp = NULL; + } +} #endif #endif diff --git a/src/lxc/seccomp.c b/src/lxc/seccomp.c index f2c5d00e5..2f0b44708 100644 --- a/src/lxc/seccomp.c +++ b/src/lxc/seccomp.c @@ -27,6 +27,7 @@ #include #include #include +#include "config.h" #include "lxcseccomp.h" #include "log.h" @@ -69,7 +70,11 @@ static int parse_config(FILE *f, struct lxc_conf *conf) ret = sscanf(line, "%d", &nr); if (ret != 1) return -1; - ret = seccomp_rule_add(SCMP_ACT_ALLOW, nr, 0); + ret = seccomp_rule_add( +#if HAVE_SCMP_FILTER_CTX + conf->seccomp_ctx, +#endif + SCMP_ACT_ALLOW, nr, 0); if (ret < 0) { ERROR("failed loading allow rule for %d\n", nr); return ret; @@ -83,16 +88,28 @@ int lxc_read_seccomp_config(struct lxc_conf *conf) FILE *f; int ret; - if (seccomp_init(SCMP_ACT_ERRNO(31)) < 0) { /* for debug, pass in SCMP_ACT_TRAP */ - ERROR("failed initializing seccomp"); - return -1; - } if (!conf->seccomp) return 0; +#if HAVE_SCMP_FILTER_CTX + /* XXX for debug, pass in SCMP_ACT_TRAP */ + conf->seccomp_ctx = seccomp_init(SCMP_ACT_ERRNO(31)); + ret = !conf->seccomp_ctx; +#else + ret = seccomp_init(SCMP_ACT_ERRNO(31)) < 0; +#endif + if (ret) { + ERROR("failed initializing seccomp"); + return -1; + } + /* turn of no-new-privs. We don't want it in lxc, and it breaks * with apparmor */ - if (seccomp_attr_set(SCMP_FLTATR_CTL_NNP, 0)) { + if (seccomp_attr_set( +#if HAVE_SCMP_FILTER_CTX + conf->seccomp_ctx, +#endif + SCMP_FLTATR_CTL_NNP, 0)) { ERROR("failed to turn off n-new-privs\n"); return -1; } @@ -112,10 +129,27 @@ int lxc_seccomp_load(struct lxc_conf *conf) int ret; if (!conf->seccomp) return 0; - ret = seccomp_load(); + ret = seccomp_load( +#if HAVE_SCMP_FILTER_CTX + conf->seccomp_ctx +#endif + ); if (ret < 0) { ERROR("Error loading the seccomp policy"); return -1; } return 0; } + +void lxc_seccomp_free(struct lxc_conf *conf) { + if (conf->seccomp) { + free(conf->seccomp); + conf->seccomp = NULL; + } +#if HAVE_SCMP_FILTER_CTX + if (conf->seccomp_ctx) { + seccomp_release(conf->seccomp_ctx); + conf->seccomp_ctx = NULL; + } +#endif +} From 222fea5a10544018f26d60d73132069fb0fa8797 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 11 Dec 2012 12:39:16 -0500 Subject: [PATCH 171/171] Don't attempt to symlink kmsg without rootfs->path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For example doing "lxc-execute -n tmpct /bin/bash" will call setup_kmsg(), but in this case rootfs->mount/dev directory doesn't even exist so the call to symlink fails with ENOENT. Commit f62b3449 made this failure not fatal, but we should not even try it when we know it will fail. See similar code in setup_tty(), setup_console(), etc. Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn Acked-by: Stéphane Graber --- src/lxc/conf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 7e44c3ce3..84e51db1b 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1204,6 +1204,8 @@ static int setup_kmsg(const struct lxc_rootfs *rootfs, char kpath[MAXPATHLEN]; int ret; + if (!rootfs->path) + return 0; ret = snprintf(kpath, sizeof(kpath), "%s/dev/kmsg", rootfs->mount); if (ret < 0 || ret >= sizeof(kpath)) return -1;