From fd95f2402dc70ad41fa2db8fb101f950196458a9 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 9 Apr 2013 16:23:05 -0500 Subject: [PATCH 001/325] lxc.functions: don't let LXC_PATH= line end in failure Otherwise if called from dash with set -e, dash will exit. This causes lxc-clone to fail. Signed-off-by: Serge Hallyn --- src/lxc/lxc.functions.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/lxc.functions.in b/src/lxc/lxc.functions.in index b41f9b899..aa5717d0b 100644 --- a/src/lxc/lxc.functions.in +++ b/src/lxc/lxc.functions.in @@ -26,7 +26,7 @@ templatedir=@LXCTEMPLATEDIR@ lxcinitdir=@LXCINITDIR@ get_default_lxcpath() { - LXC_PATH=$(grep -v "^#" "$globalconf" 2>/dev/null | grep "[ \t]*lxcpath[ \t]*=") + LXC_PATH=$(grep -v "^#" "$globalconf" 2>/dev/null | grep "[ \t]*lxcpath[ \t]*=") || true if [ -n "$LXC_PATH" ]; then echo $LXC_PATH | awk -F= '{ print $2 }' else From 190a2ea88e9820e5e150ce36414233da4bd34b44 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 10 Apr 2013 10:49:51 -0400 Subject: [PATCH 002/325] remove unused lxc_copy_file Commit e3642c43 added lxc_copy_file for use in 64e1ae63. The use of it was removed in commit 1bc60a65. Removing it reduces dead code and the footprint of liblxc. Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/utils.c | 71 ------------------------------------------------- src/lxc/utils.h | 1 - 2 files changed, 72 deletions(-) diff --git a/src/lxc/utils.c b/src/lxc/utils.c index f4ba90520..ecf9d2c77 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -39,77 +39,6 @@ lxc_log_define(lxc_utils, lxc); -int lxc_copy_file(const char *srcfile, const char *dstfile) -{ - void *srcaddr = NULL, *dstaddr; - struct stat stat; - int srcfd, dstfd, ret = -1; - char c = '\0'; - - dstfd = open(dstfile, O_CREAT | O_EXCL | O_RDWR, 0600); - if (dstfd < 0) { - SYSERROR("failed to creat '%s'", dstfile); - goto out; - } - - srcfd = open(srcfile, O_RDONLY); - if (srcfd < 0) { - SYSERROR("failed to open '%s'", srcfile); - goto err; - } - - if (fstat(srcfd, &stat)) { - SYSERROR("failed to stat '%s'", srcfile); - goto err; - } - - if (!stat.st_size) { - INFO("copy '%s' which is an empty file", srcfile); - ret = 0; - goto out_close; - } - - if (lseek(dstfd, stat.st_size - 1, SEEK_SET) < 0) { - SYSERROR("failed to seek dest file '%s'", dstfile); - goto err; - } - - /* fixup length */ - if (write(dstfd, &c, 1) < 0) { - SYSERROR("failed to write to '%s'", dstfile); - goto err; - } - - srcaddr = mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, srcfd, 0L); - if (srcaddr == MAP_FAILED) { - SYSERROR("failed to mmap '%s'", srcfile); - goto err; - } - - dstaddr = mmap(NULL, stat.st_size, PROT_WRITE, MAP_SHARED, dstfd, 0L); - if (dstaddr == MAP_FAILED) { - SYSERROR("failed to mmap '%s'", dstfile); - goto err; - } - - ret = 0; - - memcpy(dstaddr, srcaddr, stat.st_size); - - munmap(dstaddr, stat.st_size); -out_mmap: - if (srcaddr) - munmap(srcaddr, stat.st_size); -out_close: - close(dstfd); - close(srcfd); -out: - return ret; -err: - unlink(dstfile); - goto out_mmap; -} - static int mount_fs(const char *source, const char *target, const char *type) { /* the umount may fail */ diff --git a/src/lxc/utils.h b/src/lxc/utils.h index bf5b6cdc6..8954503a3 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -23,7 +23,6 @@ #ifndef _utils_h #define _utils_h -extern int lxc_copy_file(const char *src, const char *dst); extern int lxc_setup_fs(void); extern int get_u16(unsigned short *val, const char *arg, int base); extern int mkdir_p(const char *dir, mode_t mode); From 1354f952876e96b456425efc7ed9994caf687028 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 9 Apr 2013 09:41:15 -0400 Subject: [PATCH 003/325] minor documentation fixes / clarification Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn Signed-off-by: Serge Hallyn --- doc/legacy/lxc-ls.sgml.in | 4 ++-- doc/lxc-execute.sgml.in | 8 ++++---- doc/lxc-ps.sgml.in | 6 +++--- doc/lxc-unshare.sgml.in | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/legacy/lxc-ls.sgml.in b/doc/legacy/lxc-ls.sgml.in index c04a4a4c2..60c085cdc 100644 --- a/doc/legacy/lxc-ls.sgml.in +++ b/doc/legacy/lxc-ls.sgml.in @@ -50,7 +50,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA lxc-ls --active - ls option + ls options @@ -79,7 +79,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 c83a5eb53..de233f68f 100644 --- a/doc/lxc-execute.sgml.in +++ b/doc/lxc-execute.sgml.in @@ -132,12 +132,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Signal the end of options and disables further option processing. Any arguments after the -- are treated as - arguments. + arguments to command. - This option is useful when you want to execute, with the - command lxc-execute, a command line - with its own options. + This option is useful when you want specify options + to command and don't want + lxc-execute to interpret them. diff --git a/doc/lxc-ps.sgml.in b/doc/lxc-ps.sgml.in index f20bb0293..b0103cfd9 100644 --- a/doc/lxc-ps.sgml.in +++ b/doc/lxc-ps.sgml.in @@ -52,7 +52,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --name name --lxc --host - -- ps option + -- ps options @@ -69,7 +69,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA the container associated to processes. - The additionnal specified ps options must not + The additional specified ps options must not remove the default ps header and the pid information, to be able to have the lxc-ps to find the container associated to processes. @@ -119,7 +119,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - + diff --git a/doc/lxc-unshare.sgml.in b/doc/lxc-unshare.sgml.in index 1fbaf0bba..5c899b63d 100644 --- a/doc/lxc-unshare.sgml.in +++ b/doc/lxc-unshare.sgml.in @@ -49,7 +49,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - lxc-clone + lxc-unshare -s namespaces -u user command @@ -115,7 +115,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA To spawn a new shell with its own UTS (hostname) namespace, - lxc-clone -s UTSNAME /bin/bash + lxc-unshare -s UTSNAME /bin/bash If the hostname is changed in that shell, the change will not be reflected on the host. @@ -123,7 +123,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA To spawn a shell in a new network, pid, and mount namespace, - lxc-clone -s "NETWORK|PID|MOUNT" /bin/bash + lxc-unshare -s "NETWORK|PID|MOUNT" /bin/bash The resulting shell will have pid 1 and will see no network interfaces. After re-mounting /proc in that shell, From fe19f236a2295da1e01ab05ff59853c5a4556811 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 8 Apr 2013 12:45:23 -0400 Subject: [PATCH 004/325] fix wait status in pid reuse case Commit 37c3dfc9 sets the wait status on only the child pid. It intended to match the pid only once to protect against pid reuse but it won't because the indicator was reset to 0 every time at the top of the loop. If the child pid is reused, the wait status will be set again. Fix by setting indicator outside the loop. Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/lxc_init.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/lxc/lxc_init.c b/src/lxc/lxc_init.c index 5693da58f..c83c2f18b 100644 --- a/src/lxc/lxc_init.c +++ b/src/lxc/lxc_init.c @@ -63,7 +63,7 @@ int main(int argc, char *argv[]) int err = -1; char **aargv; sigset_t mask, omask; - int i, shutdown = 0; + int i, have_status = 0, shutdown = 0; while (1) { int ret = getopt_long_only(argc, argv, "", options, NULL); @@ -162,7 +162,6 @@ int main(int argc, char *argv[]) err = 0; for (;;) { int status; - int orphan = 0; pid_t waited_pid; switch (was_interrupted) { @@ -209,10 +208,10 @@ int main(int argc, char *argv[]) * (not wrapped pid) and continue to wait for * the end of the orphan group. */ - if ((waited_pid != pid) || (orphan ==1)) - continue; - orphan = 1; - err = lxc_error_set_and_log(waited_pid, status); + if (waited_pid == pid && !have_status) { + err = lxc_error_set_and_log(waited_pid, status); + have_status = 1; + } } out: return err; From 6efdcb6a3cc4d06bf64af69b08bc95335f02b79f Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 3 Apr 2013 17:08:32 -0400 Subject: [PATCH 005/325] debian template: set arch when dpkg doesn't exist on host Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- templates/lxc-debian.in | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/templates/lxc-debian.in b/templates/lxc-debian.in index 7bbc46b94..e36523c26 100644 --- a/templates/lxc-debian.in +++ b/templates/lxc-debian.in @@ -172,7 +172,18 @@ install_debian() return 1 fi - arch=$(dpkg --print-architecture) + if which dpkg >/dev/null 2>&1 ; then + arch=$(dpkg --print-architecture) + else + arch=$(arch) + if [ "$arch" = "i686" ]; then + arch="i386" + elif [ "$arch" = "x86_64" ]; then + arch="amd64" + elif [ "$arch" = "armv7l" ]; then + arch="armhf" + fi + fi echo "Checking cache download in $cache/rootfs-$SUITE-$arch ... " if [ ! -e "$cache/rootfs-$SUITE-$arch" ]; then From 33892746e373449a8a69a4265d783bf701cb5784 Mon Sep 17 00:00:00 2001 From: Wojciech Izykowski Date: Sat, 6 Apr 2013 16:33:00 +0200 Subject: [PATCH 006/325] lxc-start-ephemeral: fixed bug with wrong ssh option (-k instead of -i) Corrected ssh option for custom key (from -k to -i). Just see ssh manpage for justification. Signed-off-by: Wojciech Izykowski Acked-by: Serge E. 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 d1bb6be45..bed86e6d2 100644 --- a/src/lxc/lxc-start-ephemeral.in +++ b/src/lxc/lxc-start-ephemeral.in @@ -287,7 +287,7 @@ if args.user: cmd += ["-l", args.user] if args.key: - cmd += ["-k", args.key] + cmd += ["-i", args.key] for ip in ips: ssh_cmd = cmd + [ip] + args.command From 9eee2f7739dbaf82d3b0837de41cdcba5ee4a1d3 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 3 Apr 2013 12:31:46 -0400 Subject: [PATCH 007/325] oracle template: install additional user specified pkgs Fix lxc-create to not word split template arguments. This makes lxc-create -n ol -t oracle -- -r "at cronie wget" work since the argument to -r will be passed as one arg instead of three. Fix oracle template -u option to shift the correct amount. Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/lxc-create.in | 2 +- templates/lxc-oracle.in | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in index ebbdd7b42..fbb566f63 100644 --- a/src/lxc/lxc-create.in +++ b/src/lxc/lxc-create.in @@ -336,7 +336,7 @@ if [ "$backingstore" = "lvm" ]; then fi if [ ! -z "$lxc_template" ]; then - $template_path --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 diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in index aad21e8a8..16a622e13 100644 --- a/templates/lxc-oracle.in +++ b/templates/lxc-oracle.in @@ -511,7 +511,7 @@ container_rootfs_create() # we unshare the mount namespace because yum installing the ol4 # packages causes $rootfs/proc to be mounted on - lxc-unshare -s MOUNT yum -- $yum_args install $min_pkgs + lxc-unshare -s MOUNT yum -- $yum_args install $min_pkgs $user_pkgs if [ $? -ne 0 ]; then die "Failed to download and install the rootfs, aborting." fi @@ -599,6 +599,7 @@ usage() cat < architecture (ie. i386, x86_64) -R|--release= release to download for the new container + -r|--rpms= additional rpms to install into container -u|--url= replace yum repo url (ie. local yum mirror) -t|--templatefs= copy/clone rootfs at path instead of downloading -h|--help @@ -608,7 +609,7 @@ EOF return 0 } -options=$(getopt -o hp:n:a:R:u:t: -l help,path:,name:,arch:,release:,url:,templatefs: -- "$@") +options=$(getopt -o hp:n:a:R:r:u:t: -l help,path:,name:,arch:,release:,rpms:,url:,templatefs: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 @@ -624,7 +625,8 @@ do -n|--name) name=$2; shift 2;; -a|--arch) arch=$2; shift 2;; -R|--release) container_release_version=$2; shift 2;; - -u|--url) repourl=$2; shift;; + -r|--rpms) user_pkgs=$2; shift 2;; + -u|--url) repourl=$2; shift 2;; -t|--templatefs) template_rootfs=$2; shift 2;; --) shift 1; break ;; *) break ;; From 75129865d48d2293383316f88ce7661e37dde43d Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 3 Apr 2013 14:43:15 -0400 Subject: [PATCH 008/325] ubuntu template: fix installation when LANG=C The ubuntu template will silently fail (because it is set -e) on the locale-gen command when LANG=C Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- templates/lxc-ubuntu.in | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/templates/lxc-ubuntu.in b/templates/lxc-ubuntu.in index f01163326..db38af063 100644 --- a/templates/lxc-ubuntu.in +++ b/templates/lxc-ubuntu.in @@ -85,8 +85,10 @@ EOF 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 + if [ "$LANG" != "C" ]; then + chroot $rootfs locale-gen $LANG + chroot $rootfs update-locale LANG=$LANG + fi fi return 0 From e649c8032f84b488cac8ea6c8fb9a77c424a0419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 11 Apr 2013 14:15:21 +0200 Subject: [PATCH 009/325] python: Fix memory management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- src/python-lxc/lxc.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/python-lxc/lxc.c b/src/python-lxc/lxc.c index 06d339691..8da6f36dc 100644 --- a/src/python-lxc/lxc.c +++ b/src/python-lxc/lxc.c @@ -1,7 +1,7 @@ /* * python-lxc: Python bindings for LXC * - * (C) Copyright Canonical Ltd. 2012 + * (C) Copyright Canonical Ltd. 2012-2013 * * Authors: * Stéphane Graber @@ -43,9 +43,10 @@ convert_tuple_to_char_pointer_array(PyObject *argv) { PyObject *pyobj = PyTuple_GetItem(argv, i); char *str = NULL; - PyObject *pystr; + PyObject *pystr = NULL; if (!PyUnicode_Check(pyobj)) { PyErr_SetString(PyExc_ValueError, "Expected a string"); + free(result); return NULL; } @@ -62,6 +63,7 @@ convert_tuple_to_char_pointer_array(PyObject *argv) { static void Container_dealloc(Container* self) { + lxc_container_put(self->container); Py_TYPE(self)->tp_free((PyObject*)self); } @@ -190,9 +192,11 @@ Container_create(Container *self, PyObject *args, PyObject *kwds) } if (self->container->create(self->container, template_name, create_args)) { + free(create_args); Py_RETURN_TRUE; } + free(create_args); Py_RETURN_FALSE; } @@ -222,6 +226,7 @@ Container_get_cgroup_item(Container *self, PyObject *args, PyObject *kwds) static char *kwlist[] = {"key", NULL}; char* key = NULL; int len = 0; + PyObject *ret = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, &key)) @@ -235,10 +240,13 @@ Container_get_cgroup_item(Container *self, PyObject *args, PyObject *kwds) char* value = (char*) malloc(sizeof(char)*len + 1); if (self->container->get_cgroup_item(self->container, key, value, len + 1) != len) { + free(value); Py_RETURN_FALSE; } - return PyUnicode_FromString(value); + ret = PyUnicode_FromString(value); + free(value); + return ret; } static PyObject * @@ -247,6 +255,7 @@ Container_get_config_item(Container *self, PyObject *args, PyObject *kwds) static char *kwlist[] = {"key", NULL}; char* key = NULL; int len = 0; + PyObject *ret = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, &key)) @@ -260,10 +269,13 @@ Container_get_config_item(Container *self, PyObject *args, PyObject *kwds) char* value = (char*) malloc(sizeof(char)*len + 1); if (self->container->get_config_item(self->container, key, value, len + 1) != len) { + free(value); Py_RETURN_FALSE; } - return PyUnicode_FromString(value); + ret = PyUnicode_FromString(value); + free(value); + return ret; } static PyObject * @@ -278,6 +290,7 @@ Container_get_keys(Container *self, PyObject *args, PyObject *kwds) static char *kwlist[] = {"key", NULL}; char* key = NULL; int len = 0; + PyObject *ret = NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, &key)) @@ -291,10 +304,13 @@ Container_get_keys(Container *self, PyObject *args, PyObject *kwds) char* value = (char*) malloc(sizeof(char)*len + 1); if (self->container->get_keys(self->container, key, value, len + 1) != len) { + free(value); Py_RETURN_FALSE; } - return PyUnicode_FromString(value); + ret = PyUnicode_FromString(value); + free(value); + return ret; } static PyObject * @@ -427,9 +443,11 @@ Container_start(Container *self, PyObject *args, PyObject *kwds) self->container->want_daemonize(self->container); if (self->container->start(self->container, init_useinit, init_args)) { + free(init_args); Py_RETURN_TRUE; } + free(init_args); Py_RETURN_FALSE; } From 43d1aa34aab1c43bce8f083d024bf54f0246a884 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Thu, 11 Apr 2013 11:43:31 -0500 Subject: [PATCH 010/325] Fix up struct lxc_container locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. in container_free, set c->privlock to NULL before calling sem_destroy, to prevent a window where another thread could call sem_wait(c->privlock) while c->privlock is not NULL but is already destroyed. 2. in container_get, check for numthreads < 0 before calling lxclock. Once numthreads is 0, it never goes back up. Following is a comment added to lxccontainer.c: /* * Consider the following case: freer | racing get()er ================================================================== lxc_container_put() | lxc_container_get() \ lxclock(c->privlock) | c->numthreads < 1? (no) \ c->numthreads = 0 | \ lxclock(c->privlock) -> waits \ lxcunlock() | \ \ lxc_container_free() | \ lxclock() returns | \ c->numthreads < 1 -> return 0 \ \ (free stuff) | \ \ sem_destroy(privlock) | * When the get()er checks numthreads the first time, one of the following * is true: * 1. freer has set numthreads = 0. get() returns 0 * 2. freer is between lxclock and setting numthreads to 0. get()er will * sem_wait on privlock, get lxclock after freer() drops it, then see * numthreads is 0 and exit without touching lxclock again.. * 3. freer has not yet locked privlock. If get()er runs first, then put()er * will see --numthreads = 1 and not call lxc_container_free(). */ Signed-off-by: Serge Hallyn Acked-by: Seth Arnold Acked-by: Stéphane Graber --- src/lxc/lxccontainer.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index a4376b405..e09b3fb91 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -72,9 +72,10 @@ static void lxc_container_free(struct lxc_container *c) c->slock = NULL; } if (c->privlock) { - sem_destroy(c->privlock); - free(c->privlock); + sem_t *l = c->privlock; c->privlock = NULL; + sem_destroy(l); + free(l); } if (c->name) { free(c->name); @@ -91,11 +92,39 @@ static void lxc_container_free(struct lxc_container *c) free(c); } +/* + * Consider the following case: +freer | racing get()er +================================================================== +lxc_container_put() | lxc_container_get() +\ lxclock(c->privlock) | c->numthreads < 1? (no) +\ c->numthreads = 0 | \ lxclock(c->privlock) -> waits +\ lxcunlock() | \ +\ lxc_container_free() | \ lxclock() returns + | \ c->numthreads < 1 -> return 0 +\ \ (free stuff) | +\ \ sem_destroy(privlock) | + + * When the get()er checks numthreads the first time, one of the following + * is true: + * 1. freer has set numthreads = 0. get() returns 0 + * 2. freer is between lxclock and setting numthreads to 0. get()er will + * sem_wait on privlock, get lxclock after freer() drops it, then see + * numthreads is 0 and exit without touching lxclock again.. + * 3. freer has not yet locked privlock. If get()er runs first, then put()er + * will see --numthreads = 1 and not call lxc_container_free(). +*/ + int lxc_container_get(struct lxc_container *c) { if (!c) return 0; + // if someone else has already started freeing the container, don't + // try to take the lock, which may be invalid + if (c->numthreads < 1) + return 0; + if (lxclock(c->privlock, 0)) return 0; if (c->numthreads < 1) { From 8767795058ca5b46c8a9e335ad941d8799241716 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 12 Apr 2013 15:11:11 -0500 Subject: [PATCH 011/325] lxclock: indentation Signed-off-by: Serge Hallyn --- src/lxc/lxclock.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lxc/lxclock.c b/src/lxc/lxclock.c index bbc39efa4..88a388182 100644 --- a/src/lxc/lxclock.c +++ b/src/lxc/lxclock.c @@ -51,16 +51,16 @@ static void lxcfree_name(char *name) static sem_t *lxc_new_unnamed_sem(void) { - sem_t *s; - int ret; + 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; + s = malloc(sizeof(*s)); + if (!s) + return NULL; + ret = sem_init(s, 0, 1); + if (ret) + return NULL; + return s; } sem_t *lxc_newlock(const char *name) @@ -76,8 +76,8 @@ sem_t *lxc_newlock(const char *name) return NULL; lock = sem_open(lname, OFLAG, SEMMODE, SEMVALUE); lxcfree_name(lname); - if (lock == SEM_FAILED) - return NULL; + if (lock == SEM_FAILED) + return NULL; return lock; } @@ -90,7 +90,7 @@ int lxclock(sem_t *sem, int timeout) } else { struct timespec ts; if (clock_gettime(CLOCK_REALTIME, &ts) == -1) - return -2; + return -2; ts.tv_sec += timeout; ret = sem_timedwait(sem, &ts); } From bdb539b89bbe123018392bb8c64cb94c13d736a8 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 12 Apr 2013 15:11:29 -0500 Subject: [PATCH 012/325] lxclock: fix coverity-found leak if sem_init fails, free what we mallocd. Signed-off-by: Serge Hallyn --- src/lxc/lxclock.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxclock.c b/src/lxc/lxclock.c index 88a388182..0cbe843a1 100644 --- a/src/lxc/lxclock.c +++ b/src/lxc/lxclock.c @@ -58,8 +58,10 @@ static sem_t *lxc_new_unnamed_sem(void) if (!s) return NULL; ret = sem_init(s, 0, 1); - if (ret) + if (ret) { + free(s); return NULL; + } return s; } From af41709c4243e0fd9dc1fac5f22cdd47316f8277 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 12 Apr 2013 15:15:22 -0500 Subject: [PATCH 013/325] af_unix.c: fix coverity-found bug: pass addr size Signed-off-by: Serge Hallyn --- src/lxc/af_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/af_unix.c b/src/lxc/af_unix.c index 6c393359e..eff13d4fb 100644 --- a/src/lxc/af_unix.c +++ b/src/lxc/af_unix.c @@ -75,7 +75,7 @@ int lxc_af_unix_open(const char *path, int type, int flags) int lxc_af_unix_close(int fd) { struct sockaddr_un addr; - socklen_t addrlen; + socklen_t addrlen = sizeof(addr); if (!getsockname(fd, (struct sockaddr *)&addr, &addrlen) && addr.sun_path[0]) From 586d4e9be1eb13cd9cb77cf6c56ce57e24623c44 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Sun, 14 Apr 2013 21:12:58 -0500 Subject: [PATCH 014/325] lxcccontainer: add missing va_end found by coverity Signed-off-by: Serge Hallyn --- src/lxc/lxccontainer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index e09b3fb91..926f089b8 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -440,8 +440,10 @@ static bool lxcapi_startl(struct lxc_container *c, int useinit, ...) break; n_inargs++; temp = realloc(inargs, n_inargs * sizeof(*inargs)); - if (!temp) + if (!temp) { + va_end(ap); goto out; + } inargs = temp; inargs[n_inargs - 1] = strdup(arg); // not sure if it's safe not to copy } From 416707883893211a15c031b1f3589bc7cde9bf2b Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Sun, 14 Apr 2013 21:17:09 -0500 Subject: [PATCH 015/325] lxccontaienr: fix missing va_end in error case. Signed-off-by: Serge Hallyn --- src/lxc/lxccontainer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 926f089b8..27e86f972 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -719,8 +719,10 @@ static bool lxcapi_createl(struct lxc_container *c, char *t, ...) break; nargs++; temp = realloc(args, (nargs+1) * sizeof(*args)); - if (!temp) + if (!temp) { + va_end(ap); goto out; + } args = temp; args[nargs - 1] = arg; } From c928f41fc0e79a24e4c43a80fb26b3c46997d91a Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Sun, 14 Apr 2013 21:18:53 -0500 Subject: [PATCH 016/325] ifdef out skipped startone test code Unfortunately installing a working lxc-init is somewhat hairy and distro-dependent. So we skipped it before, but Coverity didn't like that, so just ifdef it out. Signed-off-by: Serge Hallyn --- src/tests/startone.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tests/startone.c b/src/tests/startone.c index 1eb3e9915..591399d9d 100644 --- a/src/tests/startone.c +++ b/src/tests/startone.c @@ -222,6 +222,7 @@ int main(int argc, char *argv[]) c->stop(c); /* feh - multilib has moved the lxc-init crap */ +#if 0 goto ok; ret = system("mkdir -p " LXCPATH "/lxctest1/rootfs//usr/local/libexec/lxc"); @@ -251,6 +252,7 @@ int main(int argc, char *argv[]) // auto-check result? ('bobo' is printed on stdout) ok: +#endif fprintf(stderr, "all lxc_container tests passed for %s\n", c->name); ret = 0; From b4e4ca49c792d7320787a6991ce1815d26060d39 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Sun, 14 Apr 2013 21:39:34 -0500 Subject: [PATCH 017/325] lxc_monitor: make sure msg.name is null terminated (bug found by coverity) Signed-off-by: Serge Hallyn --- src/lxc/lxc_monitor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lxc/lxc_monitor.c b/src/lxc/lxc_monitor.c index 053037390..8c158698e 100644 --- a/src/lxc/lxc_monitor.c +++ b/src/lxc/lxc_monitor.c @@ -97,6 +97,7 @@ int main(int argc, char *argv[]) if (lxc_monitor_read(fd, &msg) < 0) return -1; + msg.name[sizeof(msg.name)-1] = '\0'; if (regexec(&preg, msg.name, 0, NULL, 0)) continue; From a741a85d8e241e9ca773f3cd7575d720837fcb51 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Sun, 14 Apr 2013 21:45:00 -0500 Subject: [PATCH 018/325] lxcapi_create: fix leak of tpath when a container already exists 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 27e86f972..ce751ea41 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -561,7 +561,7 @@ static bool lxcapi_create(struct lxc_container *c, char *t, char *const argv[]) /* container is already created if we have a config and rootfs.path is accessible */ if (lxcapi_is_defined(c) && c->lxc_conf && c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) == 0) - return false; + goto out; /* we're going to fork. but since we'll wait for our child, we don't need to lxc_container_get */ From a6537fbbfb0b9d08adc58ae23b873a084e5d479c Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Sun, 14 Apr 2013 21:48:49 -0500 Subject: [PATCH 019/325] genl.c: fix a resource leak found by coverity Signed-off-by: Serge Hallyn --- src/lxc/genl.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/lxc/genl.c b/src/lxc/genl.c index c9a3d72dc..873b0847e 100644 --- a/src/lxc/genl.c +++ b/src/lxc/genl.c @@ -60,45 +60,46 @@ static int genetlink_resolve_family(const char *family) ret = netlink_open(&handler, NETLINK_GENERIC); if (ret) - return ret; + goto out; ret = nla_put_string((struct nlmsg *)&request->nlmsghdr, CTRL_ATTR_FAMILY_NAME, family); if (ret) - goto out; + goto out_close; ret = netlink_transaction(&handler, (struct nlmsg *)&request->nlmsghdr, (struct nlmsg *)&reply->nlmsghdr); if (ret < 0) - goto out; + goto out_close; genlmsghdr = NLMSG_DATA(&reply->nlmsghdr); len = reply->nlmsghdr.nlmsg_len; ret = -ENOMSG; if (reply->nlmsghdr.nlmsg_type != GENL_ID_CTRL) - goto out; + goto out_close; if (genlmsghdr->cmd != CTRL_CMD_NEWFAMILY) - goto out; + goto out_close; ret = -EMSGSIZE; len -= NLMSG_LENGTH(GENL_HDRLEN); if (len < 0) - goto out; + goto out_close; attr = (struct nlattr *)GENLMSG_DATA(reply); attr = (struct nlattr *)((char *)attr + NLA_ALIGN(attr->nla_len)); ret = -ENOMSG; if (attr->nla_type != CTRL_ATTR_FAMILY_ID) - goto out; + goto out_close; ret = *(__u16 *) NLA_DATA(attr); +out_close: + netlink_close(&handler); out: genlmsg_free(request); genlmsg_free(reply); - netlink_close(&handler); return ret; } From b6f24d54f54146a0f5de700dac7ffc2ef7624359 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Sun, 14 Apr 2013 21:56:51 -0500 Subject: [PATCH 020/325] fix resource leak of utsname in error path found by coverity Signed-off-by: Serge Hallyn --- src/lxc/confile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 676878e62..38685fb0d 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -1344,6 +1344,7 @@ static int config_utsname(const char *key, const char *value, if (strlen(value) >= sizeof(utsname->nodename)) { ERROR("node name '%s' is too long", utsname->nodename); + free(utsname); return -1; } From 022de5f317014c538e17378b626cf3267625e141 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Sun, 14 Apr 2013 21:59:02 -0500 Subject: [PATCH 021/325] fix resource leak of netdev on error path found by coverity Signed-off-by: Serge Hallyn --- src/lxc/confile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 38685fb0d..29d3b23fa 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -272,6 +272,7 @@ static int config_network_type(const char *key, const char *value, list = malloc(sizeof(*list)); if (!list) { SYSERROR("failed to allocate memory"); + free(netdev); return -1; } From bb1d227404ff96564877a04ef9299c63f608f543 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Sun, 14 Apr 2013 22:02:03 -0500 Subject: [PATCH 022/325] fix free of alloca()d buffer (found by coverity) Signed-off-by: Serge Hallyn --- src/lxc/conf.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 6b3f31813..33061df22 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -358,7 +358,6 @@ static int run_script(const char *name, const char *section, ret = snprintf(buffer, size, "%s %s %s", script, name, section); if (ret < 0 || ret >= size) { ERROR("Script name too long"); - free(buffer); return -1; } @@ -368,7 +367,6 @@ static int run_script(const char *name, const char *section, int rc; rc = snprintf(buffer + ret, len, " %s", p); if (rc < 0 || rc >= len) { - free(buffer); ERROR("Script args too long"); return -1; } From 2802732032aeaabe8c793ae76112d9c8ba13ee23 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Sun, 14 Apr 2013 22:16:26 -0500 Subject: [PATCH 023/325] fix coverity-found resource leaks in config_network_ipv6 Signed-off-by: Serge Hallyn --- src/lxc/confile.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 29d3b23fa..9549b1596 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -726,6 +726,7 @@ static int config_network_ipv6(const char *key, const char *value, list = malloc(sizeof(*list)); if (!list) { SYSERROR("failed to allocate memory"); + free(inet6dev); return -1; } @@ -735,6 +736,8 @@ static int config_network_ipv6(const char *key, const char *value, valdup = strdup(value); if (!valdup) { ERROR("no address specified"); + free(list); + free(inet6dev); return -1; } @@ -748,6 +751,8 @@ static int config_network_ipv6(const char *key, const char *value, if (!inet_pton(AF_INET6, value, &inet6dev->addr)) { SYSERROR("invalid ipv6 address: %s", value); + free(list); + free(inet6dev); free(valdup); return -1; } @@ -785,6 +790,7 @@ static int config_network_ipv6_gateway(const char *key, const char *value, } else { if (!inet_pton(AF_INET6, value, gw)) { SYSERROR("invalid ipv6 gateway address: %s", value); + free(gw); return -1; } From 5371906219ff19886169612993efbb8e82f749a7 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Sun, 14 Apr 2013 22:22:10 -0500 Subject: [PATCH 024/325] fix coverity-found resource leaks on error paths. Signed-off-by: Serge Hallyn --- src/lxc/confile.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 9549b1596..ba017074a 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -611,6 +611,7 @@ static int config_network_ipv4(const char *key, const char *value, list = malloc(sizeof(*list)); if (!list) { SYSERROR("failed to allocate memory"); + free(inetdev); return -1; } @@ -620,6 +621,8 @@ static int config_network_ipv4(const char *key, const char *value, addr = strdup(value); if (!addr) { ERROR("no address specified"); + free(inetdev); + free(list); return -1; } @@ -637,12 +640,16 @@ static int config_network_ipv4(const char *key, const char *value, if (!inet_pton(AF_INET, addr, &inetdev->addr)) { SYSERROR("invalid ipv4 address: %s", value); + free(inetdev); free(addr); + free(list); return -1; } if (bcast && !inet_pton(AF_INET, bcast, &inetdev->bcast)) { SYSERROR("invalid ipv4 broadcast address: %s", value); + free(inetdev); + free(list); free(addr); return -1; } @@ -684,6 +691,7 @@ static int config_network_ipv4_gateway(const char *key, const char *value, if (!value) { ERROR("no ipv4 gateway address specified"); + free(gw); return -1; } @@ -693,6 +701,7 @@ static int config_network_ipv4_gateway(const char *key, const char *value, } else { if (!inet_pton(AF_INET, value, gw)) { SYSERROR("invalid ipv4 gateway address: %s", value); + free(gw); return -1; } From 00b6be440f93131e35e75fb1b34d8d3220590bb5 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Sun, 14 Apr 2013 22:44:09 -0500 Subject: [PATCH 025/325] coverity resource leak fixes Signed-off-by: Serge Hallyn --- src/lxc/apparmor.c | 2 ++ src/lxc/cgroup.c | 4 +++- src/lxc/conf.c | 7 ++++++- src/lxc/confile.c | 4 +++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/lxc/apparmor.c b/src/lxc/apparmor.c index 11f32d550..a2d647672 100644 --- a/src/lxc/apparmor.c +++ b/src/lxc/apparmor.c @@ -36,6 +36,8 @@ again: f = fopen(path, "r"); if (!f) { SYSERROR("opening %s\n", path); + if (buf) + free(buf); return NULL; } sz += 1024; diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c index 812bfb80f..368214f24 100644 --- a/src/lxc/cgroup.c +++ b/src/lxc/cgroup.c @@ -69,8 +69,10 @@ static char *mount_has_subsystem(const struct mntent *mntent) return 0; /* skip the first line, which contains column headings */ - if (!fgets(line, MAXPATHLEN, f)) + if (!fgets(line, MAXPATHLEN, f)) { + fclose(f); return 0; + } while (fgets(line, MAXPATHLEN, f)) { c = strchr(line, '\t'); diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 33061df22..b6bdd936b 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -309,6 +309,7 @@ static int run_buffer(char *buffer) output = malloc(LXC_LOG_BUFFER_SIZE); if (!output) { ERROR("failed to allocate memory for script output"); + pclose(f); return -1; } @@ -535,6 +536,7 @@ static int mount_rootfs_file(const char *rootfs, const char *target) if (errno != ENXIO) { WARN("unexpected error for ioctl on '%s': %m", direntp->d_name); + close(fd); continue; } @@ -792,6 +794,7 @@ static int setup_rootfs_pivot_root_cb(char *buffer, void *data) listentry->elem = strdup(mountpoint); if (!listentry->elem) { SYSERROR("strdup failed"); + free(listentry); return -1; } lxc_list_add_tail(mountlist, listentry); @@ -1053,8 +1056,10 @@ int detect_shared_rootfs(void) if (strcmp(p+1, "/") == 0) { // this is '/'. is it shared? p = index(p2+1, ' '); - if (strstr(p, "shared:")) + if (strstr(p, "shared:")) { + fclose(f); return 1; + } } } fclose(f); diff --git a/src/lxc/confile.c b/src/lxc/confile.c index ba017074a..d0a404f4a 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -1249,8 +1249,10 @@ static int config_mount(const char *key, const char *value, return -1; mntelem = strdup(value); - if (!mntelem) + if (!mntelem) { + free(mntlist); return -1; + } mntlist->elem = mntelem; lxc_list_add_tail(&lxc_conf->mount_list, mntlist); From 4d44e274dcd933327c4f1c1cc7e1f876d08ffa85 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Sun, 14 Apr 2013 22:57:46 -0500 Subject: [PATCH 026/325] fix coverity-found errors. Signed-off-by: Serge Hallyn --- src/lxc/conf.c | 6 ++++-- src/lxc/namespace.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index b6bdd936b..c416da5fa 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -700,7 +700,8 @@ static int setup_tty(const struct lxc_rootfs *rootfs, SYSERROR("error creating %s\n", lxcpath); return -1; } - close(ret); + if (ret >= 0) + close(ret); ret = unlink(path); if (ret && errno != ENOENT) { SYSERROR("error unlinking %s\n", path); @@ -1314,7 +1315,8 @@ static int setup_ttydir_console(const struct lxc_rootfs *rootfs, SYSERROR("error %d creating %s\n", errno, lxcpath); return -1; } - close(ret); + if (ret >= 0) + close(ret); if (console->peer == -1) { INFO("no console output required"); diff --git a/src/lxc/namespace.c b/src/lxc/namespace.c index 644705472..cc237920f 100644 --- a/src/lxc/namespace.c +++ b/src/lxc/namespace.c @@ -53,7 +53,7 @@ pid_t lxc_clone(int (*fn)(void *), void *arg, int flags) .arg = arg, }; - long stack_size = sysconf(_SC_PAGESIZE); + size_t stack_size = sysconf(_SC_PAGESIZE); void *stack = alloca(stack_size); pid_t ret; From 03027ad99f2759182fbcd3363298ae6adaf88cdb Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 15 Apr 2013 16:05:36 -0400 Subject: [PATCH 027/325] fix lxc-attach usage This makes it match the manpage and be consistent with lxc-execute Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/lxc_attach.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c index 77039fbea..e5619df40 100644 --- a/src/lxc/lxc_attach.c +++ b/src/lxc/lxc_attach.c @@ -103,9 +103,9 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg) static struct lxc_arguments my_args = { .progname = "lxc-attach", .help = "\ ---name=NAME\n\ +--name=NAME [-- COMMAND]\n\ \n\ -Execute the specified command - enter the container NAME\n\ +Execute the specified COMMAND - enter the container NAME\n\ \n\ Options :\n\ -n, --name=NAME NAME for name of the container\n\ From 0a2188544a538b421612c90d44e56853a9d64458 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 15 Apr 2013 15:40:53 -0400 Subject: [PATCH 028/325] fortify: check the value returned from write(2) Also check that we wrote the amount we expected to. The write on the pty is blocking but we could still get a short write on EINTR, so we should SYSERROR it. Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/lxc_console.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lxc/lxc_console.c b/src/lxc/lxc_console.c index 3dd2155c6..643c44276 100644 --- a/src/lxc/lxc_console.c +++ b/src/lxc/lxc_console.c @@ -165,14 +165,18 @@ static int master_handler(int fd, void *data, struct lxc_epoll_descr *descr) { char buf[1024]; int *peer = (int *)data; - int r; + int r,w; r = read(fd, buf, sizeof(buf)); if (r < 0) { SYSERROR("failed to read"); return 1; } - r = write(*peer, buf, r); + w = write(*peer, buf, r); + if (w < 0 || w != r) { + SYSERROR("failed to write"); + return 1; + } return 0; } From e6a19d2683629888175371ed2eeb8a49a7b44873 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 15 Apr 2013 15:59:12 -0400 Subject: [PATCH 029/325] fortify: minor cleanups for unused variables, stricter types Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/log.c | 1 - src/lxc/log.h | 2 +- src/lxc/lxccontainer.c | 4 ++-- src/lxc/network.c | 1 - src/lxc/state.c | 7 ++++--- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/lxc/log.c b/src/lxc/log.c index 68a181c3a..3c6e4d188 100644 --- a/src/lxc/log.c +++ b/src/lxc/log.c @@ -244,7 +244,6 @@ extern int lxc_log_init(const char *name, const char *file, lxc_log_setprefix(prefix); if (file && strcmp(file, "none") == 0) { - want_lxc_log_specified = 1; return 0; } diff --git a/src/lxc/log.h b/src/lxc/log.h index 4252fa17f..cd43068a8 100644 --- a/src/lxc/log.h +++ b/src/lxc/log.h @@ -172,7 +172,7 @@ __lxc_log(const struct lxc_log_category* category, } /* - * Helper macro to define log fonctions. + * Helper macro to define log functions. */ #define lxc_log_priority_define(acategory, PRIORITY) \ \ diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index ce751ea41..1df6a9817 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -630,7 +630,7 @@ static bool lxcapi_create(struct lxc_container *c, char *t, char *const argv[]) newargv[nargs - 1] = NULL; /* execute */ - ret = execv(tpath, newargv); + execv(tpath, newargv); SYSERROR("failed to execute template %s", tpath); exit(1); } @@ -833,7 +833,7 @@ static bool lxcapi_destroy(struct lxc_container *c) if (pid < 0) return false; if (pid == 0) { // child - ret = execlp("lxc-destroy", "lxc-destroy", "-n", c->name, "-P", c->config_path, NULL); + execlp("lxc-destroy", "lxc-destroy", "-n", c->name, "-P", c->config_path, NULL); perror("execl"); exit(1); } diff --git a/src/lxc/network.c b/src/lxc/network.c index 3829757ec..93fc169c2 100644 --- a/src/lxc/network.c +++ b/src/lxc/network.c @@ -836,7 +836,6 @@ static int ip_addr_get(int family, int ifindex, void **res) err = netlink_send(&nlh, nlmsg); if (err < 0) goto out; - err = 0; do { /* Restore the answer buffer length, it might have been diff --git a/src/lxc/state.c b/src/lxc/state.c index 3e7e94afb..437f11aa6 100644 --- a/src/lxc/state.c +++ b/src/lxc/state.c @@ -56,7 +56,8 @@ const char *lxc_state2str(lxc_state_t state) lxc_state_t lxc_str2state(const char *state) { - int i, len; + size_t len; + lxc_state_t i; len = sizeof(strstate)/sizeof(strstate[0]); for (i = 0; i < len; i++) if (!strcmp(strstate[i], state)) @@ -66,7 +67,7 @@ lxc_state_t lxc_str2state(const char *state) return -1; } -static int freezer_state(const char *name, const char *lxcpath) +static lxc_state_t freezer_state(const char *name, const char *lxcpath) { char *nsgroup; char freezer[MAXPATHLEN]; @@ -132,7 +133,7 @@ static lxc_state_t __lxc_getstate(const char *name, const char *lxcpath) lxc_state_t lxc_getstate(const char *name, const char *lxcpath) { - int state = freezer_state(name, lxcpath); + lxc_state_t state = freezer_state(name, lxcpath); if (state != FROZEN && state != FREEZING) state = __lxc_getstate(name, lxcpath); return state; From 2796cf790f80e8be8dd90238f6789e52bd3cc2ac Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 15 Apr 2013 15:28:07 -0400 Subject: [PATCH 030/325] fortify: use reentrant safe strtok_r Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/conf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index c416da5fa..917c0526b 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -750,7 +750,7 @@ static int setup_tty(const struct lxc_rootfs *rootfs, static int setup_rootfs_pivot_root_cb(char *buffer, void *data) { struct lxc_list *mountlist, *listentry, *iterator; - char *pivotdir, *mountpoint, *mountentry; + char *pivotdir, *mountpoint, *mountentry, *saveptr; int found; void **cbparm; @@ -761,12 +761,12 @@ static int setup_rootfs_pivot_root_cb(char *buffer, void *data) pivotdir = cbparm[1]; /* parse entry, first field is mountname, ignore */ - mountpoint = strtok(mountentry, " "); + mountpoint = strtok_r(mountentry, " ", &saveptr); if (!mountpoint) return -1; /* second field is mountpoint */ - mountpoint = strtok(NULL, " "); + mountpoint = strtok_r(NULL, " ", &saveptr); if (!mountpoint) return -1; From 8e7da691af29fe1d8b93d2e4acc98eb188ae74cc Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 15 Apr 2013 13:43:14 -0400 Subject: [PATCH 031/325] fix checking hook script exit code pclose returns the exit status from wait, we need to check that to see if the script itself failed or not. Tested a script that returned 0, 1, and also one that did a sleep and then was killed by a signal. Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/conf.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 917c0526b..cf97eeffd 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -299,6 +299,7 @@ static int run_buffer(char *buffer) { FILE *f; char *output; + int ret; f = popen(buffer, "r"); if (!f) { @@ -318,9 +319,17 @@ static int run_buffer(char *buffer) free(output); - if (pclose(f) == -1) { + ret = pclose(f); + if (ret == -1) { SYSERROR("Script exited on error"); return -1; + } else if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0) { + ERROR("Script exited with status %d", WEXITSTATUS(ret)); + return -1; + } else if (WIFSIGNALED(ret)) { + ERROR("Script terminated by signal %d (%s)", WTERMSIG(ret), + strsignal(WTERMSIG(ret))); + return -1; } return 0; From ce4c4ca43586825a13c1abb4ce13e90d9447a0eb Mon Sep 17 00:00:00 2001 From: Bogdan Purcareata Date: Thu, 11 Apr 2013 16:29:44 +0300 Subject: [PATCH 032/325] lxc-template: enable chroot + chpasswd functionality for Busybox hosts This patch supports the scenario where a user wants to install a busybox container on a busybox host. When running the template, in order to change the root password, the template needs to do the chroot. On busybox-powered hosts, chroot is not part of the coreutils package - it's part of busybox. And the busybox implementation or chroot only works if it has /lib in the new root populated with the right binaries (or at least that's the solution I found to make it work). The temporarily bind-mounts /lib in the NEWROOT, chroots there, changes the password, goes back and unmounts. This set of operations is contained in a new MOUNT namespace, using the lxc-unshare call. Signed-off-by: Bogdan Purcareata Acked-by: Serge E. Hallyn --- templates/lxc-busybox.in | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/templates/lxc-busybox.in b/templates/lxc-busybox.in index cb425ecf8..2ca2bfd70 100644 --- a/templates/lxc-busybox.in +++ b/templates/lxc-busybox.in @@ -189,9 +189,35 @@ configure_busybox() # passwd exec must be setuid chmod +s $rootfs/bin/passwd touch $rootfs/etc/shadow - echo "setting root passwd to root" - echo "root:root" | chroot $rootfs chpasswd + # setting passwd for root + CHPASSWD_FILE=$rootfs/root/chpasswd.sh + + cat <$CHPASSWD_FILE +echo "setting root password to \"root\"" + +mount --bind /lib $rootfs/lib +if [ \$? -ne 0 ]; then + echo "Failed bind-mounting /lib at $rootfs/lib" + exit 1 +fi + +chroot $rootfs chpasswd </dev/null +root:root +EOFF + + +if [ \$? -ne 0 ]; then + echo "Failed to change root password" + exit 1 +fi + +umount $rootfs/lib + +EOF + + lxc-unshare -s MOUNT -- /bin/sh < $CHPASSWD_FILE + rm $CHPASSWD_FILE # add ssh functionality if dropbear package available on host which dropbear >/dev/null 2>&1 From 883f4a1eae77f332059dc0be6f965485a0361ec0 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 16 Apr 2013 07:35:05 -0500 Subject: [PATCH 033/325] mkdir_p: account for '//foo/bar' As Richard reported, dirname('//') returns //. But mkdir_p only stops when called with '/', resulting in infinite recursion when given a pathname '//foo/bar'. Reported-by: richard -rw- weinberger Signed-off-by: Serge Hallyn --- src/lxc/utils.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/lxc/utils.c b/src/lxc/utils.c index ecf9d2c77..b17f51de9 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -95,12 +95,21 @@ extern int get_u16(unsigned short *val, const char *arg, int base) return 0; } +static int is_all_slashes(char *path) +{ + while (*path && *path == '/') + path++; + if (*path) + return 0; + return 1; +} + extern int mkdir_p(char *dir, mode_t mode) { int ret; char *d; - if (!strcmp(dir, "/")) + if (is_all_slashes(dir)) return 0; d = strdup(dir); From 98663823e47ec56ff5a8205a17cc884acbf9cabd Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 16 Apr 2013 07:41:17 -0500 Subject: [PATCH 034/325] fix spacing Signed-off-by: Serge Hallyn --- src/lxc/utils.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/lxc/utils.c b/src/lxc/utils.c index b17f51de9..e07ca7b13 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -106,30 +106,30 @@ static int is_all_slashes(char *path) extern int mkdir_p(char *dir, mode_t mode) { - int ret; - char *d; + int ret; + char *d; if (is_all_slashes(dir)) - return 0; + return 0; - d = strdup(dir); - if (!d) - return -1; + d = strdup(dir); + if (!d) + return -1; - ret = mkdir_p(dirname(d), mode); - free(d); - if (ret) - return -1; + ret = mkdir_p(dirname(d), mode); + free(d); + if (ret) + return -1; - if (!access(dir, F_OK)) - return 0; + if (!access(dir, F_OK)) + return 0; - if (mkdir(dir, mode)) { - SYSERROR("failed to create directory '%s'\n", dir); - return -1; - } + if (mkdir(dir, mode)) { + SYSERROR("failed to create directory '%s'\n", dir); + return -1; + } - return 0; + return 0; } static char *copypath(char *p) From 2c7d90ac6eb4d883d9650d17cd915d958b4e5e66 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 16 Apr 2013 11:47:29 -0400 Subject: [PATCH 035/325] quiet gcc 4.4.7 warning about saveptr use before initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The recent change to use strtok_r causes a build warning with this older gcc version, so initialize saveptr to NULL to quiet the compiler and unbreak the build. There was no warning with gcc 4.7.2 that I originally tested with. Signed-off-by: Dwight Engen Acked-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 cf97eeffd..f89505003 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -759,7 +759,7 @@ static int setup_tty(const struct lxc_rootfs *rootfs, static int setup_rootfs_pivot_root_cb(char *buffer, void *data) { struct lxc_list *mountlist, *listentry, *iterator; - char *pivotdir, *mountpoint, *mountentry, *saveptr; + char *pivotdir, *mountpoint, *mountentry, *saveptr = NULL; int found; void **cbparm; From a81bad13ec305b885eff2934307d9205d55e0050 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Tue, 16 Apr 2013 23:48:15 +0200 Subject: [PATCH 036/325] init: unnest interrupt_handler There is no need to use nested functions voodoo. Signed-off-by: Richard Weinberger Acked-by: Serge E. Hallyn --- src/lxc/lxc_init.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/lxc/lxc_init.c b/src/lxc/lxc_init.c index c83c2f18b..e4c9a32de 100644 --- a/src/lxc/lxc_init.c +++ b/src/lxc/lxc_init.c @@ -49,15 +49,14 @@ static struct option options[] = { static int was_interrupted = 0; +static void interrupt_handler(int sig) +{ + if (!was_interrupted) + was_interrupted = sig; +} + int main(int argc, char *argv[]) { - - void interrupt_handler(int sig) - { - if (!was_interrupted) - was_interrupted = sig; - } - pid_t pid; int nbargs = 0; int err = -1; From 6b28a086310b8715f4655446f4c01d9555ef1786 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Tue, 16 Apr 2013 23:48:16 +0200 Subject: [PATCH 037/325] init: Fix whitespace damage While we are here, fix the whitespace damage. Signed-off-by: Richard Weinberger Acked-by: Serge E. Hallyn --- src/lxc/lxc_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/lxc_init.c b/src/lxc/lxc_init.c index e4c9a32de..84e12932e 100644 --- a/src/lxc/lxc_init.c +++ b/src/lxc/lxc_init.c @@ -47,7 +47,7 @@ static struct option options[] = { { 0, 0, 0, 0 }, }; -static int was_interrupted = 0; +static int was_interrupted = 0; static void interrupt_handler(int sig) { From 5a5c35c3a01afec515e688c8366e6f893985518d Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Tue, 16 Apr 2013 23:42:23 +0200 Subject: [PATCH 038/325] start: Detect early failure of the new child If the process in the new namespace dies very early we have currently no chance to detect this. The parent process will just die due to SIGPIPE if it write to the fd used for synchronisation and nobody will notice the real cause of the problem. Install a SIGCHLD handler to detect the death. Later when the child does execve() to the init within the new namespace the handler will be disabled automatically. Signed-off-by: Richard Weinberger Acked-by: Serge E. Hallyn --- src/lxc/start.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/lxc/start.c b/src/lxc/start.c index aefccd650..a58737a4f 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -198,6 +198,7 @@ static int setup_signal_fd(sigset_t *oldmask) sigdelset(&mask, SIGILL) || sigdelset(&mask, SIGSEGV) || sigdelset(&mask, SIGBUS) || + sigdelset(&mask, SIGCHLD) || sigprocmask(SIG_BLOCK, &mask, oldmask)) { SYSERROR("failed to set signal mask"); return -1; @@ -739,10 +740,29 @@ int save_phys_nics(struct lxc_conf *conf) return 0; } +static void sigchild_handler(int sig) +{ + int status; + pid_t child; + + child = wait(&status); + if (child < 0) { + SYSERROR("SIGCHLD caught but wait() failed: %m\n"); + return; + } + + if (WIFSIGNALED(status)) + ERROR("Process in the new namespace died before execve()" + " due to signal: %i", WTERMSIG(status)); + else if (WIFEXITED(status)) + ERROR("Process in the new namespace died before execve()" + " with exit code: %i", WIFEXITED(status)); +} int lxc_spawn(struct lxc_handler *handler) { int failed_before_rename = 0; + struct sigaction act; const char *name = handler->name; if (lxc_sync_init(handler)) @@ -793,6 +813,14 @@ int lxc_spawn(struct lxc_handler *handler) goto out_delete_net; } + /* + * Install a SIGCHLD handler to detect the death of the new child between + * clone() and execve(). + */ + memset(&act, 0, sizeof(act)); + act.sa_handler = sigchild_handler; + sigaction(SIGCHLD, &act, NULL); + /* Create a process in a new set of namespaces */ handler->pid = lxc_clone(do_start, handler, handler->clone_flags); if (handler->pid < 0) { From 8de4140644f01180f2fdab55b0ab0f13d1c761c6 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Wed, 17 Apr 2013 17:13:40 +0200 Subject: [PATCH 039/325] utils: reimplement/fix mkdir_p() Reimplement mkdir_p() such that it: ...handles relativ paths correctly. (currently it crashes) ...does not rely on dirname(). ...is not recursive. ...is shorter. ;-) Signed-off-by: Richard Weinberger Acked-by: Serge E. Hallyn --- src/lxc/utils.c | 46 ++++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/src/lxc/utils.c b/src/lxc/utils.c index e07ca7b13..979455347 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -95,39 +95,25 @@ extern int get_u16(unsigned short *val, const char *arg, int base) return 0; } -static int is_all_slashes(char *path) -{ - while (*path && *path == '/') - path++; - if (*path) - return 0; - return 1; -} - extern int mkdir_p(char *dir, mode_t mode) { - int ret; - char *d; + char *tmp = dir; + char *orig = dir; + char *makeme; - if (is_all_slashes(dir)) - return 0; - - d = strdup(dir); - if (!d) - return -1; - - ret = mkdir_p(dirname(d), mode); - free(d); - if (ret) - return -1; - - if (!access(dir, F_OK)) - return 0; - - if (mkdir(dir, mode)) { - SYSERROR("failed to create directory '%s'\n", dir); - return -1; - } + do { + dir = tmp + strspn(tmp, "/"); + tmp = dir + strcspn(dir, "/"); + makeme = strndupa(orig, dir - orig); + if (*makeme) { + if (!access(makeme, F_OK)) + return 0; + if (mkdir(makeme, mode)) { + SYSERROR("failed to create directory '%s'\n", makeme); + return -1; + } + } + } while(tmp != dir); return 0; } From 3763ee85915d28737bfebffa136bfb49ef0a2109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 18 Apr 2013 10:29:44 +0200 Subject: [PATCH 040/325] Revert "utils: reimplement/fix mkdir_p()" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 8de4140644f01180f2fdab55b0ab0f13d1c761c6. This commit was preventing container startup on my machine, making them all fail with various "No such file or directory" errors. Signed-off-by: Stéphane Graber --- src/lxc/utils.c | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/src/lxc/utils.c b/src/lxc/utils.c index 979455347..e07ca7b13 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -95,25 +95,39 @@ extern int get_u16(unsigned short *val, const char *arg, int base) return 0; } +static int is_all_slashes(char *path) +{ + while (*path && *path == '/') + path++; + if (*path) + return 0; + return 1; +} + extern int mkdir_p(char *dir, mode_t mode) { - char *tmp = dir; - char *orig = dir; - char *makeme; + int ret; + char *d; - do { - dir = tmp + strspn(tmp, "/"); - tmp = dir + strcspn(dir, "/"); - makeme = strndupa(orig, dir - orig); - if (*makeme) { - if (!access(makeme, F_OK)) - return 0; - if (mkdir(makeme, mode)) { - SYSERROR("failed to create directory '%s'\n", makeme); - return -1; - } - } - } while(tmp != dir); + if (is_all_slashes(dir)) + return 0; + + d = strdup(dir); + if (!d) + return -1; + + ret = mkdir_p(dirname(d), mode); + free(d); + if (ret) + return -1; + + if (!access(dir, F_OK)) + return 0; + + if (mkdir(dir, mode)) { + SYSERROR("failed to create directory '%s'\n", dir); + return -1; + } return 0; } From 23154d5764c06b68a5c154cecd89524ebe747ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 18 Apr 2013 10:30:33 +0200 Subject: [PATCH 041/325] Revert "start: Detect early failure of the new child" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 5a5c35c3a01afec515e688c8366e6f893985518d. This commit was preventing startup of containers using lxc hooks and shutdown of all other containers, requiring the use of a good old kill -9 to get rid of lxc-start after a container shutdown. Signed-off-by: Stéphane Graber --- src/lxc/start.c | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/lxc/start.c b/src/lxc/start.c index a58737a4f..aefccd650 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -198,7 +198,6 @@ static int setup_signal_fd(sigset_t *oldmask) sigdelset(&mask, SIGILL) || sigdelset(&mask, SIGSEGV) || sigdelset(&mask, SIGBUS) || - sigdelset(&mask, SIGCHLD) || sigprocmask(SIG_BLOCK, &mask, oldmask)) { SYSERROR("failed to set signal mask"); return -1; @@ -740,29 +739,10 @@ int save_phys_nics(struct lxc_conf *conf) return 0; } -static void sigchild_handler(int sig) -{ - int status; - pid_t child; - - child = wait(&status); - if (child < 0) { - SYSERROR("SIGCHLD caught but wait() failed: %m\n"); - return; - } - - if (WIFSIGNALED(status)) - ERROR("Process in the new namespace died before execve()" - " due to signal: %i", WTERMSIG(status)); - else if (WIFEXITED(status)) - ERROR("Process in the new namespace died before execve()" - " with exit code: %i", WIFEXITED(status)); -} int lxc_spawn(struct lxc_handler *handler) { int failed_before_rename = 0; - struct sigaction act; const char *name = handler->name; if (lxc_sync_init(handler)) @@ -813,14 +793,6 @@ int lxc_spawn(struct lxc_handler *handler) goto out_delete_net; } - /* - * Install a SIGCHLD handler to detect the death of the new child between - * clone() and execve(). - */ - memset(&act, 0, sizeof(act)); - act.sa_handler = sigchild_handler; - sigaction(SIGCHLD, &act, NULL); - /* Create a process in a new set of namespaces */ handler->pid = lxc_clone(do_start, handler, handler->clone_flags); if (handler->pid < 0) { From 860fc865b0ae0fd6381a8a9a777efdbde0aaefb6 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Wed, 17 Apr 2013 23:54:09 +0200 Subject: [PATCH 042/325] utils: reimplement/fix mkdir_p() (v2) Reimplement mkdir_p() such that it: ...handles relativ paths correctly. (currently it crashes) ...does not rely on dirname(). ...is not recursive. ...is shorter. ;-) Signed-off-by: Richard Weinberger Acked-by: Serge E. Hallyn --- src/lxc/utils.c | 44 ++++++++++++++------------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/src/lxc/utils.c b/src/lxc/utils.c index e07ca7b13..6a154f916 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -95,39 +95,23 @@ extern int get_u16(unsigned short *val, const char *arg, int base) return 0; } -static int is_all_slashes(char *path) -{ - while (*path && *path == '/') - path++; - if (*path) - return 0; - return 1; -} - extern int mkdir_p(char *dir, mode_t mode) { - int ret; - char *d; + char *tmp = dir; + char *orig = dir; + char *makeme; - if (is_all_slashes(dir)) - return 0; - - d = strdup(dir); - if (!d) - return -1; - - ret = mkdir_p(dirname(d), mode); - free(d); - if (ret) - return -1; - - if (!access(dir, F_OK)) - return 0; - - if (mkdir(dir, mode)) { - SYSERROR("failed to create directory '%s'\n", dir); - return -1; - } + do { + dir = tmp + strspn(tmp, "/"); + tmp = dir + strcspn(dir, "/"); + makeme = strndupa(orig, dir - orig); + if (*makeme) { + if (mkdir(makeme, mode) && errno != EEXIST) { + SYSERROR("failed to create directory '%s'\n", makeme); + return -1; + } + } + } while(tmp != dir); return 0; } From 2ebec36f271d4ee943281e32feb3552745115347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 12 Apr 2013 11:19:56 +0200 Subject: [PATCH 043/325] python: Lots of fixes in C extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes a lot of issues found by a code review done by Barry Warsaw. Those include: - Wrong signature for getters - Various memory leaks - Various optimizations - More consistent return values - Proper exception handling Signed-off-by: Stéphane Graber Reported-by: Barry Warsaw Acked-by: Barry Warsaw Acked-by: Serge E. Hallyn --- src/python-lxc/lxc.c | 270 +++++++++++++++++++++++++++++-------------- 1 file changed, 184 insertions(+), 86 deletions(-) diff --git a/src/python-lxc/lxc.c b/src/python-lxc/lxc.c index 8da6f36dc..85bab1126 100644 --- a/src/python-lxc/lxc.c +++ b/src/python-lxc/lxc.c @@ -34,13 +34,19 @@ typedef struct { char** convert_tuple_to_char_pointer_array(PyObject *argv) { - int argc = PyTuple_Size(argv); + int argc = PyTuple_GET_SIZE(argv); int i; char **result = (char**) malloc(sizeof(char*)*argc + 1); + if (result == NULL) { + PyErr_SetNone(PyExc_MemoryError); + return NULL; + } + for (i = 0; i < argc; i++) { - PyObject *pyobj = PyTuple_GetItem(argv, i); + PyObject *pyobj = PyTuple_GET_ITEM(argv, i); + assert(pyobj != NULL); char *str = NULL; PyObject *pystr = NULL; @@ -51,8 +57,17 @@ convert_tuple_to_char_pointer_array(PyObject *argv) { } pystr = PyUnicode_AsUTF8String(pyobj); + if (pystr == NULL) { + PyErr_SetString(PyExc_ValueError, "Unable to convert to UTF-8"); + free(result); + return NULL; + } + str = PyBytes_AsString(pystr); + assert(str != NULL); + memcpy((char *) &result[i], (char *) &str, sizeof(str)); + Py_DECREF(pystr); } result[argc] = NULL; @@ -82,18 +97,27 @@ Container_init(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"name", "config_path", NULL}; char *name = NULL; + PyObject *fs_config_path = NULL; char *config_path = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s", kwlist, - &name, &config_path)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O&", kwlist, + &name, + PyUnicode_FSConverter, &fs_config_path)) return -1; + if (fs_config_path != NULL) { + config_path = PyBytes_AS_STRING(fs_config_path); + assert(config_path != NULL); + } + self->container = lxc_container_new(name, config_path); if (!self->container) { - fprintf(stderr, "%d: error creating lxc_container %s\n", __LINE__, name); + Py_XDECREF(fs_config_path); + fprintf(stderr, "%d: error creating container %s\n", __LINE__, name); return -1; } + Py_XDECREF(fs_config_path); return 0; } @@ -111,13 +135,14 @@ LXC_get_version(PyObject *self, PyObject *args) // Container properties static PyObject * -Container_config_file_name(Container *self, PyObject *args, PyObject *kwds) +Container_config_file_name(Container *self, void *closure) { - return PyUnicode_FromString(self->container->config_file_name(self->container)); + return PyUnicode_FromString( + self->container->config_file_name(self->container)); } static PyObject * -Container_defined(Container *self, PyObject *args, PyObject *kwds) +Container_defined(Container *self, void *closure) { if (self->container->is_defined(self->container)) { Py_RETURN_TRUE; @@ -127,19 +152,19 @@ Container_defined(Container *self, PyObject *args, PyObject *kwds) } static PyObject * -Container_init_pid(Container *self, PyObject *args, PyObject *kwds) +Container_init_pid(Container *self, void *closure) { - return Py_BuildValue("i", self->container->init_pid(self->container)); + return PyLong_FromLong(self->container->init_pid(self->container)); } static PyObject * -Container_name(Container *self, PyObject *args, PyObject *kwds) +Container_name(Container *self, void *closure) { return PyUnicode_FromString(self->container->name); } static PyObject * -Container_running(Container *self, PyObject *args, PyObject *kwds) +Container_running(Container *self, void *closure) { if (self->container->is_running(self->container)) { Py_RETURN_TRUE; @@ -149,7 +174,7 @@ Container_running(Container *self, PyObject *args, PyObject *kwds) } static PyObject * -Container_state(Container *self, PyObject *args, PyObject *kwds) +Container_state(Container *self, void *closure) { return PyUnicode_FromString(self->container->state(self->container)); } @@ -161,9 +186,9 @@ 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, + if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &key)) - Py_RETURN_FALSE; + return NULL; if (self->container->clear_config_item(self->container, key)) { Py_RETURN_TRUE; @@ -177,27 +202,40 @@ Container_create(Container *self, PyObject *args, PyObject *kwds) { char* template_name = NULL; char** create_args = {NULL}; - PyObject *vargs = NULL; + PyObject *retval = NULL, *vargs = NULL; static char *kwlist[] = {"template", "args", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist, &template_name, &vargs)) - Py_RETURN_FALSE; + return NULL; - if (vargs && PyTuple_Check(vargs)) { - create_args = convert_tuple_to_char_pointer_array(vargs); - if (!create_args) { + if (vargs) { + if (PyTuple_Check(vargs)) { + create_args = convert_tuple_to_char_pointer_array(vargs); + if (!create_args) { + return NULL; + } + } + else { + PyErr_SetString(PyExc_ValueError, "args needs to be a tuple"); return NULL; } } - if (self->container->create(self->container, template_name, create_args)) { + if (self->container->create(self->container, template_name, create_args)) + retval = Py_True; + else + retval = Py_False; + + if (vargs) { + /* We cannot have gotten here unless vargs was given and create_args + * was successfully allocated. + */ free(create_args); - Py_RETURN_TRUE; } - free(create_args); - Py_RETURN_FALSE; + Py_INCREF(retval); + return retval; } static PyObject * @@ -228,20 +266,26 @@ Container_get_cgroup_item(Container *self, PyObject *args, PyObject *kwds) int len = 0; PyObject *ret = NULL; - if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, + if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &key)) - Py_RETURN_FALSE; + return NULL; len = self->container->get_cgroup_item(self->container, key, NULL, 0); - if (len <= 0) { - Py_RETURN_FALSE; + if (len < 0) { + PyErr_SetString(PyExc_KeyError, "Invalid cgroup entry"); + return NULL; } char* value = (char*) malloc(sizeof(char)*len + 1); - if (self->container->get_cgroup_item(self->container, key, value, len + 1) != len) { + if (value == NULL) + return PyErr_NoMemory(); + + if (self->container->get_cgroup_item(self->container, + key, value, len + 1) != len) { + PyErr_SetString(PyExc_ValueError, "Unable to read config value"); free(value); - Py_RETURN_FALSE; + return NULL; } ret = PyUnicode_FromString(value); @@ -259,18 +303,24 @@ Container_get_config_item(Container *self, PyObject *args, PyObject *kwds) if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, &key)) - Py_RETURN_FALSE; + return NULL; len = self->container->get_config_item(self->container, key, NULL, 0); - if (len <= 0) { - Py_RETURN_FALSE; + if (len < 0) { + PyErr_SetString(PyExc_KeyError, "Invalid configuration key"); + return NULL; } char* value = (char*) malloc(sizeof(char)*len + 1); - if (self->container->get_config_item(self->container, key, value, len + 1) != len) { - free(value); - Py_RETURN_FALSE; + if (value == NULL) + return PyErr_NoMemory(); + + if (self->container->get_config_item(self->container, + key, value, len + 1) != len) { + PyErr_SetString(PyExc_ValueError, "Unable to read config value"); + free(value); + return NULL; } ret = PyUnicode_FromString(value); @@ -281,7 +331,8 @@ Container_get_config_item(Container *self, PyObject *args, PyObject *kwds) static PyObject * Container_get_config_path(Container *self, PyObject *args, PyObject *kwds) { - return PyUnicode_FromString(self->container->get_config_path(self->container)); + return PyUnicode_FromString( + self->container->get_config_path(self->container)); } static PyObject * @@ -294,18 +345,24 @@ Container_get_keys(Container *self, PyObject *args, PyObject *kwds) if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, &key)) - Py_RETURN_FALSE; + return NULL; len = self->container->get_keys(self->container, key, NULL, 0); - if (len <= 0) { - Py_RETURN_FALSE; + if (len < 0) { + PyErr_SetString(PyExc_KeyError, "Invalid configuration key"); + return NULL; } char* value = (char*) malloc(sizeof(char)*len + 1); - if (self->container->get_keys(self->container, key, value, len + 1) != len) { + if (value == NULL) + return PyErr_NoMemory(); + + if (self->container->get_keys(self->container, + key, value, len + 1) != len) { + PyErr_SetString(PyExc_ValueError, "Unable to read config keys"); free(value); - Py_RETURN_FALSE; + return NULL; } ret = PyUnicode_FromString(value); @@ -317,16 +374,24 @@ static PyObject * Container_load_config(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"path", NULL}; + PyObject *fs_path = NULL; char* path = NULL; - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, - &path)) - Py_RETURN_FALSE; + if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist, + PyUnicode_FSConverter, &fs_path)) + return NULL; + + if (fs_path != NULL) { + path = PyBytes_AS_STRING(fs_path); + assert(path != NULL); + } if (self->container->load_config(self->container, path)) { + Py_XDECREF(fs_path); Py_RETURN_TRUE; } + Py_XDECREF(fs_path); Py_RETURN_FALSE; } @@ -334,16 +399,24 @@ static PyObject * Container_save_config(Container *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"path", NULL}; + PyObject *fs_path = NULL; char* path = NULL; - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, - &path)) - Py_RETURN_FALSE; + if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist, + PyUnicode_FSConverter, &fs_path)) + return NULL; + + if (fs_path != NULL) { + path = PyBytes_AS_STRING(fs_path); + assert(path != NULL); + } if (self->container->save_config(self->container, path)) { + Py_XDECREF(fs_path); Py_RETURN_TRUE; } + Py_XDECREF(fs_path); Py_RETURN_FALSE; } @@ -354,9 +427,9 @@ Container_set_cgroup_item(Container *self, PyObject *args, PyObject *kwds) char *key = NULL; char *value = NULL; - if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss|", kwlist, + if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, &key, &value)) - Py_RETURN_FALSE; + return NULL; if (self->container->set_cgroup_item(self->container, key, value)) { Py_RETURN_TRUE; @@ -372,9 +445,9 @@ Container_set_config_item(Container *self, PyObject *args, PyObject *kwds) char *key = NULL; char *value = NULL; - if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss|", kwlist, + if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, &key, &value)) - Py_RETURN_FALSE; + return NULL; if (self->container->set_config_item(self->container, key, value)) { Py_RETURN_TRUE; @@ -389,9 +462,9 @@ Container_set_config_path(Container *self, PyObject *args, PyObject *kwds) static char *kwlist[] = {"path", NULL}; char *path = NULL; - if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, + if (! PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &path)) - Py_RETURN_FALSE; + return NULL; if (self->container->set_config_path(self->container, path)) { Py_RETURN_TRUE; @@ -408,7 +481,7 @@ Container_shutdown(Container *self, PyObject *args, PyObject *kwds) if (! PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &timeout)) - Py_RETURN_FALSE; + return NULL; if (self->container->shutdown(self->container, timeout)) { Py_RETURN_TRUE; @@ -421,13 +494,13 @@ static PyObject * Container_start(Container *self, PyObject *args, PyObject *kwds) { char** init_args = {NULL}; - PyObject *useinit = NULL, *vargs = NULL; + PyObject *useinit = NULL, *retval = 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; + return NULL; if (useinit && useinit == Py_True) { init_useinit = 1; @@ -442,13 +515,20 @@ Container_start(Container *self, PyObject *args, PyObject *kwds) self->container->want_daemonize(self->container); - if (self->container->start(self->container, init_useinit, init_args)) { + if (self->container->start(self->container, init_useinit, init_args)) + retval = Py_True; + else + retval = Py_False; + + if (vargs) { + /* We cannot have gotten here unless vargs was given and create_args + * was successfully allocated. + */ free(init_args); - Py_RETURN_TRUE; } - free(init_args); - Py_RETURN_FALSE; + Py_INCREF(retval); + return retval; } static PyObject * @@ -480,7 +560,7 @@ Container_wait(Container *self, PyObject *args, PyObject *kwds) if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &state, &timeout)) - Py_RETURN_FALSE; + return NULL; if (self->container->wait(self->container, state, timeout)) { Py_RETURN_TRUE; @@ -491,125 +571,143 @@ Container_wait(Container *self, PyObject *args, PyObject *kwds) static PyGetSetDef Container_getseters[] = { {"config_file_name", - (getter)Container_config_file_name, 0, + (getter)Container_config_file_name, NULL, "Path to the container configuration", NULL}, {"defined", - (getter)Container_defined, 0, + (getter)Container_defined, NULL, "Boolean indicating whether the container configuration exists", NULL}, {"init_pid", - (getter)Container_init_pid, 0, + (getter)Container_init_pid, NULL, "PID of the container's init process in the host's PID namespace", NULL}, {"name", - (getter)Container_name, 0, + (getter)Container_name, NULL, "Container name", NULL}, {"running", - (getter)Container_running, 0, + (getter)Container_running, NULL, "Boolean indicating whether the container is running or not", NULL}, {"state", - (getter)Container_state, 0, + (getter)Container_state, NULL, "Container state", NULL}, {NULL, NULL, NULL, NULL, NULL} }; static PyMethodDef Container_methods[] = { - {"clear_config_item", (PyCFunction)Container_clear_config_item, METH_VARARGS | METH_KEYWORDS, + {"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", (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", (PyCFunction)Container_destroy, + METH_NOARGS, "destroy() -> boolean\n" "\n" "Destroys the container." }, - {"freeze", (PyCFunction)Container_freeze, METH_NOARGS, + {"freeze", (PyCFunction)Container_freeze, + METH_NOARGS, "freeze() -> boolean\n" "\n" "Freezes the container and returns its return code." }, - {"get_cgroup_item", (PyCFunction)Container_get_cgroup_item, METH_VARARGS | METH_KEYWORDS, + {"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", (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_config_path", (PyCFunction)Container_get_config_path, METH_NOARGS, + {"get_config_path", (PyCFunction)Container_get_config_path, + METH_NOARGS, "get_config_path() -> string\n" "\n" "Return the LXC config path (where the containers are stored)." }, - {"get_keys", (PyCFunction)Container_get_keys, METH_VARARGS | METH_KEYWORDS, + {"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", (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", (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_cgroup_item", (PyCFunction)Container_set_cgroup_item, METH_VARARGS | METH_KEYWORDS, + {"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", (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." }, - {"set_config_path", (PyCFunction)Container_set_config_path, METH_VARARGS | METH_KEYWORDS, + {"set_config_path", (PyCFunction)Container_set_config_path, + METH_VARARGS|METH_KEYWORDS, "set_config_path(path) -> boolean\n" "\n" "Set the LXC config path (where the containers are stored)." }, - {"shutdown", (PyCFunction)Container_shutdown, METH_VARARGS | METH_KEYWORDS, + {"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", (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", (PyCFunction)Container_stop, + METH_NOARGS, "stop() -> boolean\n" "\n" "Stop the container and returns its return code." }, - {"unfreeze", (PyCFunction)Container_unfreeze, METH_NOARGS, + {"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", (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." From 6c5db2af1f706e8f21f2a5f074bada96e9011052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 18 Apr 2013 22:20:53 +0200 Subject: [PATCH 044/325] python: Various fixes to the python scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a few issues uncovered by the recent C module fix. In lxc-start-ephemeral, the hwaddr code wasn't actually working. Replace by code that properly iterates through the network interfaces and sets a new MAC address for each entry. In the python overlay, catch the newly emitted KeyError when in set_config_item (or setting any previously unset variable would fail). Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/lxc/lxc-start-ephemeral.in | 4 +++- src/python-lxc/lxc/__init__.py | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/lxc/lxc-start-ephemeral.in b/src/lxc/lxc-start-ephemeral.in index bed86e6d2..b5cad9a4e 100644 --- a/src/lxc/lxc-start-ephemeral.in +++ b/src/lxc/lxc-start-ephemeral.in @@ -134,7 +134,9 @@ dest = lxc.Container(os.path.basename(dest_path), args.lxcpath) 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()) +for nic in dest.network: + if hasattr(nic, 'hwaddr'): + nic.hwaddr = randomMAC() overlay_dirs = [(orig.get_config_item("lxc.rootfs"), "%s/rootfs/" % dest_path)] diff --git a/src/python-lxc/lxc/__init__.py b/src/python-lxc/lxc/__init__.py index 118a08125..e3ce8ebf0 100644 --- a/src/python-lxc/lxc/__init__.py +++ b/src/python-lxc/lxc/__init__.py @@ -412,7 +412,10 @@ class Container(_lxc.Container): 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) + try: + old_value = self.get_config_item(key) + except KeyError: + old_value = None # Check if it's a list def set_key(key, value): From ed4616b1cfbc84dd01caa8546d813e8c5d482921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20B=C3=BChler?= Date: Sat, 20 Apr 2013 15:50:13 +0200 Subject: [PATCH 045/325] Use "uname -m" instead of "arch" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to "arch"'s manpage, it's identical to "uname -m". Some distros ship uname but don't ship arch, however all distros ship uname, therefore it makes sense to use "uname -m" whenever possible. Signed-off-by: Christian Bühler Acked-by: Stéphane Graber --- templates/lxc-altlinux.in | 2 +- templates/lxc-debian.in | 2 +- templates/lxc-fedora.in | 2 +- templates/lxc-opensuse.in | 2 +- templates/lxc-oracle.in | 2 +- templates/lxc-ubuntu-cloud.in | 4 ++-- templates/lxc-ubuntu.in | 4 ++-- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/templates/lxc-altlinux.in b/templates/lxc-altlinux.in index fac545cc3..da66ae78c 100644 --- a/templates/lxc-altlinux.in +++ b/templates/lxc-altlinux.in @@ -25,7 +25,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #Configurations -arch=$(arch) +arch=$(uname -m) cache_base=@LOCALSTATEDIR@/cache/lxc/altlinux/$arch default_path=@LXCPATH@ default_profile=default diff --git a/templates/lxc-debian.in b/templates/lxc-debian.in index e36523c26..568bc2cfb 100644 --- a/templates/lxc-debian.in +++ b/templates/lxc-debian.in @@ -175,7 +175,7 @@ install_debian() if which dpkg >/dev/null 2>&1 ; then arch=$(dpkg --print-architecture) else - arch=$(arch) + arch=$(uname -m) if [ "$arch" = "i686" ]; then arch="i386" elif [ "$arch" = "x86_64" ]; then diff --git a/templates/lxc-fedora.in b/templates/lxc-fedora.in index 684bb9cca..6f31e997e 100644 --- a/templates/lxc-fedora.in +++ b/templates/lxc-fedora.in @@ -26,7 +26,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #Configurations -arch=$(arch) +arch=$(uname -m) cache_base=@LOCALSTATEDIR@/cache/lxc/fedora/$arch default_path=@LXCPATH@ root_password=root diff --git a/templates/lxc-opensuse.in b/templates/lxc-opensuse.in index 77ef6b20e..af92cf5d1 100644 --- a/templates/lxc-opensuse.in +++ b/templates/lxc-opensuse.in @@ -212,7 +212,7 @@ install_opensuse() return 1 fi - arch=$(arch) + arch=$(uname -m) echo "Checking cache download in $cache/rootfs-$arch ... " if [ ! -e "$cache/rootfs-$arch" ]; then diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in index 16a622e13..946956d1c 100644 --- a/templates/lxc-oracle.in +++ b/templates/lxc-oracle.in @@ -615,7 +615,7 @@ if [ $? -ne 0 ]; then exit 1 fi -arch=$(arch) +arch=$(uname -m) eval set -- "$options" while true do diff --git a/templates/lxc-ubuntu-cloud.in b/templates/lxc-ubuntu-cloud.in index 8673e4cbb..22bce1f27 100644 --- a/templates/lxc-ubuntu-cloud.in +++ b/templates/lxc-ubuntu-cloud.in @@ -148,7 +148,7 @@ if [ -f /etc/lsb-release ]; then esac fi -arch=$(arch) +arch=$(uname -m) # Code taken from debootstrap if [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; then @@ -156,7 +156,7 @@ if [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; t elif type udpkg >/dev/null 2>&1 && udpkg --print-architecture >/dev/null 2>&1; then arch=`/usr/bin/udpkg --print-architecture` else - arch=$(arch) + arch=$(uname -m) if [ "$arch" = "i686" ]; then arch="i386" elif [ "$arch" = "x86_64" ]; then diff --git a/templates/lxc-ubuntu.in b/templates/lxc-ubuntu.in index db38af063..8c6593b15 100644 --- a/templates/lxc-ubuntu.in +++ b/templates/lxc-ubuntu.in @@ -605,7 +605,7 @@ if [ -f /etc/lsb-release ]; then fi bindhome= -arch=$(arch) +arch=$(uname -m) # Code taken from debootstrap if [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; then @@ -613,7 +613,7 @@ if [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; t elif which udpkg >/dev/null 2>&1 && udpkg --print-architecture >/dev/null 2>&1; then arch=`/usr/bin/udpkg --print-architecture` else - arch=$(arch) + arch=$(uname -m) if [ "$arch" = "i686" ]; then arch="i386" elif [ "$arch" = "x86_64" ]; then From a2abaa9ec60a8967611e8c8905698bd01bde5861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Sun, 21 Apr 2013 20:09:24 +0200 Subject: [PATCH 046/325] ubuntu: Various fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Drop disabled entries from allowed devices list - Improve generated config layout a bit - Drop redundant uname call - Re-generate the SSH host keys on container creation Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- templates/lxc-ubuntu-cloud.in | 14 +++++--------- templates/lxc-ubuntu.in | 23 ++++++++++++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/templates/lxc-ubuntu-cloud.in b/templates/lxc-ubuntu-cloud.in index 22bce1f27..d60f2c74f 100644 --- a/templates/lxc-ubuntu-cloud.in +++ b/templates/lxc-ubuntu-cloud.in @@ -70,8 +70,6 @@ lxc.cgroup.devices.allow = c 1:5 rwm # consoles lxc.cgroup.devices.allow = c 5:1 rwm lxc.cgroup.devices.allow = c 5:0 rwm -#lxc.cgroup.devices.allow = c 4:0 rwm -#lxc.cgroup.devices.allow = c 4:1 rwm # /dev/{,u}random lxc.cgroup.devices.allow = c 1:9 rwm lxc.cgroup.devices.allow = c 1:8 rwm @@ -79,15 +77,15 @@ lxc.cgroup.devices.allow = c 136:* rwm lxc.cgroup.devices.allow = c 5:2 rwm # rtc lxc.cgroup.devices.allow = c 254:0 rwm -#fuse +# fuse lxc.cgroup.devices.allow = c 10:229 rwm -#tun +# tun lxc.cgroup.devices.allow = c 10:200 rwm -#full +# full lxc.cgroup.devices.allow = c 1:7 rwm -#hpet +# hpet lxc.cgroup.devices.allow = c 10:228 rwm -#kvm +# kvm lxc.cgroup.devices.allow = c 10:232 rwm EOF @@ -148,8 +146,6 @@ if [ -f /etc/lsb-release ]; then esac fi -arch=$(uname -m) - # Code taken from debootstrap if [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; then arch=`/usr/bin/dpkg --print-architecture` diff --git a/templates/lxc-ubuntu.in b/templates/lxc-ubuntu.in index 8c6593b15..8a92f955b 100644 --- a/templates/lxc-ubuntu.in +++ b/templates/lxc-ubuntu.in @@ -36,7 +36,7 @@ configure_ubuntu() hostname=$2 release=$3 - # configure the network using the dhcp + # configure the network using the dhcp cat < $rootfs/etc/network/interfaces # This file describes the network interfaces available on your system # and how to activate them. For more information, see interfaces(5). @@ -91,6 +91,14 @@ EOF fi fi + # generate new SSH keys + if [ -x $rootfs@LOCALSTATEDIR@/lib/dpkg/info/openssh-server.postinst ]; then + rm -f $rootfs/etc/ssh/ssh_host_*key* + mv $rootfs/etc/init/ssh.conf $rootfs/etc/init/ssh.conf.disabled + chroot $rootfs @LOCALSTATEDIR@/lib/dpkg/info/openssh-server.postinst configure + mv $rootfs/etc/init/ssh.conf.disabled $rootfs/etc/init/ssh.conf + fi + return 0 } @@ -350,8 +358,6 @@ lxc.cgroup.devices.allow = c 1:5 rwm # consoles lxc.cgroup.devices.allow = c 5:1 rwm lxc.cgroup.devices.allow = c 5:0 rwm -#lxc.cgroup.devices.allow = c 4:0 rwm -#lxc.cgroup.devices.allow = c 4:1 rwm # /dev/{,u}random lxc.cgroup.devices.allow = c 1:9 rwm lxc.cgroup.devices.allow = c 1:8 rwm @@ -359,15 +365,15 @@ lxc.cgroup.devices.allow = c 136:* rwm lxc.cgroup.devices.allow = c 5:2 rwm # rtc lxc.cgroup.devices.allow = c 254:0 rwm -#fuse +# fuse lxc.cgroup.devices.allow = c 10:229 rwm -#tun +# tun lxc.cgroup.devices.allow = c 10:200 rwm -#full +# full lxc.cgroup.devices.allow = c 1:7 rwm -#hpet +# hpet lxc.cgroup.devices.allow = c 10:228 rwm -#kvm +# kvm lxc.cgroup.devices.allow = c 10:232 rwm EOF @@ -605,7 +611,6 @@ if [ -f /etc/lsb-release ]; then fi bindhome= -arch=$(uname -m) # Code taken from debootstrap if [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; then From 599d42525144cf0fcc7de6ac1b576c5c6ae290c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Sun, 21 Apr 2013 22:42:06 +0200 Subject: [PATCH 047/325] python: Fix get_ips and nesting with lxcpath MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using -P (lxcpath), the parameter path needs to be forwarded to the various commands being run but not used by the nested lxc-ls as it's relatively unlikely that both the host and the nested containers use a custom path. This isn't ideal but short of having a way to provide the container path for every single of the nesting (with potential unlimited depth), it's the best we can do. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/lxc/lxc-ls | 5 +++-- src/python-lxc/lxc/__init__.py | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lxc/lxc-ls b/src/lxc/lxc-ls index 4308edea7..d4e369b6f 100644 --- a/src/lxc/lxc-ls +++ b/src/lxc/lxc-ls @@ -89,12 +89,13 @@ def getTerminalSize(): def getSubContainers(container, lxcpath): - attach = ['lxc-attach', '-R', '-s', 'NETWORK|PID', '-n', container, + attach = ['lxc-attach', '-P', lxcpath, '-R', '-s', 'NETWORK|PID', + '-n', container, '--', sys.argv[0], "--nesting"] with open(os.devnull, "w") as fd: newenv = dict(os.environ) - newenv['NESTED'] = "/proc/1/root/%s" % lxcpath + newenv['NESTED'] = "/proc/1/root/%s" % lxc.default_config_path sp = subprocess.Popen(attach, stderr=fd, stdout=subprocess.PIPE, env=newenv, universal_newlines=True) sp.wait() diff --git a/src/python-lxc/lxc/__init__.py b/src/python-lxc/lxc/__init__.py index e3ce8ebf0..c235d1826 100644 --- a/src/python-lxc/lxc/__init__.py +++ b/src/python-lxc/lxc/__init__.py @@ -347,7 +347,8 @@ class Container(_lxc.Container): if count != 0: time.sleep(1) - base_cmd = ["lxc-attach", "-s", "NETWORK", "-n", self.name, "--", + base_cmd = ["lxc-attach", "-s", "NETWORK", + "-P", self.get_config_path(), "-n", self.name, "--", "ip"] # Get IPv6 From ddb17f1f0870ddb1678e34652f54458207cb3bb0 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 22 Apr 2013 11:16:57 -0400 Subject: [PATCH 048/325] make lxc_af_unix_open() safely return error on long pathnames Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/af_unix.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/lxc/af_unix.c b/src/lxc/af_unix.c index eff13d4fb..45fe128c8 100644 --- a/src/lxc/af_unix.c +++ b/src/lxc/af_unix.c @@ -36,6 +36,7 @@ lxc_log_define(lxc_af_unix, lxc); int lxc_af_unix_open(const char *path, int type, int flags) { int fd; + size_t len; struct sockaddr_un addr; if (flags & O_TRUNC) @@ -52,8 +53,16 @@ int lxc_af_unix_open(const char *path, int type, int flags) addr.sun_family = AF_UNIX; /* copy entire buffer in case of abstract socket */ - memcpy(addr.sun_path, path, - path[0]?strlen(path):sizeof(addr.sun_path)); + len = sizeof(addr.sun_path); + if (path[0]) { + len = strlen(path); + if (len >= sizeof(addr.sun_path)) { + close(fd); + errno = ENAMETOOLONG; + return -1; + } + } + memcpy(addr.sun_path, path, len); if (bind(fd, (struct sockaddr *)&addr, sizeof(addr))) { int tmp = errno; @@ -61,7 +70,7 @@ int lxc_af_unix_open(const char *path, int type, int flags) errno = tmp; return -1; } - + if (type == SOCK_STREAM && listen(fd, 100)) { int tmp = errno; close(fd); @@ -76,7 +85,7 @@ int lxc_af_unix_close(int fd) { struct sockaddr_un addr; socklen_t addrlen = sizeof(addr); - + if (!getsockname(fd, (struct sockaddr *)&addr, &addrlen) && addr.sun_path[0]) unlink(addr.sun_path); From 7e1667d76e76eb3d571be5e4b545e8ace6e92187 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Mon, 22 Apr 2013 15:40:57 -0500 Subject: [PATCH 049/325] cgpath test: don't check path len before checking if it is null Signed-off-by: Serge Hallyn --- src/tests/cgpath.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/tests/cgpath.c b/src/tests/cgpath.c index f05c43592..de2d13bb3 100644 --- a/src/tests/cgpath.c +++ b/src/tests/cgpath.c @@ -63,8 +63,7 @@ int main() free(path); path = lxc_cgroup_path_create("ab", MYNAME); - len = strlen(path); - if (!path || !len) { + if (!path || !(len = strlen(path))) { TSTERR("zero result from lxc_cgroup_path_create"); exit(1); } @@ -89,7 +88,7 @@ int main() c->set_config_item(c, "lxc.network.type", "empty"); if (!c->createl(c, "ubuntu", NULL)) { TSTERR("creating first container"); - exit(1); + goto out; } c->load_config(c, NULL); c->want_daemonize(c); @@ -141,7 +140,7 @@ int main() const char *dirpath; if (lxc_get_cgpath(&dirpath, NULL, c2->name, c2->config_path) < 0) { TSTERR("getting second container's cgpath"); - return -1; + goto out; } if (lxc_cgroup_nrtasks(dirpath) < 1) { From bbb8a488aeacf8a226d49773fe13798a202a78e2 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Mon, 22 Apr 2013 15:46:26 -0500 Subject: [PATCH 050/325] remove needless check for 'line' which cannot be NULl there (found by coverity) Signed-off-by: Serge Hallyn --- src/lxc/attach.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/attach.c b/src/lxc/attach.c index 5b3ee4fc1..a7e907475 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -358,7 +358,7 @@ char *lxc_attach_getpwshell(uid_t uid) continue; /* trim line on the right hand side */ - for (i = strlen(line); line && i > 0 && (line[i - 1] == '\n' || line[i - 1] == '\r'); --i) + for (i = strlen(line); i > 0 && (line[i - 1] == '\n' || line[i - 1] == '\r'); --i) line[i - 1] = '\0'; /* split into tokens: first user name */ From 051151de890705173a42bbead40a6125d34ea41b Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 22 Apr 2013 14:02:30 -0400 Subject: [PATCH 051/325] goto correct cleanup label to ensure fd is closed Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/start.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lxc/start.c b/src/lxc/start.c index aefccd650..0a0cc40f7 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -434,10 +434,10 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char goto out_close_maincmd_fd; } - /* Begin the set the state to STARTING*/ + /* Begin by setting the state to STARTING */ if (lxc_set_state(name, handler, STARTING)) { ERROR("failed to set state '%s'", lxc_state2str(STARTING)); - goto out_free_name; + goto out_close_maincmd_fd; } /* Start of environment variable setup for hooks */ From cf0f903326cf3cdd10f834c1bbc627fd81e06044 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 23 Apr 2013 08:37:41 -0500 Subject: [PATCH 052/325] detect APT_PROXY from host apt.conf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new HTTP_PROXY variable in /etc/default/lxc. If unset or set to none, then behavior continues as before. If set to 'apt', then any http::proxy set in apt.conf will be used as http_proxy for debootstrap, and specified in the container's /etc/apt/apt.conf.d/70proxy. If set to something else, then the value of HTTP_PROXY will be used as http_proxy for debootstrap and specified in the container's 70proxy. Changelog: (apr 23) merge the two apt proxy detection functions. Signed-off-by: Serge Hallyn Acked-by: Stéphane Graber --- templates/lxc-ubuntu.in | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/templates/lxc-ubuntu.in b/templates/lxc-ubuntu.in index 8a92f955b..83311fdde 100644 --- a/templates/lxc-ubuntu.in +++ b/templates/lxc-ubuntu.in @@ -134,12 +134,48 @@ finalize_user() return 0 } +# +# Choose proxies for container +# http_proxy will be used by debootstrap on the host. +# APT_PROXY will be used to set /etc/apt/apt.conf.d/70proxy in the container. +# +choose_container_proxy() +{ + local rootfs=$1 + local arch=$2 + + if [ -z "$HTTP_PROXY" ]; then + HTTP_PROXY="none" + fi + case "$HTTP_PROXY" in + none) + APT_PROXY= + ;; + apt) + RES=`apt-config shell APT_PROXY Acquire::http::Proxy` + eval $RES + [ -z "$APT_PROXY" ] || export http_proxy=$APT_PROXY + ;; + *) + APT_PROXY=$HTTP_PROXY + export http_proxy=$HTTP_PROXY + ;; + esac +} + write_sourceslist() { # $1 => path to the rootfs # $2 => architecture we want to add # $3 => whether to use the multi-arch syntax or not + if [ -n "$APT_PROXY" ]; then + mkdir -p $rootfs/etc/apt/apt.conf.d + cat > $rootfs/etc/apt/apt.conf.d/70proxy << EOF +Acquire::http::Proxy "$APT_PROXY" ; +EOF + fi + case $2 in amd64|i386) MIRROR=${MIRROR:-http://archive.ubuntu.com/ubuntu} @@ -206,6 +242,7 @@ download_ubuntu() return 1 fi + choose_container_proxy $cache/partial-$arch/ $arch # download a mini ubuntu into a cache echo "Downloading ubuntu $release minimal ..." if [ -n "$(which qemu-debootstrap)" ]; then From fd37327f57a6d53692babcaf69dfbd8f62e59918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=2E=C3=87a=C4=9Flar=20Onur?= Date: Wed, 17 Apr 2013 17:15:51 -0400 Subject: [PATCH 053/325] Support stopping containers concurrently MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trying to stop multiple containers concurrently ends up with "cgroup is not mounted" errors as multiple threads corrupts the shared variables. Fix that stack corruption and start to use getmntent_r to support stopping multiple containers concurrently. Signed-off-by: S.ÇaÄŸlar Onur Acked-by: Serge E. Hallyn --- src/lxc/cgroup.c | 156 ++++++++++++++++++++++++++++++---------------- src/lxc/freezer.c | 18 ++++-- src/lxc/state.c | 15 +++-- 3 files changed, 128 insertions(+), 61 deletions(-) diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c index 368214f24..b6f3e6ea1 100644 --- a/src/lxc/cgroup.c +++ b/src/lxc/cgroup.c @@ -54,6 +54,11 @@ lxc_log_define(lxc_cgroup, lxc); #define MTAB "/proc/mounts" +/* In the case of a bind mount, there could be two long pathnames in the + * mntent plus options so use large enough buffer size + */ +#define LARGE_MAXPATHLEN 4 * MAXPATHLEN + /* Check if a mount is a cgroup hierarchy for any subsystem. * Return the first subsystem found (or NULL if none). */ @@ -100,29 +105,31 @@ static char *mount_has_subsystem(const struct mntent *mntent) */ static int get_cgroup_mount(const char *subsystem, char *mnt) { - struct mntent *mntent; + struct mntent *mntent, mntent_r; FILE *file = NULL; int ret, err = -1; + char buf[LARGE_MAXPATHLEN] = {0}; + file = setmntent(MTAB, "r"); if (!file) { SYSERROR("failed to open %s", MTAB); return -1; } - while ((mntent = getmntent(file))) { - if (strcmp(mntent->mnt_type, "cgroup")) + while ((mntent = getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { + if (strcmp(mntent_r.mnt_type, "cgroup") != 0) continue; - + if (subsystem) { - if (!hasmntopt(mntent, subsystem)) + if (!hasmntopt(&mntent_r, subsystem)) continue; } else { - if (!mount_has_subsystem(mntent)) + if (!mount_has_subsystem(&mntent_r)) continue; } - ret = snprintf(mnt, MAXPATHLEN, "%s", mntent->mnt_dir); + ret = snprintf(mnt, MAXPATHLEN, "%s", mntent_r.mnt_dir); if (ret < 0 || ret >= MAXPATHLEN) goto fail; @@ -148,22 +155,33 @@ out: * * Returns 0 on success, -1 on error. * - * The answer is written in a static char[MAXPATHLEN] in this function and - * should not be freed. */ extern int cgroup_path_get(char **path, const char *subsystem, const char *cgpath) { - static char buf[MAXPATHLEN]; - static char retbuf[MAXPATHLEN]; int rc; + char *buf = NULL; + char *retbuf = NULL; + + buf = malloc(MAXPATHLEN * sizeof(char)); + if (!buf) { + ERROR("malloc failed"); + goto fail; + } + + retbuf = malloc(MAXPATHLEN * sizeof(char)); + if (!retbuf) { + ERROR("malloc failed"); + goto fail; + } + /* lxc_cgroup_set passes a state object for the subsystem, * so trim it to just the subsystem part */ if (subsystem) { rc = snprintf(retbuf, MAXPATHLEN, "%s", subsystem); if (rc < 0 || rc >= MAXPATHLEN) { ERROR("subsystem name too long"); - return -1; + goto fail; } char *s = index(retbuf, '.'); if (s) @@ -172,19 +190,28 @@ extern int cgroup_path_get(char **path, const char *subsystem, const char *cgpat } if (get_cgroup_mount(subsystem ? retbuf : NULL, buf)) { ERROR("cgroup is not mounted"); - return -1; + goto fail; } rc = snprintf(retbuf, MAXPATHLEN, "%s/%s", buf, cgpath); if (rc < 0 || rc >= MAXPATHLEN) { ERROR("name too long"); - return -1; + goto fail; } DEBUG("%s: returning %s for subsystem %s", __func__, retbuf, subsystem); + if(buf) + free(buf); + *path = retbuf; return 0; +fail: + if (buf) + free(buf); + if (retbuf) + free(retbuf); + return -1; } /* @@ -292,20 +319,25 @@ static int do_cgroup_set(const char *path, const char *value) int lxc_cgroup_set_bypath(const char *cgpath, const char *filename, const char *value) { int ret; - char *dirpath; + char *dirpath = NULL; char path[MAXPATHLEN]; ret = cgroup_path_get(&dirpath, filename, cgpath); if (ret) - return -1; + goto fail; ret = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename); if (ret < 0 || ret >= MAXPATHLEN) { ERROR("pathname too long"); - return -1; + goto fail; } return do_cgroup_set(path, value); + +fail: + if(dirpath) + free(dirpath); + return -1; } /* @@ -323,20 +355,25 @@ int lxc_cgroup_set(const char *name, const char *filename, const char *value, const char *lxcpath) { int ret; - char *dirpath; + char *dirpath = NULL; char path[MAXPATHLEN]; ret = lxc_cgroup_path_get(&dirpath, filename, name, lxcpath); if (ret) - return -1; + goto fail; ret = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename); if (ret < 0 || ret >= MAXPATHLEN) { ERROR("pathname too long"); - return -1; + goto fail; } return do_cgroup_set(path, value); + +fail: + if(dirpath) + free(dirpath); + return -1; } /* @@ -363,24 +400,24 @@ int lxc_cgroup_get(const char *name, const char *filename, char *value, size_t len, const char *lxcpath) { int fd, ret = -1; - char *dirpath; + char *dirpath = NULL; char path[MAXPATHLEN]; int rc; ret = lxc_cgroup_path_get(&dirpath, filename, name, lxcpath); if (ret) - return -1; + goto fail; rc = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename); if (rc < 0 || rc >= MAXPATHLEN) { ERROR("pathname too long"); - return -1; + goto fail; } fd = open(path, O_RDONLY); if (fd < 0) { ERROR("open %s : %s", path, strerror(errno)); - return -1; + goto fail; } if (!len || !value) { @@ -400,24 +437,28 @@ int lxc_cgroup_get(const char *name, const char *filename, char *value, close(fd); return ret; +fail: + if(dirpath) + free(dirpath); + return -1; } int lxc_cgroup_nrtasks(const char *cgpath) { - char *dpath; + char *dirpath = NULL; char path[MAXPATHLEN]; int pid, ret, count = 0; FILE *file; int rc; - ret = cgroup_path_get(&dpath, NULL, cgpath); + ret = cgroup_path_get(&dirpath, NULL, cgpath); if (ret) - return -1; + goto fail; - rc = snprintf(path, MAXPATHLEN, "%s/tasks", dpath); + rc = snprintf(path, MAXPATHLEN, "%s/tasks", dirpath); if (rc < 0 || rc >= MAXPATHLEN) { ERROR("pathname too long"); - return -1; + goto fail; } file = fopen(path, "r"); @@ -432,6 +473,10 @@ int lxc_cgroup_nrtasks(const char *cgpath) fclose(file); return count; +fail: + if(dirpath) + free(dirpath); + return -1; } /* @@ -472,33 +517,35 @@ static void set_clone_children(const char *mntdir) static int create_lxcgroups(const char *lxcgroup) { FILE *file = NULL; - struct mntent *mntent; + struct mntent *mntent, mntent_r; int ret, retv = -1; char path[MAXPATHLEN]; + char buf[LARGE_MAXPATHLEN] = {0}; + file = setmntent(MTAB, "r"); if (!file) { SYSERROR("failed to open %s", MTAB); return -1; } - while ((mntent = getmntent(file))) { + while ((mntent = getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { - if (strcmp(mntent->mnt_type, "cgroup")) + if (strcmp(mntent_r.mnt_type, "cgroup")) continue; - if (!mount_has_subsystem(mntent)) + if (!mount_has_subsystem(&mntent_r)) continue; - /* + /* * TODO - handle case where lxcgroup has subdirs? (i.e. build/l1) * We probably only want to support that for /users/joe */ ret = snprintf(path, MAXPATHLEN, "%s/%s", - mntent->mnt_dir, lxcgroup ? lxcgroup : "lxc"); + mntent_r.mnt_dir, lxcgroup ? lxcgroup : "lxc"); if (ret < 0 || ret >= MAXPATHLEN) goto fail; if (access(path, F_OK)) { - set_clone_children(mntent->mnt_dir); + set_clone_children(mntent_r.mnt_dir); ret = mkdir(path, 0755); if (ret == -1 && errno != EEXIST) { SYSERROR("failed to create '%s' directory", path); @@ -535,7 +582,7 @@ fail: * freezer cgroup's full path will be /sys/fs/cgroup/freezer/lxc/r1/. * * XXX This should probably be locked globally - * + * * Races won't be determintal, you'll just end up with leftover unused cgroups */ char *lxc_cgroup_path_create(const char *lxcgroup, const char *name) @@ -544,7 +591,9 @@ char *lxc_cgroup_path_create(const char *lxcgroup, const char *name) char *retpath, path[MAXPATHLEN]; char tail[12]; FILE *file = NULL; - struct mntent *mntent; + struct mntent *mntent, mntent_r; + + char buf[LARGE_MAXPATHLEN] = {0}; if (create_lxcgroups(lxcgroup) < 0) return NULL; @@ -561,15 +610,15 @@ again: else *tail = '\0'; - while ((mntent = getmntent(file))) { + while ((mntent = getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { - if (strcmp(mntent->mnt_type, "cgroup")) + if (strcmp(mntent_r.mnt_type, "cgroup")) continue; - if (!mount_has_subsystem(mntent)) + if (!mount_has_subsystem(&mntent_r)) continue; /* find unused mnt_dir + lxcgroup + name + -$i */ - ret = snprintf(path, MAXPATHLEN, "%s/%s/%s%s", mntent->mnt_dir, + ret = snprintf(path, MAXPATHLEN, "%s/%s/%s%s", mntent_r.mnt_dir, lxcgroup ? lxcgroup : "lxc", name, tail); if (ret < 0 || ret >= MAXPATHLEN) goto fail; @@ -609,8 +658,9 @@ int lxc_cgroup_enter(const char *cgpath, pid_t pid) { char path[MAXPATHLEN]; FILE *file = NULL, *fout; - struct mntent *mntent; + struct mntent *mntent, mntent_r; int ret, retv = -1; + char buf[LARGE_MAXPATHLEN] = {0}; file = setmntent(MTAB, "r"); if (!file) { @@ -618,13 +668,13 @@ int lxc_cgroup_enter(const char *cgpath, pid_t pid) return -1; } - while ((mntent = getmntent(file))) { - if (strcmp(mntent->mnt_type, "cgroup")) + while ((mntent = getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { + if (strcmp(mntent_r.mnt_type, "cgroup")) continue; - if (!mount_has_subsystem(mntent)) + if (!mount_has_subsystem(&mntent_r)) continue; ret = snprintf(path, MAXPATHLEN, "%s/%s/tasks", - mntent->mnt_dir, cgpath); + mntent_r.mnt_dir, cgpath); if (ret < 0 || ret >= MAXPATHLEN) { ERROR("entering cgroup"); goto out; @@ -716,23 +766,25 @@ static int lxc_one_cgroup_destroy(struct mntent *mntent, const char *cgpath) */ int lxc_cgroup_destroy(const char *cgpath) { - struct mntent *mntent; + struct mntent *mntent, mntent_r; FILE *file = NULL; int err, retv = 0; + char buf[LARGE_MAXPATHLEN] = {0}; + file = setmntent(MTAB, "r"); if (!file) { SYSERROR("failed to open %s", MTAB); return -1; } - while ((mntent = getmntent(file))) { - if (strcmp(mntent->mnt_type, "cgroup")) + while ((mntent = getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { + if (strcmp(mntent_r.mnt_type, "cgroup")) continue; - if (!mount_has_subsystem(mntent)) + if (!mount_has_subsystem(&mntent_r)) continue; - err = lxc_one_cgroup_destroy(mntent, cgpath); + err = lxc_one_cgroup_destroy(&mntent_r, cgpath); if (err) // keep trying to clean up the others retv = -1; } diff --git a/src/lxc/freezer.c b/src/lxc/freezer.c index 111bc3540..35bf3a764 100644 --- a/src/lxc/freezer.c +++ b/src/lxc/freezer.c @@ -120,14 +120,19 @@ out: static int freeze_unfreeze(const char *name, int freeze, const char *lxcpath) { - char *nsgroup; + char *nsgroup = NULL; int ret; ret = lxc_cgroup_path_get(&nsgroup, "freezer", name, lxcpath); if (ret) - return -1; + goto fail; return do_unfreeze(nsgroup, freeze, name, lxcpath); + +fail: + if (nsgroup) + free(nsgroup); + return -1; } int lxc_freeze(const char *name, const char *lxcpath) @@ -143,12 +148,17 @@ int lxc_unfreeze(const char *name, const char *lxcpath) int lxc_unfreeze_bypath(const char *cgpath) { - char *nsgroup; + char *nsgroup = NULL; int ret; ret = cgroup_path_get(&nsgroup, "freezer", cgpath); if (ret) - return -1; + goto fail; return do_unfreeze(nsgroup, 0, NULL, NULL); + +fail: + if (nsgroup) + free(nsgroup); + return -1; } diff --git a/src/lxc/state.c b/src/lxc/state.c index 437f11aa6..313cbb9ce 100644 --- a/src/lxc/state.c +++ b/src/lxc/state.c @@ -69,7 +69,7 @@ lxc_state_t lxc_str2state(const char *state) static lxc_state_t freezer_state(const char *name, const char *lxcpath) { - char *nsgroup; + char *nsgroup = NULL; char freezer[MAXPATHLEN]; char status[MAXPATHLEN]; FILE *file; @@ -77,25 +77,30 @@ static lxc_state_t freezer_state(const char *name, const char *lxcpath) err = lxc_cgroup_path_get(&nsgroup, "freezer", name, lxcpath); if (err) - return -1; + goto fail; err = snprintf(freezer, MAXPATHLEN, "%s/freezer.state", nsgroup); if (err < 0 || err >= MAXPATHLEN) - return -1; + goto fail; file = fopen(freezer, "r"); if (!file) - return -1; + goto fail; err = fscanf(file, "%s", status); fclose(file); if (err == EOF) { SYSERROR("failed to read %s", freezer); - return -1; + goto fail; } return lxc_str2state(status); + +fail: + if (nsgroup) + free(nsgroup); + return -1; } static lxc_state_t __lxc_getstate(const char *name, const char *lxcpath) From 93d564edc5d69819e85c3fa93368d37ec803a2f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 24 Apr 2013 11:41:45 +0200 Subject: [PATCH 054/325] cgroup: Remove unused mntent variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spotted by coverity, we were now assigning mntent but only every using mntent_r, so drop those variables and assignation. Signed-off-by: Stéphane Graber --- src/lxc/cgroup.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c index b6f3e6ea1..0c93703d6 100644 --- a/src/lxc/cgroup.c +++ b/src/lxc/cgroup.c @@ -105,7 +105,7 @@ static char *mount_has_subsystem(const struct mntent *mntent) */ static int get_cgroup_mount(const char *subsystem, char *mnt) { - struct mntent *mntent, mntent_r; + struct mntent mntent_r; FILE *file = NULL; int ret, err = -1; @@ -117,7 +117,7 @@ static int get_cgroup_mount(const char *subsystem, char *mnt) return -1; } - while ((mntent = getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { + while ((getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { if (strcmp(mntent_r.mnt_type, "cgroup") != 0) continue; @@ -517,7 +517,7 @@ static void set_clone_children(const char *mntdir) static int create_lxcgroups(const char *lxcgroup) { FILE *file = NULL; - struct mntent *mntent, mntent_r; + struct mntent mntent_r; int ret, retv = -1; char path[MAXPATHLEN]; @@ -529,7 +529,7 @@ static int create_lxcgroups(const char *lxcgroup) return -1; } - while ((mntent = getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { + while ((getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { if (strcmp(mntent_r.mnt_type, "cgroup")) continue; @@ -591,7 +591,7 @@ char *lxc_cgroup_path_create(const char *lxcgroup, const char *name) char *retpath, path[MAXPATHLEN]; char tail[12]; FILE *file = NULL; - struct mntent *mntent, mntent_r; + struct mntent mntent_r; char buf[LARGE_MAXPATHLEN] = {0}; @@ -610,7 +610,7 @@ again: else *tail = '\0'; - while ((mntent = getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { + while ((getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { if (strcmp(mntent_r.mnt_type, "cgroup")) continue; @@ -658,7 +658,7 @@ int lxc_cgroup_enter(const char *cgpath, pid_t pid) { char path[MAXPATHLEN]; FILE *file = NULL, *fout; - struct mntent *mntent, mntent_r; + struct mntent mntent_r; int ret, retv = -1; char buf[LARGE_MAXPATHLEN] = {0}; @@ -668,7 +668,7 @@ int lxc_cgroup_enter(const char *cgpath, pid_t pid) return -1; } - while ((mntent = getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { + while ((getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { if (strcmp(mntent_r.mnt_type, "cgroup")) continue; if (!mount_has_subsystem(&mntent_r)) @@ -766,7 +766,7 @@ static int lxc_one_cgroup_destroy(struct mntent *mntent, const char *cgpath) */ int lxc_cgroup_destroy(const char *cgpath) { - struct mntent *mntent, mntent_r; + struct mntent mntent_r; FILE *file = NULL; int err, retv = 0; @@ -778,7 +778,7 @@ int lxc_cgroup_destroy(const char *cgpath) return -1; } - while ((mntent = getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { + while ((getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { if (strcmp(mntent_r.mnt_type, "cgroup")) continue; if (!mount_has_subsystem(&mntent_r)) From 6516ad8b01aac298bffe60a8d7d21745f3354a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 24 Apr 2013 00:50:44 +0200 Subject: [PATCH 055/325] python: Fix convert_tuple_to_char_pointer_array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This finally fixes a few issues with the magic convert_tuple_to_char_pointer_array function. This now clearly copies the char* from the python object so we don't end up keeping reference to those. Also add the few required free calls to free the content of the array. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/python-lxc/lxc.c | 51 +++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/src/python-lxc/lxc.c b/src/python-lxc/lxc.c index 85bab1126..85710d642 100644 --- a/src/python-lxc/lxc.c +++ b/src/python-lxc/lxc.c @@ -35,7 +35,7 @@ typedef struct { char** convert_tuple_to_char_pointer_array(PyObject *argv) { int argc = PyTuple_GET_SIZE(argv); - int i; + int i, j; char **result = (char**) malloc(sizeof(char*)*argc + 1); @@ -49,30 +49,46 @@ convert_tuple_to_char_pointer_array(PyObject *argv) { assert(pyobj != NULL); char *str = NULL; - PyObject *pystr = NULL; + if (!PyUnicode_Check(pyobj)) { PyErr_SetString(PyExc_ValueError, "Expected a string"); - free(result); - return NULL; + goto error; } - pystr = PyUnicode_AsUTF8String(pyobj); - if (pystr == NULL) { - PyErr_SetString(PyExc_ValueError, "Unable to convert to UTF-8"); - free(result); - return NULL; + str = PyUnicode_AsUTF8(pyobj); + if (!str) { + /* Maybe it wasn't UTF-8 encoded. An exception is already set. */ + goto error; } - str = PyBytes_AsString(pystr); - assert(str != NULL); + /* We must make a copy of str, because it points into internal memory + * which we do not own. Assume it's NULL terminated, otherwise we'd + * have to use PyUnicode_AsUTF8AndSize() and be explicit about copying + * the memory. + */ + result[i] = strdup(str); - memcpy((char *) &result[i], (char *) &str, sizeof(str)); - Py_DECREF(pystr); + /* Do not decref pyobj since we stole a reference by using + * PyTuple_GET_ITEM(). + */ + if (result[i] == NULL) { + PyErr_SetNone(PyExc_MemoryError); + goto error; + } } result[argc] = NULL; - return result; + +error: + /* We can only iterate up to but not including i because malloc() does not + * initialize its memory. Thus if we got here, i points to the index + * after the last strdup'd entry in result. + */ + for (j = 0; j < i; j++) + free(result[j]); + free(result); + return NULL; } static void @@ -203,6 +219,7 @@ Container_create(Container *self, PyObject *args, PyObject *kwds) char* template_name = NULL; char** create_args = {NULL}; PyObject *retval = NULL, *vargs = NULL; + int i = 0; static char *kwlist[] = {"template", "args", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist, @@ -231,6 +248,8 @@ Container_create(Container *self, PyObject *args, PyObject *kwds) /* We cannot have gotten here unless vargs was given and create_args * was successfully allocated. */ + for (i = 0; i < PyTuple_GET_SIZE(vargs); i++) + free(create_args[i]); free(create_args); } @@ -495,7 +514,7 @@ Container_start(Container *self, PyObject *args, PyObject *kwds) { char** init_args = {NULL}; PyObject *useinit = NULL, *retval = NULL, *vargs = NULL; - int init_useinit = 0; + int init_useinit = 0, i = 0; static char *kwlist[] = {"useinit", "cmd", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, @@ -524,6 +543,8 @@ Container_start(Container *self, PyObject *args, PyObject *kwds) /* We cannot have gotten here unless vargs was given and create_args * was successfully allocated. */ + for (i = 0; i < PyTuple_GET_SIZE(vargs); i++) + free(init_args[i]); free(init_args); } From 15451ecf742bfa38a0732270b36d4a8666d2124e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 24 Apr 2013 17:24:26 +0200 Subject: [PATCH 056/325] python: Make the code compatibly with 3.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous change used some 3.3-specific functions. We still support 3.2 so revert to 3.2-compatible calls. Reported-by: S.ÇaÄŸlar Onur Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/python-lxc/lxc.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/python-lxc/lxc.c b/src/python-lxc/lxc.c index 85710d642..4e9fde7ed 100644 --- a/src/python-lxc/lxc.c +++ b/src/python-lxc/lxc.c @@ -49,18 +49,27 @@ convert_tuple_to_char_pointer_array(PyObject *argv) { assert(pyobj != NULL); char *str = NULL; + PyObject *pystr = NULL; if (!PyUnicode_Check(pyobj)) { PyErr_SetString(PyExc_ValueError, "Expected a string"); goto error; } - str = PyUnicode_AsUTF8(pyobj); - if (!str) { + pystr = PyUnicode_AsUTF8String(pyobj); + if (!pystr) { /* Maybe it wasn't UTF-8 encoded. An exception is already set. */ goto error; } + str = PyBytes_AsString(pystr); + if (!str) { + /* Maybe pystr wasn't a valid object. An exception is already set. + */ + Py_DECREF(pystr); + goto error; + } + /* We must make a copy of str, because it points into internal memory * which we do not own. Assume it's NULL terminated, otherwise we'd * have to use PyUnicode_AsUTF8AndSize() and be explicit about copying @@ -71,6 +80,7 @@ convert_tuple_to_char_pointer_array(PyObject *argv) { /* Do not decref pyobj since we stole a reference by using * PyTuple_GET_ITEM(). */ + Py_DECREF(pystr); if (result[i] == NULL) { PyErr_SetNone(PyExc_MemoryError); goto error; From 0a9362f5745a58a3d63354d76182108ea81ecf05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=2E=C3=87a=C4=9Flar=20Onur?= Date: Tue, 23 Apr 2013 17:24:31 -0400 Subject: [PATCH 057/325] Support starting containers concurrently MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trying to start multiple containers concurrently may cause lxc_monitor_read_timeout to fail as select call could be interrupted by a signal, handle it. Signed-off-by: S.ÇaÄŸlar Onur Acked-by: Stéphane Graber --- src/lxc/state.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lxc/state.c b/src/lxc/state.c index 313cbb9ce..4ab131a4c 100644 --- a/src/lxc/state.c +++ b/src/lxc/state.c @@ -236,8 +236,11 @@ extern int lxc_wait(const char *lxcname, const char *states, int timeout, const goto out_close; curtime = tv.tv_sec; } - if (lxc_monitor_read_timeout(fd, &msg, timeout) < 0) - goto out_close; + if (lxc_monitor_read_timeout(fd, &msg, timeout) < 0) { + /* try again if select interrupted by signal */ + if (errno != EINTR) + goto out_close; + } if (timeout != -1) { retval = gettimeofday(&tv, NULL); From dc7f65454ee88fbd50f4d6f8a7c567eb27107314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 24 Apr 2013 21:38:32 +0200 Subject: [PATCH 058/325] ubuntu: Don't break when the locale is C.* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the code to also match C.* so that C.UTF-8 doesn't make the container creation fail. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- templates/lxc-ubuntu.in | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/templates/lxc-ubuntu.in b/templates/lxc-ubuntu.in index 83311fdde..7100acc87 100644 --- a/templates/lxc-ubuntu.in +++ b/templates/lxc-ubuntu.in @@ -81,14 +81,12 @@ EOF fi # make sure we have the current locale defined in the container - if [ -z "$LANG" ]; then + if [ -z "$LANG" ] || echo $LANG | grep -E -q "^C(\..+)*$"; then chroot $rootfs locale-gen en_US.UTF-8 chroot $rootfs update-locale LANG=en_US.UTF-8 else - if [ "$LANG" != "C" ]; then - chroot $rootfs locale-gen $LANG - chroot $rootfs update-locale LANG=$LANG - fi + chroot $rootfs locale-gen $LANG + chroot $rootfs update-locale LANG=$LANG fi # generate new SSH keys From e51d4895129209cec1c15bda2322136a03ec94b2 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 24 Apr 2013 15:06:20 -0400 Subject: [PATCH 059/325] Allow multiple monitor clients This fixes a long standing issue that there could only be a single lxc-monitor per container. With this change, a new lxc-monitord daemon is spawned the first time lxc-monitor is called against the container and will accept connections from any subsequent lxc-monitor. Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- .gitignore | 1 + doc/lxc-monitor.sgml.in | 12 -- src/lxc/Makefile.am | 2 + src/lxc/lxc_console.c | 4 +- src/lxc/lxc_monitor.c | 2 + src/lxc/lxc_monitord.c | 409 ++++++++++++++++++++++++++++++++++++++++ src/lxc/lxccontainer.c | 6 +- src/lxc/mainloop.c | 7 +- src/lxc/mainloop.h | 7 +- src/lxc/monitor.c | 196 +++++++++++++------ src/lxc/monitor.h | 10 +- src/lxc/start.c | 4 +- src/lxc/utils.h | 26 +++ 13 files changed, 610 insertions(+), 76 deletions(-) create mode 100644 src/lxc/lxc_monitord.c diff --git a/.gitignore b/.gitignore index 905a2dc77..c614a7539 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,7 @@ src/lxc/lxc-info src/lxc/lxc-init src/lxc/lxc-kill src/lxc/lxc-monitor +src/lxc/lxc-monitord src/lxc/lxc-netstat src/lxc/lxc-ps src/lxc/lxc-restart diff --git a/doc/lxc-monitor.sgml.in b/doc/lxc-monitor.sgml.in index f9760a5a8..eae6f8233 100644 --- a/doc/lxc-monitor.sgml.in +++ b/doc/lxc-monitor.sgml.in @@ -67,18 +67,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA &commonoptions; - - Bugs - - - Only one lxc-monitor can run at a time. Other - invocations will fail with the following error: - - - lxc-monitor: bind : Address already in use - - - Examples diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index ebeca4663..1fa0fa88a 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -150,6 +150,7 @@ bin_PROGRAMS = \ lxc-start \ lxc-execute \ lxc-monitor \ + lxc-monitord \ lxc-wait \ lxc-console \ lxc-freeze \ @@ -181,6 +182,7 @@ lxc_freeze_SOURCES = lxc_freeze.c lxc_info_SOURCES = lxc_info.c lxc_init_SOURCES = lxc_init.c lxc_monitor_SOURCES = lxc_monitor.c +lxc_monitord_SOURCES = lxc_monitord.c lxc_restart_SOURCES = lxc_restart.c lxc_start_SOURCES = lxc_start.c lxc_stop_SOURCES = lxc_stop.c diff --git a/src/lxc/lxc_console.c b/src/lxc/lxc_console.c index 643c44276..f6659f64e 100644 --- a/src/lxc/lxc_console.c +++ b/src/lxc/lxc_console.c @@ -241,7 +241,7 @@ Type to exit the console, \ goto out_mainloop_open; } - err = lxc_mainloop(&descr); + err = lxc_mainloop(&descr, -1); if (err) { ERROR("mainloop returned an error"); goto out_mainloop_open; @@ -255,7 +255,7 @@ out_mainloop_open: out: /* Restore previous terminal parameter */ tcsetattr(0, TCSAFLUSH, &oldtios); - + /* Return to line it is */ printf("\n"); diff --git a/src/lxc/lxc_monitor.c b/src/lxc/lxc_monitor.c index 8c158698e..0ca829f67 100644 --- a/src/lxc/lxc_monitor.c +++ b/src/lxc/lxc_monitor.c @@ -87,6 +87,8 @@ int main(int argc, char *argv[]) return -1; } + lxc_monitord_spawn(my_args.lxcpath); + fd = lxc_monitor_open(my_args.lxcpath); if (fd < 0) return -1; diff --git a/src/lxc/lxc_monitord.c b/src/lxc/lxc_monitord.c new file mode 100644 index 000000000..3b5cf53b1 --- /dev/null +++ b/src/lxc/lxc_monitord.c @@ -0,0 +1,409 @@ +/* + * lxc: linux Container library + * + * Copyright © 2012 Oracle. + * + * Authors: + * 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 + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define CLIENTFDS_CHUNK 64 + +lxc_log_define(lxc_monitord, lxc); + +static struct lxc_monitor mon; + +static void lxc_monitord_cleanup(void); + +/* + * Defines the structure to store the monitor information + * @lxcpath : the path being monitored + * @fifofd : the file descriptor for publishers (containers) to write state + * @listenfd : the file descriptor for subscribers (lxc-monitors) to connect + * @clientfds : accepted client file descriptors + * @clientfds_size : number of file descriptors clientfds can hold + * @clientfds_cnt : the count of valid fds in clientfds + * @descr : the lxc_mainloop state + */ +struct lxc_monitor { + const char *lxcpath; + int fifofd; + int listenfd; + int *clientfds; + int clientfds_size; + int clientfds_cnt; + struct lxc_epoll_descr descr; +}; + +static int lxc_monitord_fifo_create(struct lxc_monitor *mon) +{ + char fifo_path[PATH_MAX]; + int ret; + + ret = snprintf(fifo_path, sizeof(fifo_path), "%s/monitor-fifo", mon->lxcpath); + if (ret < 0 || ret >= sizeof(fifo_path)) { + ERROR("lxcpath too long to monitor fifo"); + return -1; + } + + ret = mknod(fifo_path, S_IFIFO|S_IRUSR|S_IWUSR, 0); + if (ret < 0) { + INFO("monitor fifo %s exists, already running?", fifo_path); + return -1; + } + + mon->fifofd = open(fifo_path, O_RDWR); + if (mon->fifofd < 0) { + unlink(fifo_path); + ERROR("failed to open monitor fifo"); + return -1; + } + return 0; +} + +static int lxc_monitord_fifo_delete(struct lxc_monitor *mon) +{ + char fifo_path[PATH_MAX]; + int ret; + + ret = snprintf(fifo_path, sizeof(fifo_path), "%s/monitor-fifo", mon->lxcpath); + if (ret < 0 || ret >= sizeof(fifo_path)) { + ERROR("lxcpath too long to monitor fifo"); + return -1; + } + unlink(fifo_path); + return 0; +} + +static void lxc_monitord_sockfd_remove(struct lxc_monitor *mon, int fd) { + int i; + + if (lxc_mainloop_del_handler(&mon->descr, fd)) + CRIT("fd:%d not found in mainloop", fd); + close(fd); + + for (i = 0; i < mon->clientfds_cnt; i++) { + if (mon->clientfds[i] == fd) + break; + } + if (i >= mon->clientfds_cnt) { + CRIT("fd:%d not found in clients array", fd); + lxc_monitord_cleanup(); + exit(EXIT_FAILURE); + } + + memmove(&mon->clientfds[i], &mon->clientfds[i+1], + (mon->clientfds_cnt - i - 1) * sizeof(mon->clientfds[0])); + mon->clientfds_cnt--; +} + +static int lxc_monitord_sock_handler(int fd, void *data, + struct lxc_epoll_descr *descr) +{ + struct lxc_monitor *mon = data; + + lxc_monitord_sockfd_remove(mon, fd); + return 0; +} + +static int lxc_monitord_sock_accept(int fd, void *data, + struct lxc_epoll_descr *descr) +{ + int ret,clientfd; + struct lxc_monitor *mon = data; + struct ucred cred; + socklen_t credsz = sizeof(cred); + + ret = -1; + clientfd = accept(fd, NULL, 0); + if (clientfd < 0) { + SYSERROR("failed to accept connection"); + goto out; + } + + if (fcntl(clientfd, F_SETFD, FD_CLOEXEC)) { + SYSERROR("failed to set close-on-exec on incoming connection"); + goto err1; + } + + if (getsockopt(clientfd, SOL_SOCKET, SO_PEERCRED, &cred, &credsz)) + { + ERROR("failed to get credentials on socket"); + goto err1; + } + if (cred.uid && cred.uid != geteuid()) { + WARN("monitor denied for uid:%d", cred.uid); + ret = -EACCES; + goto err1; + } + + if (mon->clientfds_cnt + 1 > mon->clientfds_size) { + int *clientfds; + DEBUG("realloc space for %d clientfds", + mon->clientfds_size + CLIENTFDS_CHUNK); + clientfds = realloc(mon->clientfds, + (mon->clientfds_size + CLIENTFDS_CHUNK) * + sizeof(mon->clientfds[0])); + if (clientfds == NULL) { + ERROR("failed to realloc memory for clientfds"); + goto err1; + } + mon->clientfds = clientfds; + mon->clientfds_size += CLIENTFDS_CHUNK; + } + + ret = lxc_mainloop_add_handler(&mon->descr, clientfd, + lxc_monitord_sock_handler, mon); + if (ret) { + ERROR("failed to add socket handler"); + goto err1; + } + + mon->clientfds[mon->clientfds_cnt++] = clientfd; + INFO("accepted client fd:%d clients:%d", clientfd, mon->clientfds_cnt); + goto out; + +err1: + close(clientfd); +out: + return ret; +} + +static int lxc_monitord_sock_create(struct lxc_monitor *mon) +{ + struct sockaddr_un addr; + int fd; + + if (lxc_monitor_sock_name(mon->lxcpath, &addr) < 0) + return -1; + + fd = lxc_af_unix_open(addr.sun_path, SOCK_STREAM, O_TRUNC); + if (fd < 0) { + ERROR("failed to open unix socket : %s", strerror(errno)); + return -1; + } + + mon->listenfd = fd; + return 0; +} + +static int lxc_monitord_sock_delete(struct lxc_monitor *mon) +{ + struct sockaddr_un addr; + + if (lxc_monitor_sock_name(mon->lxcpath, &addr) < 0) + return -1; + if (addr.sun_path[0]) + unlink(addr.sun_path); + return 0; +} + +static int lxc_monitord_create(struct lxc_monitor *mon) +{ + int ret; + + ret = lxc_monitord_fifo_create(mon); + if (ret < 0) + return ret; + + ret = lxc_monitord_sock_create(mon); + return ret; +} + +static void lxc_monitord_delete(struct lxc_monitor *mon) +{ + int i; + + lxc_mainloop_del_handler(&mon->descr, mon->listenfd); + close(mon->listenfd); + lxc_monitord_sock_delete(mon); + + lxc_mainloop_del_handler(&mon->descr, mon->fifofd); + close(mon->fifofd); + lxc_monitord_fifo_delete(mon); + + for (i = 0; i < mon->clientfds_cnt; i++) { + lxc_mainloop_del_handler(&mon->descr, mon->clientfds[i]); + close(mon->clientfds[i]); + } + mon->clientfds_cnt = 0; +} + +static int lxc_monitord_fifo_handler(int fd, void *data, + struct lxc_epoll_descr *descr) +{ + int ret,i; + struct lxc_msg msglxc; + struct lxc_monitor *mon = data; + + ret = read(fd, &msglxc, sizeof(msglxc)); + if (ret != sizeof(msglxc)) { + SYSERROR("read fifo failed : %s", strerror(errno)); + return 1; + } + + for (i = 0; i < mon->clientfds_cnt; i++) { + DEBUG("writing client fd:%d", mon->clientfds[i]); + ret = write(mon->clientfds[i], &msglxc, sizeof(msglxc)); + if (ret < 0) { + ERROR("write failed to client sock:%d %d %s", + mon->clientfds[i], errno, strerror(errno)); + } + } + + return 0; +} + +static int lxc_monitord_mainloop_add(struct lxc_monitor *mon) +{ + int ret; + + ret = lxc_mainloop_add_handler(&mon->descr, mon->fifofd, + lxc_monitord_fifo_handler, mon); + if (ret < 0) { + ERROR("failed to add to mainloop monitor handler for fifo"); + return -1; + } + + ret = lxc_mainloop_add_handler(&mon->descr, mon->listenfd, + lxc_monitord_sock_accept, mon); + if (ret < 0) { + ERROR("failed to add to mainloop monitor handler for listen socket"); + return -1; + } + + return 0; +} + +static void lxc_monitord_cleanup(void) +{ + lxc_monitord_delete(&mon); +} + +static void lxc_monitord_sig_handler(int sig) +{ + INFO("caught signal %d", sig); + lxc_monitord_cleanup(); + exit(EXIT_SUCCESS); +} + +int main(int argc, char *argv[]) +{ + int ret,pipefd; + char *lxcpath = argv[1]; + char logpath[PATH_MAX]; + sigset_t mask; + + if (argc != 3) { + fprintf(stderr, + "Usage: lxc-monitord lxcpath sync-pipe-fd\n\n" + "NOTE: lxc-monitord is intended for use by lxc internally\n" + " and does not need to be run by hand\n\n"); + exit(EXIT_FAILURE); + } + + ret = snprintf(logpath, sizeof(logpath), "%s/lxc-monitord.log", + lxcpath); + if (ret < 0 || ret >= sizeof(logpath)) + return EXIT_FAILURE; + + ret = lxc_log_init(NULL, logpath, "NOTICE", "lxc-monitord", 0); + if (ret) + return ret; + + pipefd = atoi(argv[2]); + + if (sigfillset(&mask) || + sigdelset(&mask, SIGILL) || + sigdelset(&mask, SIGSEGV) || + sigdelset(&mask, SIGBUS) || + sigdelset(&mask, SIGTERM) || + sigprocmask(SIG_BLOCK, &mask, NULL)) { + SYSERROR("failed to set signal mask"); + return -1; + } + + signal(SIGILL, lxc_monitord_sig_handler); + signal(SIGSEGV, lxc_monitord_sig_handler); + signal(SIGBUS, lxc_monitord_sig_handler); + signal(SIGTERM, lxc_monitord_sig_handler); + + ret = EXIT_FAILURE; + memset(&mon, 0, sizeof(mon)); + mon.lxcpath = lxcpath; + if (lxc_mainloop_open(&mon.descr)) { + ERROR("failed to create mainloop"); + goto out; + } + + if (lxc_monitord_create(&mon)) { + goto out; + } + + /* sync with parent, we're ignoring the return from write + * because regardless if it works or not, the following + * close will sync us with the parent process. the + * if-empty-statement construct is to quiet the + * warn-unused-result warning. + */ + if (write(pipefd, "S", 1)) ; + close(pipefd); + + if (lxc_monitord_mainloop_add(&mon)) { + ERROR("failed to add mainloop handlers"); + goto out; + } + + NOTICE("monitoring lxcpath %s", mon.lxcpath); + for(;;) { + ret = lxc_mainloop(&mon.descr, 1000 * 30); + if (mon.clientfds_cnt <= 0) + { + NOTICE("no clients for 30 seconds, exiting"); + break; + } + } + + lxc_mainloop_close(&mon.descr); + lxc_monitord_cleanup(); + ret = EXIT_SUCCESS; + NOTICE("monitor exiting"); +out: + return ret; +} diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 1df6a9817..53765b04c 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -32,6 +32,7 @@ #include #include #include +#include lxc_log_define(lxc_container, lxc); @@ -370,6 +371,7 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv if (daemonize) { if (!lxc_container_get(c)) return false; + lxc_monitord_spawn(c->config_path); pid_t pid = fork(); if (pid < 0) { lxc_container_put(c); @@ -560,7 +562,7 @@ static bool lxcapi_create(struct lxc_container *c, char *t, char *const argv[]) } /* container is already created if we have a config and rootfs.path is accessible */ - if (lxcapi_is_defined(c) && c->lxc_conf && c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) == 0) + if (lxcapi_is_defined(c) && c->lxc_conf && c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) == 0) goto out; /* we're going to fork. but since we'll wait for our child, we @@ -826,7 +828,7 @@ static bool lxcapi_destroy(struct lxc_container *c) return false; /* container is already destroyed if we don't have a config and rootfs.path is not accessible */ - if (!lxcapi_is_defined(c) && (!c->lxc_conf || !c->lxc_conf->rootfs.path || access(c->lxc_conf->rootfs.path, F_OK) != 0)) + if (!lxcapi_is_defined(c) && (!c->lxc_conf || !c->lxc_conf->rootfs.path || access(c->lxc_conf->rootfs.path, F_OK) != 0)) return false; pid = fork(); diff --git a/src/lxc/mainloop.c b/src/lxc/mainloop.c index 975215db4..d9ab5d1af 100644 --- a/src/lxc/mainloop.c +++ b/src/lxc/mainloop.c @@ -38,7 +38,7 @@ struct mainloop_handler { #define MAX_EVENTS 10 -int lxc_mainloop(struct lxc_epoll_descr *descr) +int lxc_mainloop(struct lxc_epoll_descr *descr, int timeout_ms) { int i, nfds; struct mainloop_handler *handler; @@ -46,7 +46,7 @@ int lxc_mainloop(struct lxc_epoll_descr *descr) for (;;) { - nfds = epoll_wait(descr->epfd, events, MAX_EVENTS, -1); + nfds = epoll_wait(descr->epfd, events, MAX_EVENTS, timeout_ms); if (nfds < 0) { if (errno == EINTR) continue; @@ -64,6 +64,9 @@ int lxc_mainloop(struct lxc_epoll_descr *descr) return 0; } + if (nfds == 0 && timeout_ms != 0) + return 0; + if (lxc_list_empty(&descr->handlers)) return 0; } diff --git a/src/lxc/mainloop.h b/src/lxc/mainloop.h index 6b16242aa..ec875693a 100644 --- a/src/lxc/mainloop.h +++ b/src/lxc/mainloop.h @@ -21,6 +21,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _mainloop_h +#define _mainloop_h + #include "list.h" struct lxc_epoll_descr { @@ -31,7 +34,7 @@ struct lxc_epoll_descr { typedef int (*lxc_mainloop_callback_t)(int fd, void *data, struct lxc_epoll_descr *descr); -extern int lxc_mainloop(struct lxc_epoll_descr *descr); +extern int lxc_mainloop(struct lxc_epoll_descr *descr, int timeout_ms); extern int lxc_mainloop_add_handler(struct lxc_epoll_descr *descr, int fd, lxc_mainloop_callback_t callback, @@ -42,3 +45,5 @@ extern int lxc_mainloop_del_handler(struct lxc_epoll_descr *descr, int fd); extern int lxc_mainloop_open(struct lxc_epoll_descr *descr); extern int lxc_mainloop_close(struct lxc_epoll_descr *descr); + +#endif diff --git a/src/lxc/monitor.c b/src/lxc/monitor.c index afdaf67a2..e108eb764 100644 --- a/src/lxc/monitor.c +++ b/src/lxc/monitor.c @@ -5,6 +5,7 @@ * * Authors: * Daniel Lezcano + * Dwight Engen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,6 +21,7 @@ * 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 #include @@ -30,7 +32,7 @@ #include #include #include -#include +#include #include #include @@ -40,37 +42,36 @@ #include #include #include +#include lxc_log_define(lxc_monitor, lxc); -#ifndef UNIX_PATH_MAX -#define UNIX_PATH_MAX 108 -#endif - -static void lxc_monitor_send(struct lxc_msg *msg, const char *lxcpath) +/* routines used by monitor publishers (containers) */ +static void lxc_monitor_fifo_send(struct lxc_msg *msg, const char *lxcpath) { - int fd; - struct sockaddr_un addr = { .sun_family = AF_UNIX }; - char *offset = &addr.sun_path[1]; - size_t ret, len; + int fd,ret; + char fifo_path[PATH_MAX]; - /* - * addr.sun_path is only 108 bytes. - * should we take a hash of lxcpath? a subset of it? - */ - len = sizeof(addr.sun_path) - 1; - ret = snprintf(offset, len, "%s/lxc-monitor", lxcpath); - if (ret < 0 || ret >= len) { - ERROR("lxcpath too long to open monitor"); + BUILD_BUG_ON(sizeof(*msg) > PIPE_BUF); /* write not guaranteed atomic */ + ret = snprintf(fifo_path, sizeof(fifo_path), "%s/monitor-fifo", lxcpath); + if (ret < 0 || ret >= sizeof(fifo_path)) { + ERROR("lxcpath too long to open monitor fifo"); return; } - fd = socket(PF_UNIX, SOCK_DGRAM, 0); - if (fd < 0) + fd = open(fifo_path, O_WRONLY); + if (fd < 0) { + /* it is normal for this open to fail when there is no monitor + * running, so we don't log it + */ return; + } - sendto(fd, msg, sizeof(*msg), 0, - (const struct sockaddr *)&addr, sizeof(addr)); + ret = write(fd, msg, sizeof(*msg)); + if (ret != sizeof(*msg)) { + SYSERROR("failed to write monitor fifo %s", fifo_path); + return; + } close(fd); } @@ -82,50 +83,74 @@ void lxc_monitor_send_state(const char *name, lxc_state_t state, const char *lxc strncpy(msg.name, name, sizeof(msg.name)); msg.name[sizeof(msg.name) - 1] = 0; - lxc_monitor_send(&msg, lxcpath); + lxc_monitor_fifo_send(&msg, lxcpath); +} + + +/* routines used by monitor subscribers (lxc-monitor) */ +int lxc_monitor_close(int fd) +{ + return close(fd); +} + +int lxc_monitor_sock_name(const char *lxcpath, struct sockaddr_un *addr) { + size_t len; + int ret; + char *sockname = &addr->sun_path[0]; // 1 for abstract + + /* addr.sun_path is only 108 bytes. + * should we take a hash of lxcpath? a subset of it? ftok()? we need + * to make sure it is unique. + */ + memset(addr, 0, sizeof(*addr)); + addr->sun_family = AF_UNIX; + len = sizeof(addr->sun_path) - 1; + ret = snprintf(sockname, len, "%s/monitor-sock", lxcpath); + if (ret < 0 || ret >= len) { + ERROR("lxcpath too long for unix socket"); + return -1; + } + return 0; } int lxc_monitor_open(const char *lxcpath) { - struct sockaddr_un addr = { .sun_family = AF_UNIX }; - char *offset = &addr.sun_path[1]; - int fd; - size_t ret, len; + struct sockaddr_un addr; + int fd,ret; + int retry,backoff_ms[] = {10, 50, 100}; - /* - * addr.sun_path is only 108 bytes. - * should we take a hash of lxcpath? a subset of it? - */ - len = sizeof(addr.sun_path) - 1; - ret = snprintf(offset, len, "%s/lxc-monitor", lxcpath); - if (ret < 0 || ret >= len) { - ERROR("lxcpath too long to open monitor"); + if (lxc_monitor_sock_name(lxcpath, &addr) < 0) return -1; - } - fd = socket(PF_UNIX, SOCK_DGRAM, 0); + fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) { ERROR("socket : %s", strerror(errno)); return -1; } - if (bind(fd, (struct sockaddr *)&addr, sizeof(addr))) { - ERROR("bind : %s", strerror(errno)); - close(fd); - return -1; + for (retry = 0; retry < sizeof(backoff_ms)/sizeof(backoff_ms[0]); retry++) { + ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (ret == 0 || errno != ECONNREFUSED) + break; + ERROR("connect : backing off %d", backoff_ms[retry]); + usleep(backoff_ms[retry] * 1000); } + if (ret < 0) { + ERROR("connect : %s", strerror(errno)); + goto err1; + } return fd; +err1: + close(fd); + return ret; } -/* timeout of 0 means return immediately; -1 means wait forever */ -int lxc_monitor_read_timeout(int fd, struct lxc_msg *msg, int timeout) +int lxc_monitor_read_timeout(int fd, struct lxc_msg *msglxc, int timeout) { - struct sockaddr_un from; - socklen_t len = sizeof(from); - int ret; fd_set rfds; struct timeval tv; + int ret; if (timeout != -1) { FD_ZERO(&rfds); @@ -141,13 +166,12 @@ int lxc_monitor_read_timeout(int fd, struct lxc_msg *msg, int timeout) return -2; // timed out } - ret = recvfrom(fd, msg, sizeof(*msg), 0, - (struct sockaddr *)&from, &len); - if (ret < 0) { - SYSERROR("failed to receive state"); + ret = recv(fd, msglxc, sizeof(*msglxc), 0); + if (ret <= 0) { + SYSERROR("client failed to recv (monitord died?) %s", + strerror(errno)); return -1; } - return ret; } @@ -156,7 +180,73 @@ int lxc_monitor_read(int fd, struct lxc_msg *msg) return lxc_monitor_read_timeout(fd, msg, -1); } -int lxc_monitor_close(int fd) + + +/* used to spawn a monitord either on startup of a daemon container, or when + * lxc-monitor starts + */ +int lxc_monitord_spawn(const char *lxcpath) { - return close(fd); + pid_t pid1,pid2; + int pipefd[2]; + char pipefd_str[11]; + + char * const args[] = { + "/usr/bin/lxc-monitord", + (char *)lxcpath, + pipefd_str, + NULL, + }; + + /* double fork to avoid zombies when monitord exits */ + pid1 = fork(); + if (pid1 < 0) { + SYSERROR("failed to fork"); + return -1; + } + + if (pid1) { + waitpid(pid1, NULL, 0); + return 0; + } + + if (pipe(pipefd) < 0) { + SYSERROR("failed to create pipe"); + exit(EXIT_FAILURE); + } + + pid2 = fork(); + if (pid2 < 0) { + SYSERROR("failed to fork"); + exit(EXIT_FAILURE); + } + if (pid2) { + char c; + /* wait for daemon to create socket */ + close(pipefd[1]); + /* sync with child, we're ignoring the return from read + * because regardless if it works or not, either way we've + * synced with the child process. the if-empty-statement + * construct is to quiet the warn-unused-result warning. + */ + if (read(pipefd[0], &c, 1)) ; + close(pipefd[0]); + exit(EXIT_SUCCESS); + } + + umask(0); + if (setsid() < 0) { + SYSERROR("failed to setsid"); + exit(EXIT_FAILURE); + } + close(0); + close(1); + close(2); + open("/dev/null", O_RDONLY); + open("/dev/null", O_RDWR); + open("/dev/null", O_RDWR); + close(pipefd[0]); + sprintf(pipefd_str, "%d", pipefd[1]); + execvp(args[0], args); + exit(EXIT_FAILURE); } diff --git a/src/lxc/monitor.h b/src/lxc/monitor.h index 8bef4c784..cd59ee841 100644 --- a/src/lxc/monitor.h +++ b/src/lxc/monitor.h @@ -24,6 +24,9 @@ #define __monitor_h #include +#include + +#include typedef enum { lxc_msg_state, @@ -32,11 +35,14 @@ typedef enum { struct lxc_msg { lxc_msg_type_t type; - char name[MAXPATHLEN]; + char name[NAME_MAX+1]; int value; }; -void lxc_monitor_send_state(const char *name, lxc_state_t state, +extern int lxc_monitor_open(const char *lxcpath); +extern int lxc_monitor_sock_name(const char *lxcpath, struct sockaddr_un *addr); +extern void lxc_monitor_send_state(const char *name, lxc_state_t state, const char *lxcpath); +extern int lxc_monitord_spawn(const char *lxcpath); #endif diff --git a/src/lxc/start.c b/src/lxc/start.c index 0a0cc40f7..fd96d4f60 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -390,7 +390,7 @@ int lxc_poll(const char *name, struct lxc_handler *handler) #endif } - return lxc_mainloop(&descr); + return lxc_mainloop(&descr, -1); out_mainloop_open: lxc_mainloop_close(&descr); @@ -808,7 +808,7 @@ int lxc_spawn(struct lxc_handler *handler) /* TODO - pass lxc.cgroup.dir (or user's pam cgroup) in for first argument */ if ((handler->cgroup = lxc_cgroup_path_create(NULL, name)) == NULL) goto out_delete_net; - + if (lxc_cgroup_enter(handler->cgroup, handler->pid) < 0) goto out_delete_net; diff --git a/src/lxc/utils.h b/src/lxc/utils.h index 8954503a3..8e6a7489c 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -32,4 +32,30 @@ extern int mkdir_p(const char *dir, mode_t mode); */ extern const char *default_lxc_path(void); +/** + * BUILD_BUG_ON - break compile if a condition is true. + * @condition: the condition which the compiler should know is false. + * + * If you have some code which relies on certain constants being equal, or + * other compile-time-evaluated condition, you should use BUILD_BUG_ON to + * detect if someone changes it. + * + * The implementation uses gcc's reluctance to create a negative array, but + * gcc (as of 4.4) only emits that error for obvious cases (eg. not arguments + * to inline functions). So as a fallback we use the optimizer; if it can't + * prove the condition is false, it will cause a link error on the undefined + * "__build_bug_on_failed". This error message can be harder to track down + * though, hence the two different methods. + */ +#ifndef __OPTIMIZE__ +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) +#else +extern int __build_bug_on_failed; +#define BUILD_BUG_ON(condition) \ + do { \ + ((void)sizeof(char[1 - 2*!!(condition)])); \ + if (condition) __build_bug_on_failed = 1; \ + } while(0) +#endif + #endif From 4fa22bfca1e94393aa3fbdc3fdf5516e75d47521 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Wed, 24 Apr 2013 15:16:21 -0500 Subject: [PATCH 060/325] lxc-create: cleanup whenever exiting with error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise we leave bad containers sitting around and further confuse things on retries. Reported-by: Mukanyiligira Didacienne Signed-off-by: Serge Hallyn Acked-by: Stéphane Graber --- src/lxc/lxc-create.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in index fbb566f63..553ffd271 100644 --- a/src/lxc/lxc-create.in +++ b/src/lxc/lxc-create.in @@ -286,7 +286,7 @@ fi if [ ! -r "$lxc_config" ]; then echo "$(basename $0): '$lxc_config' configuration file not found" >&2 - exit 1 + cleanup fi if [ ! -z "$lxc_template" ]; then @@ -317,11 +317,11 @@ echo "" >> $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" - exit 1 + cleanup fi if [ -d "$custom_rootfs" ]; then echo "specified rootfs ($custom_rootfs) already exists. Bailing." - exit 1 + cleanup fi echo "lxc.rootfs = $custom_rootfs" >> $lxc_path/$lxc_name/config fi @@ -329,9 +329,9 @@ fi # Create the fs as needed if [ "$backingstore" = "lvm" ]; then [ -d "$rootfs" ] || mkdir $rootfs - lvcreate -L $fssize -n $lvname $vgname || exit 1 + lvcreate -L $fssize -n $lvname $vgname || cleanup udevadm settle - mkfs -t $fstype $rootdev || exit 1 + mkfs -t $fstype $rootdev || cleanup mount -t $fstype $rootdev $rootfs fi From e8b9ac8fdfddec6a2eaacd6cdaa968058cf4e1e2 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Wed, 24 Apr 2013 19:49:59 -0500 Subject: [PATCH 061/325] close fd on error path Signed-off-by: Serge Hallyn --- src/lxc/monitor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lxc/monitor.c b/src/lxc/monitor.c index e108eb764..0521e9ab3 100644 --- a/src/lxc/monitor.c +++ b/src/lxc/monitor.c @@ -69,6 +69,7 @@ static void lxc_monitor_fifo_send(struct lxc_msg *msg, const char *lxcpath) ret = write(fd, msg, sizeof(*msg)); if (ret != sizeof(*msg)) { + close(fd); SYSERROR("failed to write monitor fifo %s", fifo_path); return; } From 6b7916695264238a490971e8cd87612154fc18b1 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Wed, 24 Apr 2013 19:59:10 -0500 Subject: [PATCH 062/325] monitor.c: sanity check on waitpid return value Signed-off-by: Serge Hallyn --- src/lxc/monitor.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lxc/monitor.c b/src/lxc/monitor.c index 0521e9ab3..5d655bd1a 100644 --- a/src/lxc/monitor.c +++ b/src/lxc/monitor.c @@ -207,7 +207,8 @@ int lxc_monitord_spawn(const char *lxcpath) } if (pid1) { - waitpid(pid1, NULL, 0); + if (waitpid(pid1, NULL, 0) != pid1) + SYSERROR("unexpected waitpid return value on double-fork"); return 0; } From f05699d19e27567583b9397a8d529e8aa275f5e1 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Wed, 24 Apr 2013 22:47:50 -0500 Subject: [PATCH 063/325] Revert "monitor.c: sanity check on waitpid return value" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's reported to errors in parallel starts. Reported-by: "S.ÇaÄŸlar Onur" This reverts commit 6b7916695264238a490971e8cd87612154fc18b1. --- src/lxc/monitor.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lxc/monitor.c b/src/lxc/monitor.c index 5d655bd1a..0521e9ab3 100644 --- a/src/lxc/monitor.c +++ b/src/lxc/monitor.c @@ -207,8 +207,7 @@ int lxc_monitord_spawn(const char *lxcpath) } if (pid1) { - if (waitpid(pid1, NULL, 0) != pid1) - SYSERROR("unexpected waitpid return value on double-fork"); + waitpid(pid1, NULL, 0); return 0; } From 69fe23ff0777390e34a8c0b11ce6037e5aef9109 Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Thu, 25 Apr 2013 12:20:30 +0200 Subject: [PATCH 064/325] configure: support for the "docbook2man" utility to build the documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds docbook2man as an alternative name for the docbook compiler. As that name was used on Debian based systems for an older version of the tool, this change also adds a check so that docbook2man is never used on Debian based systems. Reported-by: Peter Simons Reported-by: Christian Bühler christian@cbuehler.de Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- configure.ac | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 7dc82cfbc..9b58bab38 100644 --- a/configure.ac +++ b/configure.ac @@ -66,8 +66,14 @@ AC_ARG_ENABLE([doc], if test "x$enable_doc" = "xyes" -o "x$enable_doc" = "xauto"; then db2xman="" + if test "x$with_distro" = "xdebian" -o "x$with_distro" = "xubuntu"; then + dbparsers="docbook2x-man db2x_docbook2man" + else + dbparsers="docbook2x-man db2x_docbook2man docbook2man" + fi + AC_MSG_CHECKING(for docbook2x-man) - for name in docbook2x-man db2x_docbook2man; do + for name in ${dbparsers}; do if "$name" --help >/dev/null 2>&1; then db2xman="$name" break; From 6320e49454b0fd86dde7df0af54a2e194ae59821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 25 Apr 2013 17:31:34 +0200 Subject: [PATCH 065/325] lxc.conf: Add reference to capabilities manpage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a reference to capabilities(7) to the lxc.conf manpage. Signed-off-by: Tomáš Pospíšek Acked-by: Stéphane Graber --- doc/lxc.conf.sgml.in | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in index 226e36e2b..6fb0d0c42 100644 --- a/doc/lxc.conf.sgml.in +++ b/doc/lxc.conf.sgml.in @@ -1069,8 +1069,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA fstab 5 - + , + + capabilities + 7 + From 4f43438c476c3c5fb78d6192238d540108a33cb1 Mon Sep 17 00:00:00 2001 From: Christian Seiler Date: Thu, 25 Apr 2013 13:00:19 +0200 Subject: [PATCH 066/325] lxc_attach: Use clone() instead of second fork() Because of an assertion in glibc's fork() wrapper that parent pid and pid of child should never be the same, one should avoid fork() after attaching to a PID namespace, since the pid inside the namespace may coincide with the pid of the parent outside the namespace, thus hitting the aforementioned assertion. This patch just changes the code in the most simple manner to use clone() instead of fork(). Since clone() requires a function to be called instead of returning 0, we move the code of the child into a function child_main. Signed-off-by: Christian Seiler Acked-by: Serge E. Hallyn --- src/lxc/lxc_attach.c | 268 +++++++++++++++++++++++-------------------- 1 file changed, 144 insertions(+), 124 deletions(-) diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c index e5619df40..d845e3c48 100644 --- a/src/lxc/lxc_attach.c +++ b/src/lxc/lxc_attach.c @@ -140,17 +140,148 @@ Options :\n\ .checker = NULL, }; +struct child_data { + struct lxc_proc_context_info *init_ctx; + struct lxc_handler *handler; + int ipc_socket; +}; + +static int child_main(void* data) +{ + struct child_data* child_data = data; + struct lxc_proc_context_info *init_ctx = child_data->init_ctx; + struct lxc_handler *handler = child_data->handler; + int ipc_socket = child_data->ipc_socket; + struct passwd *passwd; + char *user_shell; + uid_t uid; + int ret; + + lxc_sync_fini_parent(handler); + close(ipc_socket); + + if ((namespace_flags & CLONE_NEWNS)) { + if (attach_apparmor(init_ctx->aa_profile) < 0) { + ERROR("failed switching apparmor profiles"); + return -1; + } + } + + /* 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 HAVE_SYS_PERSONALITY_H + if (new_personality < 0) + new_personality = init_ctx->personality; + + if (personality(new_personality) == -1) { + ERROR("could not ensure correct architecture: %s", + strerror(errno)); + return -1; + } +#endif + + if (!elevated_privileges && lxc_attach_drop_privs(init_ctx)) { + ERROR("could not drop privileges"); + return -1; + } + + if (lxc_attach_set_environment(env_policy, NULL, NULL)) { + ERROR("could not set environment"); + return -1; + } + + /* tell parent we are done setting up the container and wait + * until we have been put in the container's cgroup, if + * applicable */ + if (lxc_sync_barrier_parent(handler, LXC_SYNC_CONFIGURE)) + return -1; + + lxc_sync_fini(handler); + + if (namespace_flags & CLONE_NEWUSER) { + uid_t init_uid = 0; + gid_t init_gid = 0; + + /* ignore errors, we will fall back to root in that case + * (/proc was not mounted etc.) + */ + lxc_attach_get_init_uidgid(&init_uid, &init_gid); + + /* try to set the uid/gid combination */ + if (setgid(init_gid)) { + SYSERROR("switching to container gid"); + return -1; + } + if (setuid(init_uid)) { + SYSERROR("switching to container uid"); + return -1; + } + } + + if (my_args.argc) { + execvp(my_args.argv[0], my_args.argv); + SYSERROR("failed to exec '%s'", my_args.argv[0]); + return -1; + } + + uid = getuid(); + + passwd = getpwuid(uid); + + /* this probably happens because of incompatible nss + * implementations in host and container (remember, this + * code is still using the host's glibc but our mount + * namespace is in the container) + * we may try to get the information by spawning a + * [getent passwd uid] process and parsing the result + */ + if (!passwd) + user_shell = lxc_attach_getpwshell(uid); + else + user_shell = passwd->pw_shell; + + if (user_shell) { + char *const args[] = { + user_shell, + NULL, + }; + + (void) execvp(args[0], args); + } + + /* executed if either no passwd entry or execvp fails, + * we will fall back on /bin/sh as a default shell + */ + { + char *const args[] = { + "/bin/sh", + NULL, + }; + + execvp(args[0], args); + SYSERROR("failed to exec '%s'", args[0]); + return -1; + } +} + int main(int argc, char *argv[]) { int ret; pid_t pid, init_pid; - struct passwd *passwd; struct lxc_proc_context_info *init_ctx; struct lxc_handler *handler; - uid_t uid; char *curdir; int cgroup_ipc_sockets[2]; - char *user_shell; ret = lxc_caps_init(); if (ret) @@ -318,7 +449,14 @@ int main(int argc, char *argv[]) return -1; } - pid = fork(); + { + struct child_data child_data = { + .init_ctx = init_ctx, + .handler = handler, + .ipc_socket = cgroup_ipc_sockets[1] + }; + pid = lxc_clone(child_main, &child_data, 0); + } if (pid < 0) { SYSERROR("failed to fork"); @@ -390,124 +528,6 @@ int main(int argc, char *argv[]) return -1; } - if (!pid) { - lxc_sync_fini_parent(handler); - close(cgroup_ipc_sockets[1]); - - if ((namespace_flags & CLONE_NEWNS)) { - if (attach_apparmor(init_ctx->aa_profile) < 0) { - ERROR("failed switching apparmor profiles"); - return -1; - } - } - - /* 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 HAVE_SYS_PERSONALITY_H - if (new_personality < 0) - new_personality = init_ctx->personality; - - if (personality(new_personality) == -1) { - ERROR("could not ensure correct architecture: %s", - strerror(errno)); - return -1; - } - #endif - - if (!elevated_privileges && lxc_attach_drop_privs(init_ctx)) { - ERROR("could not drop privileges"); - return -1; - } - - if (lxc_attach_set_environment(env_policy, NULL, NULL)) { - ERROR("could not set environment"); - return -1; - } - - /* tell parent we are done setting up the container and wait - * until we have been put in the container's cgroup, if - * applicable */ - if (lxc_sync_barrier_parent(handler, LXC_SYNC_CONFIGURE)) - return -1; - - lxc_sync_fini(handler); - - if (namespace_flags & CLONE_NEWUSER) { - uid_t init_uid = 0; - gid_t init_gid = 0; - - /* ignore errors, we will fall back to root in that case - * (/proc was not mounted etc.) - */ - lxc_attach_get_init_uidgid(&init_uid, &init_gid); - - /* try to set the uid/gid combination */ - if (setgid(init_gid)) { - SYSERROR("switching to container gid"); - return -1; - } - if (setuid(init_uid)) { - SYSERROR("switching to container uid"); - return -1; - } - } - - if (my_args.argc) { - execvp(my_args.argv[0], my_args.argv); - SYSERROR("failed to exec '%s'", my_args.argv[0]); - return -1; - } - - uid = getuid(); - - passwd = getpwuid(uid); - - /* this probably happens because of incompatible nss - * implementations in host and container (remember, this - * code is still using the host's glibc but our mount - * namespace is in the container) - * we may try to get the information by spawning a - * [getent passwd uid] process and parsing the result - */ - if (!passwd) - user_shell = lxc_attach_getpwshell(uid); - else - user_shell = passwd->pw_shell; - - if (user_shell) { - char *const args[] = { - user_shell, - NULL, - }; - - (void) execvp(args[0], args); - } - - /* executed if either no passwd entry or execvp fails, - * we will fall back on /bin/sh as a default shell - */ - { - char *const args[] = { - "/bin/sh", - NULL, - }; - - execvp(args[0], args); - SYSERROR("failed to exec '%s'", args[0]); - return -1; - } - - } - - return 0; + /* shouldn't happen, because clone should never return 0 */ + return -1; } From f485f377a1caba11c58da100d3db9a8c6fdeb7d5 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Thu, 25 Apr 2013 12:21:53 -0400 Subject: [PATCH 067/325] lxc_wait should start monitord If lxc_wait is called before the container has started the socket will not yet have been created and lxc_wait's connect to it will fail. Starting the daemon will create the socket for lxc_wait to connect to. Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/state.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lxc/state.c b/src/lxc/state.c index 4ab131a4c..68ec00ba0 100644 --- a/src/lxc/state.c +++ b/src/lxc/state.c @@ -207,6 +207,9 @@ extern int lxc_wait(const char *lxcname, const char *states, int timeout, const if (fillwaitedstates(states, s)) return -1; + if (lxc_monitord_spawn(lxcpath)) + return -1; + fd = lxc_monitor_open(lxcpath); if (fd < 0) return -1; From 33c2c3ec93c17758f37cc2e53f07f7dfe6b72336 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Thu, 25 Apr 2013 15:18:25 -0500 Subject: [PATCH 068/325] add zfs support to lxc-create and lxc-destroy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is based on patch from Papp Tamas (thanks). It also does some reorganizing of lxc-create to commonize some of the backingstore handling. I played with it using: sudo lvcreate -L 100G -n zfs vg0 sudo zpool create lxc /dev/vg0/zfs sudo lxc-create -B zfs --zfsroot lxc -t ubuntu -n dir2 or you could qemu-img create zfs.img 100G sudo qemu-nbd -c /dev/nbd0 zfs.img sudo zpool create lxc /dev/nbd0 sudo lxc-create -B zfs --zfsroot lxc -t ubuntu -n dir2 I'll write the bdev.c handler and hook up lxc-clone next. This also fixses a bug in the sed expression to extract the rootfs from container config, which prepended an extra '/' to the rootdev. (That caused the zfs list entry not to match at destroy) Signed-off-by: Serge Hallyn Cc: Papp Tamas Acked-by: Stéphane Graber --- src/lxc/lxc-create.in | 167 +++++++++++++++++++++++++++-------------- src/lxc/lxc-destroy.in | 13 +++- 2 files changed, 124 insertions(+), 56 deletions(-) diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in index 553ffd271..1858c119a 100644 --- a/src/lxc/lxc-create.in +++ b/src/lxc/lxc-create.in @@ -22,6 +22,59 @@ . @DATADIR@/lxc/lxc.functions +verify_btrfs() { + if which btrfs >/dev/null 2>&1 && \ + btrfs filesystem df "$lxc_path/" >/dev/null 2>&1; then + echo "btrfs" + else + echo "no" + fi +} + +verify_zfs() { + if which zfs >/dev/null 2>&1 && zfs get all "$zfs_root" >/dev/null 2>&1; then + echo zfs + else + echo no + fi +} + +verify_lvm() { + which vgscan > /dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "vgscan not found (is lvm2 installed?)" >&2 + echo no + return + fi + + grep -q "\<$fstype\>" /proc/filesystems + if [ $? -ne 0 ]; then + echo "$fstype is not listed in /proc/filesystems" >&2 + echo no + return + fi + + vgscan | grep -q "Found volume group \"$vgname\"" + if [ $? -ne 0 ]; then + echo "could not find volume group \"$vgname\"" >&2 + echo no + return + fi + + echo lvm +} + +# if no backingstore is specified, auto-detect if $lxc_path is btrfs or zfs +detect_backingstore() { + if [ `verify_btrfs` = "btrfs" ]; then + echo btrfs + elif [ `verify_zfs` = "zfs" ]; then + echo zfs + else + echo none + fi +} + usage() { echo "usage: $(basename $0) -n NAME [-f CONFIG_FILE] [-t TEMPLATE] [FS_OPTIONS] --" >&2 echo " [-P lxcpath] [TEMPLATE_OPTIONS]" >&2 @@ -32,6 +85,7 @@ usage() { echo " -B lvm [--lvname LV_NAME] [--vgname VG_NAME] [--fstype FS_TYPE]" >&2 echo " [--fssize FS_SIZE]" >&2 echo " -B btrfs" >&2 + echo " -B zfs [--zfsroot PATH]" >&2 } help() { @@ -51,6 +105,7 @@ help() { 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 + echo " --zfsroot PATH specify the zfs path for lxcpath (default: tank/lxc)" >&2 echo >&2 if [ -z "$lxc_template" ]; then echo "To see template-specific options, specify a template. For example:" >&2 @@ -140,6 +195,11 @@ while [ $# -gt 0 ]; do fssize=$1 shift ;; + --zfsroot) + optarg_check $opt "$1" + zfs_root=$1 + shift + ;; --) break;; -?) @@ -187,6 +247,10 @@ if [ -z "$lvname" ]; then lvname="$lxc_name" fi +if [ -z "$zfs_root" ]; then + zfs_root="tank/lxc" +fi + if [ "$(id -u)" != "0" ]; then echo "$(basename $0): must be run as root" >&2 exit 1 @@ -196,10 +260,39 @@ if [ -n "$custom_rootfs" ] && [ "$backingstore" != "dir" ]; then echo "--dir is only valid with -B dir" fi +# detect / verify backing store case "$backingstore" in - dir|lvm|none|btrfs|_unset) :;; + btrfs) + if [ `verify_btrfs` != 'btrfs' ]; then + echo "missing 'btrfs' command or $lxc_path is not btrfs" >&2 + exit 1 + fi + ;; + zfs) + if [ `verify_zfs` != 'zfs' ]; then + echo "missing 'zfs' command or $zfs_root is not zfs" >&2 + exit 1 + fi + ;; + lvm) + if [ `verify_lvm` != 'lvm' ]; then + echo "system is missing 'lvm' support, or VG does not exist." >&2 + exit 1 + fi + rootdev=/dev/$vgname/$lvname + lvdisplay $rootdev > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "LV $rootdev already exists" >&2 + exit 1 + fi + ;; + _unset) + backingstore=`detect_backingstore` + ;; + dir|lvm|none) + :;; *) - echo "$(basename $0): '$backingstore' is not known (try 'none', 'dir', 'lvm', 'btrfs')" >&2 + echo "$(basename $0): '$backingstore' is not known (try 'none', 'dir', 'lvm', 'btrfs', 'zfs')" >&2 usage exit 1 ;; @@ -212,61 +305,14 @@ fi rootfs="$lxc_path/$lxc_name/rootfs" -if [ "$backingstore" = "_unset" ] || [ "$backingstore" = "btrfs" ]; then -# if no backing store was given, then see if btrfs would work - if which btrfs >/dev/null 2>&1 && \ - btrfs filesystem df "$lxc_path/" >/dev/null 2>&1; then - backingstore="btrfs" - else - if [ "$backingstore" = "btrfs" ]; then - echo "$(basename $0): missing 'btrfs' command or $lxc_path is not btrfs" >&2 - exit 1; - fi - backingstore="none" - fi -fi - -if [ "$backingstore" = "lvm" ]; then - which vgscan > /dev/null 2>&1 - if [ $? -ne 0 ]; then - echo "$(basename $0): vgscan not found (is lvm2 installed?)" >&2 - exit 1 - fi - - grep -q "\<$fstype\>" /proc/filesystems - if [ $? -ne 0 ]; then - echo "$(basename $0): $fstype is not listed in /proc/filesystems" >&2 - exit 1 - fi - - vgscan | grep -q "Found volume group \"$vgname\"" - if [ $? -ne 0 ]; then - echo "$(basename $0): could not find volume group \"$vgname\"" >&2 - exit 1 - fi - - rootdev=/dev/$vgname/$lvname - lvdisplay $rootdev > /dev/null 2>&1 - if [ $? -eq 0 ]; then - echo "$(basename $0): backing store already exists: $rootdev" >&2 - echo "please delete it (using \"lvremove $rootdev\") and try again" >&2 - exit 1 - fi - -elif [ "$backingstore" = "btrfs" ]; then - mkdir "$lxc_path/$lxc_name" - if ! out=$(btrfs subvolume create "$rootfs" 2>&1); then - echo "$(basename $0): failed to create subvolume in $rootfs: $out" >&2 - exit 1; - fi -fi - cleanup() { if [ "$backingstore" = "lvm" ]; then - umount $rootfs - lvremove -f $rootdev + umount -l $rootfs || true + lvremove -f $rootdev || true elif [ "$backingstore" = "btrfs" ]; then - btrfs subvolume delete "$rootfs" + btrfs subvolume delete "$rootfs" || true + elif [ "$backingstore" = "zfs" ]; then + zfs destroy "$zfs_root/$lxc_name" || true fi ${bindir}/lxc-destroy -n $lxc_name @@ -276,7 +322,18 @@ cleanup() { trap cleanup HUP INT TERM -mkdir -p $lxc_path/$lxc_name +# set up container dir per backing store +if [ "$backingstore" = "zfs" ]; then + zfs create -omountpoint=$lxc_path/$lxc_name/rootfs "$zfs_root/$lxc_name" +elif [ "$backingstore" = "btrfs" ]; then + mkdir "$lxc_path/$lxc_name" + if ! out=$(btrfs subvolume create "$rootfs" 2>&1); then + echo "$(basename $0): failed to create subvolume in $rootfs: $out" >&2 + exit 1; + fi +else + mkdir -p $lxc_path/$lxc_name +fi if [ -z "$lxc_config" ]; then lxc_config="@SYSCONFDIR@/lxc/default.conf" diff --git a/src/lxc/lxc-destroy.in b/src/lxc/lxc-destroy.in index 1c68f9d81..2e7a486f6 100644 --- a/src/lxc/lxc-destroy.in +++ b/src/lxc/lxc-destroy.in @@ -46,6 +46,15 @@ usage_err() { exit 1 } +verify_zfs() { + path=$1 + if which zfs >/dev/null 2>&1 && zfs list | grep -q $path; then + echo zfs + else + echo no + fi +} + optarg_check() { if [ -z "$2" ]; then usage_err "option '$1' requires an argument" @@ -123,7 +132,7 @@ fi # Deduce the type of rootfs # If LVM partition, destroy it. For btrfs, we delete the subvolue. If anything # 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/^[^/]*/\//'` +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 @@ -131,6 +140,8 @@ if [ -n "$rootdev" ]; then echo "removing backing store: $rootdev" lvremove -f $rootdev fi + elif [ `verify_zfs $rootdev` = "zfs" ]; then + zfs destroy $(zfs list | grep $rootdev | awk '{ print $1 }') elif [ -h "$rootdev" -o -d "$rootdev" ]; then if which btrfs >/dev/null 2>&1 && btrfs subvolume list "$rootdev" >/dev/null 2>&1; then From 7f95145833bb24f54e037f73ecc37444d6635697 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 29 Apr 2013 16:47:35 -0400 Subject: [PATCH 069/325] fix building docs Commit 69fe23ff added checking for the older docbook2man back into configure, but this breaks building the docs on at least Oracle Linux and Fedora when docbook2X is not installed as docbook2man will be found but the docs don't actually build with that tool. This change makes it so the docs can be built with either the older docbook2man or the newer 2X tools by using configure to set the dtd string to an appropriate value depending on use of docbook2man or db2x_docbook2man. Also fixed a small error in lxc-destroy.sgml.in that was noticed by the old tools. Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- configure.ac | 16 +++++++++------- doc/legacy/lxc-ls.sgml.in | 2 +- doc/lxc-attach.sgml.in | 2 +- doc/lxc-cgroup.sgml.in | 2 +- doc/lxc-checkconfig.sgml.in | 2 +- doc/lxc-checkpoint.sgml.in | 2 +- doc/lxc-clone.sgml.in | 2 +- doc/lxc-console.sgml.in | 2 +- doc/lxc-create.sgml.in | 2 +- doc/lxc-destroy.sgml.in | 19 +++++++++---------- doc/lxc-device.sgml.in | 2 +- doc/lxc-execute.sgml.in | 2 +- doc/lxc-freeze.sgml.in | 2 +- doc/lxc-info.sgml.in | 2 +- doc/lxc-kill.sgml.in | 2 +- doc/lxc-ls.sgml.in | 2 +- doc/lxc-monitor.sgml.in | 2 +- doc/lxc-netstat.sgml.in | 2 +- doc/lxc-ps.sgml.in | 2 +- doc/lxc-restart.sgml.in | 2 +- doc/lxc-shutdown.sgml.in | 2 +- doc/lxc-start-ephemeral.sgml.in | 2 +- doc/lxc-start.sgml.in | 2 +- doc/lxc-stop.sgml.in | 2 +- doc/lxc-top.sgml.in | 2 +- doc/lxc-unfreeze.sgml.in | 2 +- doc/lxc-unshare.sgml.in | 2 +- doc/lxc-version.sgml.in | 2 +- doc/lxc-wait.sgml.in | 2 +- doc/lxc.conf.sgml.in | 2 +- doc/lxc.sgml.in | 2 +- 31 files changed, 47 insertions(+), 46 deletions(-) diff --git a/configure.ac b/configure.ac index 9b58bab38..eba19db7f 100644 --- a/configure.ac +++ b/configure.ac @@ -60,17 +60,12 @@ AM_CONDITIONAL([ENABLE_RPATH], [test "x$enable_rpath" = "xyes"]) # Documentation (manpages) AC_ARG_ENABLE([doc], - [AC_HELP_STRING([--enable-doc], [make mans (require docbook2x-man installed) [default=auto]])], + [AC_HELP_STRING([--enable-doc], [make mans (requires docbook2man or docbook2x-man to be installed) [default=auto]])], [], [enable_doc=auto]) if test "x$enable_doc" = "xyes" -o "x$enable_doc" = "xauto"; then db2xman="" - - if test "x$with_distro" = "xdebian" -o "x$with_distro" = "xubuntu"; then - dbparsers="docbook2x-man db2x_docbook2man" - else - dbparsers="docbook2x-man db2x_docbook2man docbook2man" - fi + dbparsers="docbook2x-man db2x_docbook2man docbook2man" AC_MSG_CHECKING(for docbook2x-man) for name in ${dbparsers}; do @@ -93,6 +88,13 @@ if test "x$enable_doc" = "xyes" -o "x$enable_doc" = "xauto"; then fi AM_CONDITIONAL([ENABLE_DOCBOOK], [test "x$db2xman" != "x"]) +if test "x$db2xman" = "xdocbook2man"; then + docdtd="\"-//Davenport//DTD DocBook V3.0//EN\"" +else + docdtd="\"-//OASIS//DTD DocBook XML\" \"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd\"" +fi +AC_SUBST(docdtd) + # Apparmor AC_ARG_ENABLE([apparmor], [AC_HELP_STRING([--enable-apparmor], [enable apparmor])], diff --git a/doc/legacy/lxc-ls.sgml.in b/doc/legacy/lxc-ls.sgml.in index 60c085cdc..321e0878e 100644 --- a/doc/legacy/lxc-ls.sgml.in +++ b/doc/legacy/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-attach.sgml.in b/doc/lxc-attach.sgml.in index a3bdb4499..5c8d51337 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 --> - diff --git a/doc/lxc-cgroup.sgml.in b/doc/lxc-cgroup.sgml.in index 5dcd61962..888db369b 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-checkconfig.sgml.in b/doc/lxc-checkconfig.sgml.in index ec7a4c1b2..aa0241783 100644 --- a/doc/lxc-checkconfig.sgml.in +++ b/doc/lxc-checkconfig.sgml.in @@ -21,7 +21,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 6cd7169f7..5737320ce 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-clone.sgml.in b/doc/lxc-clone.sgml.in index d00b57b01..994f3cddb 100644 --- a/doc/lxc-clone.sgml.in +++ b/doc/lxc-clone.sgml.in @@ -24,7 +24,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 9298a5234..92997781b 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 90864e067..3969246d4 100644 --- a/doc/lxc-create.sgml.in +++ b/doc/lxc-create.sgml.in @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-destroy.sgml.in b/doc/lxc-destroy.sgml.in index 366dc9a10..91d5d19a2 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 --> - @@ -81,17 +81,16 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + + Use an alternate container path. The default is @LXCPATH@. + + + - - - - - Use an alternate container path. The default is @LXCPATH@. - - - - diff --git a/doc/lxc-device.sgml.in b/doc/lxc-device.sgml.in index e7773823e..90cd885b5 100644 --- a/doc/lxc-device.sgml.in +++ b/doc/lxc-device.sgml.in @@ -22,7 +22,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 de233f68f..f800ea6f0 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 fba139b59..c69e4c8e7 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-info.sgml.in b/doc/lxc-info.sgml.in index 03212b01a..ba0a044d1 100644 --- a/doc/lxc-info.sgml.in +++ b/doc/lxc-info.sgml.in @@ -21,7 +21,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 8d58db923..ad0eec018 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 4af2596f9..4801dbb3f 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 eae6f8233..336061dd4 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-netstat.sgml.in b/doc/lxc-netstat.sgml.in index ab475f3ea..ea4c9cd53 100644 --- a/doc/lxc-netstat.sgml.in +++ b/doc/lxc-netstat.sgml.in @@ -21,7 +21,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 b0103cfd9..0b50baa9a 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 9be025703..aae7958fa 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 a4f3accfd..9262c6d79 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-ephemeral.sgml.in b/doc/lxc-start-ephemeral.sgml.in index b753a9bf7..563acabf5 100644 --- a/doc/lxc-start-ephemeral.sgml.in +++ b/doc/lxc-start-ephemeral.sgml.in @@ -22,7 +22,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 d501636a6..0bd7f9827 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 33e3064b9..b4dcc1bcf 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-top.sgml.in b/doc/lxc-top.sgml.in index 2a4f83541..a3aa8e5dd 100644 --- a/doc/lxc-top.sgml.in +++ b/doc/lxc-top.sgml.in @@ -21,7 +21,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 34a2cf402..6b999ea72 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-unshare.sgml.in b/doc/lxc-unshare.sgml.in index 5c899b63d..7e862bb79 100644 --- a/doc/lxc-unshare.sgml.in +++ b/doc/lxc-unshare.sgml.in @@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --> - diff --git a/doc/lxc-version.sgml.in b/doc/lxc-version.sgml.in index 3833e9fc7..fbda32c66 100644 --- a/doc/lxc-version.sgml.in +++ b/doc/lxc-version.sgml.in @@ -21,7 +21,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 b43061f6d..27940677d 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 6fb0d0c42..0a5a52a41 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 a333c49cd..b06515d55 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 --> - ]> From ab1bf971d2db43777cbf3892fb887bf71ce7d155 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 29 Apr 2013 14:54:08 -0400 Subject: [PATCH 070/325] Create log file in lxcpath for non-system containers On Fri, 26 Apr 2013 10:18:12 -0500 Serge Hallyn wrote: > Quoting Dwight Engen (dwight.engen@oracle.com): > > On Fri, 26 Apr 2013 09:37:49 -0500 > > Serge Hallyn wrote: > > > > > Quoting Dwight Engen (dwight.engen@oracle.com): > > > > Using lxc configured with --enable-configpath-log, and > > > > specifying a path to the lxc commands with -P, the log file > > > > path is generated with a basename of LOGPATH instead of the > > > > lxcpath. This means for example if you do > > > > > > > > lxc-start -P /tmp/containers -n test01 -l INFO > > > > > > > > your log file will be > > > > > > > > /var/lib/lxc/test01/test01.log > > > > > > > > I was expecting the log to be /tmp/containers/test01/test01.log. > > > > This is particularly confusing if you also have test01 on the > > > > regular lxcpath. The patch below changes the log file path to be > > > > based on lxcpath rather than LOGPATH when lxc is configured with > > > > --enable-configpath-log. > > > > > > > > I think that even in the normal non --enable-configpath-log case > > > > we should consider using lxcpath as the base and not having > > > > LOGPATH at all, as attempting to create the log files > > > > in /var/log is not going to work for regular users on their own > > > > lxcpath. If we want that, I'll update the patch to do that as > > > > well. > > > > > > > > > Perhaps we should do: > > > > > > 1. If lxcpath == default_lxc_path(), then first choice is > > > LOGPATH, second is lxcpath/container.log > > > 2. when opening, if first choice fails, use second choice > > > if there is any. > > > > > > That way 'system' containers will go to /var/log/lxc, as I think > > > they should. Custom-lxcpath containers should never go > > > to /var/log/lxc, since their names could be dups of containers in > > > default_lxc_path(). And if the system is a weird one where > > > default_lxc_path is set up so that an unprivileged user can use > > > it, then we should log into $lxcpath. > > > > That sounds good to me. So these rules would apply in both the > > regular and --enable-configpath-log cases. I updated the patch to try to open the log file according to the choices given above. Along the way I cleaned up log.c a bit, making some things static, grouping external interfaces together, etc... Hopefully that doesn't add too much noise. > > > (Note this patch will trivially conflict with my new lxc_clone.c > > > causing it to fail to build - unfortunate result of timing) > > > > Yeah unfortunately this touches every lxc_log_init() caller. I can > > work on the above logic and re-submit after your new lxc_clone > > stuff goes in. > > No no, I'll just need to remember to update mine. Don't hold up on > mine, this is just the nature of such collaboration :) > > > Did you have any thoughts on the XXX what to pass in for lxcpath in > > lxc_init? Right now it just falls back to LOGPATH. > > No - that's a weird one, since lxc_init runs in the container. If > there were only system containers I'd say always use LOGPATH. > However there are people (apparently :) who use container sharing the > host's rootfs... > > lxc-execute does know the lxcpath. Perhaps we can simply have > src/lxc/execute.c:execute_start() look at handler->conf to see if a > rootfs is set. If rootfs is NOT set, then pass lxcpath along to > lxc-init. Then lxc-init can mostly do the same as the others? (It > doesn't use src/lxc/arguments.c, so you'd have to add lxcpath to > options[] in lxc-init.c) So I did this, only to realize that lxc-init is passing "none" for the file anyway, so it currently doesn't intend to log. This makes me think that passing NULL for lxcpath is the right thing to do in this patch. If you want me to make it so lxc-init can log, I can do that but I think it should be in a different change :) -- Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/log.c | 213 +++++++++++++++++++++++---------------- src/lxc/log.h | 10 +- src/lxc/lxc_attach.c | 2 +- src/lxc/lxc_cgroup.c | 2 +- src/lxc/lxc_checkpoint.c | 2 +- src/lxc/lxc_console.c | 2 +- src/lxc/lxc_execute.c | 2 +- src/lxc/lxc_freeze.c | 2 +- src/lxc/lxc_info.c | 2 +- src/lxc/lxc_init.c | 2 +- src/lxc/lxc_kill.c | 2 +- src/lxc/lxc_monitor.c | 2 +- src/lxc/lxc_monitord.c | 2 +- src/lxc/lxc_restart.c | 2 +- src/lxc/lxc_start.c | 2 +- src/lxc/lxc_stop.c | 2 +- src/lxc/lxc_unfreeze.c | 2 +- src/lxc/lxc_wait.c | 2 +- src/lxc/lxccontainer.c | 2 +- 19 files changed, 147 insertions(+), 110 deletions(-) diff --git a/src/lxc/log.c b/src/lxc/log.c index 3c6e4d188..61280bc0b 100644 --- a/src/lxc/log.c +++ b/src/lxc/log.c @@ -35,15 +35,19 @@ #include "log.h" #include "caps.h" +#include "utils.h" #define LXC_LOG_PREFIX_SIZE 32 #define LXC_LOG_BUFFER_SIZE 512 int lxc_log_fd = -1; static char log_prefix[LXC_LOG_PREFIX_SIZE] = "lxc"; +static char *log_fname = NULL; +/* command line values for logfile or logpriority should always override + * values from the configuration file or defaults + */ +static int lxc_logfile_specified = 0; static int lxc_loglevel_specified = 0; -// if logfile was specifed on command line, it won't be overridden by lxc.logfile -static int lxc_log_specified = 0; lxc_log_define(lxc_log, lxc); @@ -119,12 +123,6 @@ struct lxc_log_category lxc_log_category_lxc = { }; /*---------------------------------------------------------------------------*/ -extern void lxc_log_setprefix(const char *prefix) -{ - strncpy(log_prefix, prefix, sizeof(log_prefix)); - log_prefix[sizeof(log_prefix) - 1] = 0; -} - static int build_dir(const char *name) { char *n = strdup(name); // because we'll be modifying it @@ -180,28 +178,39 @@ static int log_open(const char *name) return newfd; } -static char *build_log_path(const char *name) +/* + * Build the path to the log file + * @name : the name of the container + * @lxcpath : the lxcpath to use as a basename or NULL to use LOGPATH + * Returns malloced path on sucess, or NULL on failure + */ +static char *build_log_path(const char *name, const char *lxcpath) { char *p; int len, ret; /* - * '$logpath' + '/' + '$name' + '.log' + '\0' - * or + * If USE_CONFIGPATH_LOGS is true the resulting path will be: * '$logpath' + '/' + '$name' + '/' + '$name' + '.log' + '\0' - * sizeof(LOGPATH) includes its \0 + * + * If USE_CONFIGPATH_LOGS is false the resulting path will be: + * '$logpath' + '/' + '$name' + '.log' + '\0' */ - len = sizeof(LOGPATH) + strlen(name) + 6; + len = strlen(name) + 6; /* 6 == '/' + '.log' + '\0' */ + if (!lxcpath) + lxcpath = LOGPATH; #if USE_CONFIGPATH_LOGS - len += strlen(name) + 1; /* add "/$container_name/" */ + len += strlen(lxcpath) + 1 + strlen(name) + 1; /* add "/$container_name/" */ +#else + len += strlen(lxcpath) + 1; #endif p = malloc(len); if (!p) return p; #if USE_CONFIGPATH_LOGS - ret = snprintf(p, len, "%s/%s/%s.log", LOGPATH, name, name); + ret = snprintf(p, len, "%s/%s/%s.log", lxcpath, name, name); #else - ret = snprintf(p, len, "%s/%s.log", LOGPATH, name); + ret = snprintf(p, len, "%s/%s.log", lxcpath, name); #endif if (ret < 0 || ret >= len) { free(p); @@ -210,19 +219,67 @@ static char *build_log_path(const char *name) return p; } -int do_lxc_log_set_file(const char *fname, int from_default); +/* + * This can be called: + * 1. when a program calls lxc_log_init with no logfile parameter (in which + * case the default is used). In this case lxc.logfile can override this. + * 2. when a program calls lxc_log_init with a logfile parameter. In this + * case we don't want lxc.logfile to override this. + * 3. When a lxc.logfile entry is found in config file. + */ +static int __lxc_log_set_file(const char *fname, int create_dirs) +{ + if (lxc_log_fd != -1) { + // we are overriding the default. + close(lxc_log_fd); + free(log_fname); + } + +#if USE_CONFIGPATH_LOGS + // we don't build_dir for the default if the default is + // i.e. /var/lib/lxc/$container/$container.log + if (create_dirs) +#endif + if (build_dir(fname)) { + ERROR("failed to create dir for log file \"%s\" : %s", fname, + strerror(errno)); + return -1; + } + + lxc_log_fd = log_open(fname); + if (lxc_log_fd == -1) + return -1; + + log_fname = strdup(fname); + return 0; +} + +static int _lxc_log_set_file(const char *name, const char *lxcpath, int create_dirs) +{ + char *logfile; + int ret; + + logfile = build_log_path(name, lxcpath); + if (!logfile) { + ERROR("could not build log path"); + return -1; + } + ret = __lxc_log_set_file(logfile, create_dirs); + free(logfile); + return ret; +} -/*---------------------------------------------------------------------------*/ extern int lxc_log_init(const char *name, const char *file, - const char *priority, const char *prefix, int quiet) + const char *priority, const char *prefix, int quiet, + const char *lxcpath) { int lxc_priority = LXC_LOG_PRIORITY_ERROR; int ret; - char *tmpfile = NULL; - int want_lxc_log_specified = 0; - if (lxc_log_fd != -1) + if (lxc_log_fd != -1) { + WARN("lxc_log_init called with log already initialized"); return 0; + } if (priority) { lxc_loglevel_specified = 1; @@ -241,38 +298,38 @@ extern int lxc_log_init(const char *name, const char *file, lxc_log_category_lxc.appender->next = &log_appender_stderr; if (prefix) - lxc_log_setprefix(prefix); + lxc_log_set_prefix(prefix); - if (file && strcmp(file, "none") == 0) { - return 0; - } - - if (!file) { - tmpfile = build_log_path(name); - if (!tmpfile) { - ERROR("could not build log path"); - return -1; - } + if (file) { + lxc_logfile_specified = 1; + if (strcmp(file, "none") == 0) + return 0; + ret = __lxc_log_set_file(file, 1); } else { - want_lxc_log_specified = 1; + ret = -1; + + /* try LOGPATH if lxcpath is the default */ + if (strcmp(lxcpath, default_lxc_path()) == 0) + ret = _lxc_log_set_file(name, NULL, 0); + + /* try in lxcpath */ + if (ret < 0) + ret = _lxc_log_set_file(name, lxcpath, 1); + + /* try LOGPATH in case its writable by the caller */ + if (ret < 0) + ret = _lxc_log_set_file(name, NULL, 0); } - ret = do_lxc_log_set_file(tmpfile ? tmpfile : file, !want_lxc_log_specified); - - if (want_lxc_log_specified) - lxc_log_specified = 1; /* - * If !want_lxc_log_specified, that is, if the user did not request - * this logpath, then ignore failures and continue logging to console + * If !file, that is, if the user did not request this logpath, then + * ignore failures and continue logging to console */ - if (!want_lxc_log_specified && ret != 0) { + if (!file && ret != 0) { INFO("Ignoring failure to open default logfile."); ret = 0; } - if (tmpfile) - free(tmpfile); - return ret; } @@ -293,51 +350,6 @@ extern int lxc_log_set_level(int level) return 0; } -char *log_fname; // default to NULL, set in lxc_log_set_file. -/* - * This can be called: - * 1. when a program calls lxc_log_init with no logfile parameter (in which - * case the default is used). In this case lxc.logfile can override this. - * 2. when a program calls lxc_log_init with a logfile parameter. In this - * case we don't want lxc.logfile to override this. - * 3. When a lxc.logfile entry is found in config file. - */ -int do_lxc_log_set_file(const char *fname, int from_default) -{ - if (lxc_log_specified) { - INFO("lxc.logfile overridden by command line"); - return 0; - } - if (lxc_log_fd != -1) { - // we are overriding the default. - close(lxc_log_fd); - free(log_fname); - } - -#if USE_CONFIGPATH_LOGS - // we don't build_dir for the default if the default is - // i.e. /var/lib/lxc/$container/$container.log - if (!from_default) -#endif - if (build_dir(fname)) { - ERROR("failed to create dir for log file \"%s\" : %s", fname, - strerror(errno)); - return -1; - } - - lxc_log_fd = log_open(fname); - if (lxc_log_fd == -1) - return -1; - - log_fname = strdup(fname); - return 0; -} - -extern int lxc_log_set_file(const char *fname) -{ - return do_lxc_log_set_file(fname, 0); -} - extern int lxc_log_get_level(void) { if (!lxc_loglevel_specified) @@ -345,7 +357,30 @@ extern int lxc_log_get_level(void) return lxc_log_category_lxc.priority; } +/* + * This is called when we read a lxc.logfile entry in a lxc.conf file. This + * happens after processing command line arguments, which override the .conf + * settings. So only set the file if previously unset. + */ +extern int lxc_log_set_file(const char *fname) +{ + if (lxc_logfile_specified) + return 0; + return __lxc_log_set_file(fname, 0); +} + extern const char *lxc_log_get_file(void) { return log_fname; } + +extern void lxc_log_set_prefix(const char *prefix) +{ + strncpy(log_prefix, prefix, sizeof(log_prefix)); + log_prefix[sizeof(log_prefix) - 1] = 0; +} + +extern const char *lxc_log_get_prefix(void) +{ + return log_prefix; +} diff --git a/src/lxc/log.h b/src/lxc/log.h index cd43068a8..2fd405000 100644 --- a/src/lxc/log.h +++ b/src/lxc/log.h @@ -288,11 +288,13 @@ extern struct lxc_log_category lxc_log_category_lxc; extern int lxc_log_fd; extern int lxc_log_init(const char *name, const char *file, - const char *priority, const char *prefix, int quiet); + const char *priority, const char *prefix, int quiet, + const char *lxcpath); -extern void lxc_log_setprefix(const char *a_prefix); -extern int lxc_log_set_level(int level); extern int lxc_log_set_file(const char *fname); -extern int lxc_log_get_level(void); +extern int lxc_log_set_level(int level); +extern void lxc_log_set_prefix(const char *prefix); extern const char *lxc_log_get_file(void); +extern int lxc_log_get_level(void); +extern const char *lxc_log_get_prefix(void); #endif diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c index d845e3c48..300bc922d 100644 --- a/src/lxc/lxc_attach.c +++ b/src/lxc/lxc_attach.c @@ -292,7 +292,7 @@ int main(int argc, char *argv[]) return ret; ret = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet); + my_args.progname, my_args.quiet, my_args.lxcpath); if (ret) return ret; diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c index f7c88a89c..7684f1b1f 100644 --- a/src/lxc/lxc_cgroup.c +++ b/src/lxc/lxc_cgroup.c @@ -69,7 +69,7 @@ int main(int argc, char *argv[]) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet)) + my_args.progname, my_args.quiet, my_args.lxcpath)) return -1; state_object = my_args.argv[0]; diff --git a/src/lxc/lxc_checkpoint.c b/src/lxc/lxc_checkpoint.c index 947d9d941..8b3a02abe 100644 --- a/src/lxc/lxc_checkpoint.c +++ b/src/lxc/lxc_checkpoint.c @@ -116,7 +116,7 @@ int main(int argc, char *argv[]) return ret; ret = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet); + my_args.progname, my_args.quiet, my_args.lxcpath); if (ret) return ret; diff --git a/src/lxc/lxc_console.c b/src/lxc/lxc_console.c index f6659f64e..1d779a161 100644 --- a/src/lxc/lxc_console.c +++ b/src/lxc/lxc_console.c @@ -192,7 +192,7 @@ int main(int argc, char *argv[]) return -1; err = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet); + my_args.progname, my_args.quiet, my_args.lxcpath); if (err) return -1; diff --git a/src/lxc/lxc_execute.c b/src/lxc/lxc_execute.c index 41e29aa9b..0fa2d2516 100644 --- a/src/lxc/lxc_execute.c +++ b/src/lxc/lxc_execute.c @@ -101,7 +101,7 @@ int main(int argc, char *argv[]) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet)) + my_args.progname, my_args.quiet, my_args.lxcpath)) return -1; /* rcfile is specified in the cli option */ diff --git a/src/lxc/lxc_freeze.c b/src/lxc/lxc_freeze.c index b52ab5bf1..ff3cdd577 100644 --- a/src/lxc/lxc_freeze.c +++ b/src/lxc/lxc_freeze.c @@ -55,7 +55,7 @@ int main(int argc, char *argv[]) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet)) + my_args.progname, my_args.quiet, my_args.lxcpath)) return -1; return lxc_freeze(my_args.name, my_args.lxcpath); diff --git a/src/lxc/lxc_info.c b/src/lxc/lxc_info.c index 3fcead583..b19b1d02f 100644 --- a/src/lxc/lxc_info.c +++ b/src/lxc/lxc_info.c @@ -80,7 +80,7 @@ int main(int argc, char *argv[]) return 1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet)) + my_args.progname, my_args.quiet, my_args.lxcpath)) return 1; if (!state && !pid) diff --git a/src/lxc/lxc_init.c b/src/lxc/lxc_init.c index 84e12932e..663875b37 100644 --- a/src/lxc/lxc_init.c +++ b/src/lxc/lxc_init.c @@ -78,7 +78,7 @@ int main(int argc, char *argv[]) if (lxc_caps_init()) exit(err); - if (lxc_log_init(NULL, "none", 0, basename(argv[0]), quiet)) + if (lxc_log_init(NULL, "none", 0, basename(argv[0]), quiet, NULL)) exit(err); if (!argv[optind]) { diff --git a/src/lxc/lxc_kill.c b/src/lxc/lxc_kill.c index ba00aa8c1..e08993139 100644 --- a/src/lxc/lxc_kill.c +++ b/src/lxc/lxc_kill.c @@ -62,7 +62,7 @@ int main(int argc, char *argv[], char *envp[]) return ret; ret = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet); + my_args.progname, my_args.quiet, my_args.lxcpath); if (ret) return ret; diff --git a/src/lxc/lxc_monitor.c b/src/lxc/lxc_monitor.c index 0ca829f67..5ddb2913a 100644 --- a/src/lxc/lxc_monitor.c +++ b/src/lxc/lxc_monitor.c @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet)) + my_args.progname, my_args.quiet, my_args.lxcpath)) return -1; len = strlen(my_args.name) + 3; diff --git a/src/lxc/lxc_monitord.c b/src/lxc/lxc_monitord.c index 3b5cf53b1..e76af716b 100644 --- a/src/lxc/lxc_monitord.c +++ b/src/lxc/lxc_monitord.c @@ -343,7 +343,7 @@ int main(int argc, char *argv[]) if (ret < 0 || ret >= sizeof(logpath)) return EXIT_FAILURE; - ret = lxc_log_init(NULL, logpath, "NOTICE", "lxc-monitord", 0); + ret = lxc_log_init(NULL, logpath, "NOTICE", "lxc-monitord", 0, lxcpath); if (ret) return ret; diff --git a/src/lxc/lxc_restart.c b/src/lxc/lxc_restart.c index 118d4b879..77099b149 100644 --- a/src/lxc/lxc_restart.c +++ b/src/lxc/lxc_restart.c @@ -124,7 +124,7 @@ int main(int argc, char *argv[]) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet)) + my_args.progname, my_args.quiet, my_args.lxcpath)) return -1; /* rcfile is specified in the cli option */ diff --git a/src/lxc/lxc_start.c b/src/lxc/lxc_start.c index 957fdb0e7..d923a7eb5 100644 --- a/src/lxc/lxc_start.c +++ b/src/lxc/lxc_start.c @@ -165,7 +165,7 @@ int main(int argc, char *argv[]) args = my_args.argv; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet)) + my_args.progname, my_args.quiet, my_args.lxcpath)) return err; /* rcfile is specified in the cli option */ diff --git a/src/lxc/lxc_stop.c b/src/lxc/lxc_stop.c index b4d9f23dd..0967a156f 100644 --- a/src/lxc/lxc_stop.c +++ b/src/lxc/lxc_stop.c @@ -55,7 +55,7 @@ int main(int argc, char *argv[]) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet)) + my_args.progname, my_args.quiet, my_args.lxcpath)) return -1; return lxc_stop(my_args.name, my_args.lxcpath); diff --git a/src/lxc/lxc_unfreeze.c b/src/lxc/lxc_unfreeze.c index 0bb5dc502..44d3cc021 100644 --- a/src/lxc/lxc_unfreeze.c +++ b/src/lxc/lxc_unfreeze.c @@ -54,7 +54,7 @@ int main(int argc, char *argv[]) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet)) + my_args.progname, my_args.quiet, my_args.lxcpath)) return -1; return lxc_unfreeze(my_args.name, my_args.lxcpath); diff --git a/src/lxc/lxc_wait.c b/src/lxc/lxc_wait.c index 18200e574..d21578b6c 100644 --- a/src/lxc/lxc_wait.c +++ b/src/lxc/lxc_wait.c @@ -84,7 +84,7 @@ int main(int argc, char *argv[]) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet)) + my_args.progname, my_args.quiet, my_args.lxcpath)) return -1; return lxc_wait(strdup(my_args.name), my_args.states, my_args.timeout, my_args.lxcpath); diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 53765b04c..740a8d99e 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -1105,7 +1105,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath c->set_config_path = lxcapi_set_config_path; /* we'll allow the caller to update these later */ - if (lxc_log_init(NULL, "none", NULL, "lxc_container", 0)) { + if (lxc_log_init(NULL, "none", NULL, "lxc_container", 0, c->config_path)) { fprintf(stderr, "failed to open log\n"); goto err; } From 9be53773792fc9e8bd173edc3b7ac7e144875387 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 16 Apr 2013 08:07:05 -0500 Subject: [PATCH 071/325] implement backend drivers and container clone API (v3) 1. commonize waitpid users to use a single helper. We frequently want to run something in a clean namespace, or fork off a script. This lets us keep the function doing fork:(1)exec(2)waitpid simpler. 2. start a blockdev backend implementation. This will be used for mounting, copying, and snapshotting container filesystems. 3. implement btrfs, lvm, directory, and overlayfs backends. 4. For overlayfs, support a new lxc.rootfs format of 'bdevtype:'. This means you can now use overlayfs-based containers without using lxc-start-ephemeral, by using lxc.rootfs = overlayfs:/readonly-dir:writeable-dir 5. add a set of simple clone testcases 6. Write a new lxc_clone.c based on api clone. Still to do (there's more, but off top of my head): 1. support zfs, aufs 2. have clone handle other mount entries (right now it only clones the rootfs) 3. python, lua, and go bindings (not me :) 4. lxc-destroy: if lvm backing store, check for snapshots of it. (what about directories which have overlayfs clones?) Changes since v2: Initialize random generator when picking new macaddr (reported by caglar@10ur.org) Fix wrong use of bitmask flags On copy-clone of btrfs, create a subvolume lxc_clone.c: respect the command line usage of the old script lxc-clone(1): update documentation Refuse to try changing backing stores expect to overlayfs, as it is not implemented (yet) anyway. Signed-off-by: Serge Hallyn Conflicts: src/lxc/utils.h --- configure.ac | 2 +- doc/lxc-clone.sgml.in | 213 ++-- src/lxc/Makefile.am | 6 +- src/lxc/bdev.c | 1189 +++++++++++++++++++++ src/lxc/bdev.h | 50 + src/lxc/conf.c | 11 +- src/lxc/{lxc-clone.in => lxc-clone-sh.in} | 0 src/lxc/lxc_clone.c | 138 +++ src/lxc/lxccontainer.c | 590 +++++++++- src/lxc/lxccontainer.h | 36 + src/lxc/utils.c | 20 + src/lxc/utils.h | 5 + src/tests/Makefile.am | 4 +- src/tests/clonetest.c | 178 +++ 14 files changed, 2323 insertions(+), 119 deletions(-) create mode 100644 src/lxc/bdev.c create mode 100644 src/lxc/bdev.h rename src/lxc/{lxc-clone.in => lxc-clone-sh.in} (100%) create mode 100644 src/lxc/lxc_clone.c create mode 100644 src/tests/clonetest.c diff --git a/configure.ac b/configure.ac index eba19db7f..6b2703876 100644 --- a/configure.ac +++ b/configure.ac @@ -382,7 +382,7 @@ AC_CONFIG_FILES([ src/lxc/lxc-checkconfig src/lxc/lxc-version src/lxc/lxc-create - src/lxc/lxc-clone + src/lxc/lxc-clone-sh src/lxc/lxc-shutdown src/lxc/lxc-start-ephemeral src/lxc/lxc-destroy diff --git a/doc/lxc-clone.sgml.in b/doc/lxc-clone.sgml.in index 994f3cddb..3ad769880 100644 --- a/doc/lxc-clone.sgml.in +++ b/doc/lxc-clone.sgml.in @@ -50,13 +50,29 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA lxc-clone + -s + -K + -M + -H + -B backingstore + -L fssize + -p lxcpath + -P newlxcpath -o orig -n new + + + lxc-clone -s + -K + -M + -H + -B backingstore -L fssize - -v vgname - -p lxc_lv_prefix - -t fstype + -p lxcpath + -P newlxcpath + orig + new @@ -64,10 +80,29 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Description - lxc-clone Creates a new container as a copy of an existing - container. When the original container's rootfs is an LVM block device or - is on a btrfs filesystem, then a snapshotted clone can be created, taking up - very little initial disk space. + lxc-clone Creates a new container as a clone of an existing + container. Two types of clones are supported: copy and snapshot. A copy + clone copies the root filessytem from the original container to the new. A + snapshot filesystem uses the backing store's snapshot functionality to create + a very small copy-on-write snapshot of the original container. Snapshot + clones require the new container backing store to support snapshotting. Currently + this includes only btrfs, lvm, overlayfs and zfs. LVM devices do not support + snapshots fo snapshots. + + + + The backing store of the new container will be the same type as the + original container, + with one exception: overlayfs snapshots can be created of directory backed + containers. This can be requested by using the -B overlayfs + arguments. + + + + The names of the original and new container can be given (in that order) + after all options, or can be specified with the + -o and -n options, + respectively. @@ -78,6 +113,108 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + + + + The new container's rootfs should be a LVM or btrfs snapshot of the original. + + + + + + + + + + + Do not change the hostname of the container (in the root + filesystem). + + + + + + + + + + + Use the same MAC address as the original container, rather tahn + generating a new random one. + + + + + + + + + + + Copy all mount hooks into the new container's directory, and + update any lxcpaths and container names as needed. + + + + + + + + + + + In the case of a block device backed container, a size for the new + block device. By default, the new device will be made the + same size as the original. + + + + + + + + + + + The lxcpath of the original container. By default, the system + wide configured lxcpath will be used. + + + + + + + + + + + The lxcpath for the new container. By default the same lxcpath + as the original will be used. Note that with btrfs snapshots, + changing lxcpaths may not be possible, as subvolume snapshots + must be in the same btrfs filesystem. + + + + + + + + + + + Select a different backing store for the new container. By + default the same as the original container's is used. Note that + currently changing the backingstore is only supported for + overlayfs snapshots of directory backed containers. Valid + backing stores include dir (directory), btrfs, lvm, zfs + and overlayfs. + + + + @@ -100,68 +237,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - - - - - - The new container's rootfs should be a LVM or btrfs snapshot of the original. - - - - - - - - - - - In the case of a LVM-backed container, a size for the new - block device. By default, the new device will be made the - same size as the original. - - - - - - - - - - - For an LVM-backed container, the volume group name to use. By - default it is 'lxc'. - - - - - - - - - - - For an LVM-backed container, a string to prefix to the container name to - form the logical volume name. For instance, specifying - -n c1 -p lxc_ will cause the container rootfs to - be on a logical volume called lxc_c1. - - - - - - - - - - - For a non-snapshot LVM clone, the file system to use for the new - container. Note this option is ignored when requesting a - snapshotted container. - - - diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 1fa0fa88a..580b41d88 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -16,6 +16,7 @@ pkginclude_HEADERS = \ attach.h \ lxccontainer.h \ lxclock.h \ + bdev.h \ version.h if IS_BIONIC @@ -36,6 +37,7 @@ so_PROGRAMS = liblxc.so liblxc_so_SOURCES = \ arguments.c arguments.h \ + bdev.c bdev.h \ commands.c commands.h \ start.c start.h \ stop.c \ @@ -122,7 +124,7 @@ bin_SCRIPTS = \ lxc-checkconfig \ lxc-version \ lxc-create \ - lxc-clone \ + lxc-clone-sh \ lxc-shutdown \ lxc-destroy @@ -148,6 +150,7 @@ bin_PROGRAMS = \ lxc-unshare \ lxc-stop \ lxc-start \ + lxc-clone \ lxc-execute \ lxc-monitor \ lxc-monitord \ @@ -184,6 +187,7 @@ lxc_init_SOURCES = lxc_init.c lxc_monitor_SOURCES = lxc_monitor.c lxc_monitord_SOURCES = lxc_monitord.c lxc_restart_SOURCES = lxc_restart.c +lxc_clone_SOURCES = lxc_clone.c lxc_start_SOURCES = lxc_start.c lxc_stop_SOURCES = lxc_stop.c lxc_unfreeze_SOURCES = lxc_unfreeze.c diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c new file mode 100644 index 000000000..990e8c577 --- /dev/null +++ b/src/lxc/bdev.c @@ -0,0 +1,1189 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * 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 + */ + +/* + * this is all just a first shot for experiment. If we go this route, much + * shoudl change. bdev should be a directory with per-bdev file. Things which + * I'm doing by calling out to userspace should sometimes be done through + * libraries like liblvm2 + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include "lxc.h" +#include "config.h" +#include "conf.h" +#include "bdev.h" +#include "log.h" +#include "error.h" +#include "utils.h" +#include "namespace.h" +#include "parse.h" + +lxc_log_define(bdev, lxc); + +/* Define unshare() if missing from the C library */ +/* this is also in attach.c and lxccontainer.c: commonize it in utils.c */ +#ifndef HAVE_UNSHARE +static int unshare(int flags) +{ +#ifdef __NR_unshare +return syscall(__NR_unshare, flags); +#else +errno = ENOSYS; +return -1; +#endif +} +#endif + +static int do_rsync(const char *src, const char *dest) +{ + // call out to rsync + pid_t pid; + char *s; + size_t l; + + pid = fork(); + if (pid < 0) + return -1; + if (pid > 0) + return wait_for_pid(pid); + l = strlen(src) + 2; + s = malloc(l); + if (!s) + exit(1); + strcpy(s, src); + s[l-2] = '/'; + s[l-1] = '\0'; + + return execlp("rsync", "rsync", "-a", s, dest, (char *)NULL); +} + +static int blk_getsize(const char *path, unsigned long *size) +{ + int fd, ret; + + fd = open(path, O_RDONLY); + if (!fd) + return -1; + ret = ioctl(fd, BLKGETSIZE64, size); + close(fd); + return ret; +} + +/* + * These are copied from conf.c. However as conf.c will be moved to using + * the callback system, they can be pulled from there eventually, so we + * don't need to pollute utils.c with these low level functions + */ +static int find_fstype_cb(char* buffer, void *data) +{ + struct cbarg { + const char *rootfs; + const char *target; + int mntopt; + } *cbarg = data; + + char *fstype; + + /* we don't try 'nodev' entries */ + if (strstr(buffer, "nodev")) + return 0; + + fstype = buffer; + fstype += lxc_char_left_gc(fstype, strlen(fstype)); + fstype[lxc_char_right_gc(fstype, strlen(fstype))] = '\0'; + + DEBUG("trying to mount '%s'->'%s' with fstype '%s'", + cbarg->rootfs, cbarg->target, fstype); + + if (mount(cbarg->rootfs, cbarg->target, fstype, cbarg->mntopt, NULL)) { + DEBUG("mount failed with error: %s", strerror(errno)); + return 0; + } + + INFO("mounted '%s' on '%s', with fstype '%s'", + cbarg->rootfs, cbarg->target, fstype); + + return 1; +} + +static int mount_unknow_fs(const char *rootfs, const char *target, int mntopt) +{ + int i; + + struct cbarg { + const char *rootfs; + const char *target; + int mntopt; + } cbarg = { + .rootfs = rootfs, + .target = target, + .mntopt = mntopt, + }; + + /* + * find the filesystem type with brute force: + * first we check with /etc/filesystems, in case the modules + * are auto-loaded and fall back to the supported kernel fs + */ + char *fsfile[] = { + "/etc/filesystems", + "/proc/filesystems", + }; + + for (i = 0; i < sizeof(fsfile)/sizeof(fsfile[0]); i++) { + + int ret; + + if (access(fsfile[i], F_OK)) + continue; + + ret = lxc_file_for_each_line(fsfile[i], find_fstype_cb, &cbarg); + if (ret < 0) { + ERROR("failed to parse '%s'", fsfile[i]); + return -1; + } + + if (ret) + return 0; + } + + ERROR("failed to determine fs type for '%s'", rootfs); + return -1; +} + +static int do_mkfs(const char *path, const char *fstype) +{ + pid_t pid; + + if ((pid = fork()) < 0) { + ERROR("error forking"); + return -1; + } + if (pid > 0) + return wait_for_pid(pid); + + return execlp("mkfs", "mkfs", "-t", fstype, path, NULL); +} + +static char *linkderef(char *path, char *dest) +{ + struct stat sbuf; + ssize_t ret; + + ret = stat(path, &sbuf); + if (ret < 0) + return NULL; + if (!S_ISLNK(sbuf.st_mode)) + return path; + ret = readlink(path, dest, MAXPATHLEN); + if (ret < 0) { + SYSERROR("error reading link %s", path); + return NULL; + } else if (ret >= MAXPATHLEN) { + ERROR("link in %s too long", path); + return NULL; + } + dest[ret] = '\0'; + return dest; +} + +/* + * Given a bdev (presumably blockdev-based), detect the fstype + * by trying mounting (in a private mntns) it. + * @bdev: bdev to investigate + * @type: preallocated char* in which to write the fstype + * @len: length of passed in char* + * Returns length of fstype, of -1 on error + */ +static int detect_fs(struct bdev *bdev, char *type, int len) +{ + int p[2], ret; + size_t linelen; + pid_t pid; + FILE *f; + char *sp1, *sp2, *sp3, *line = NULL; + + if (!bdev || !bdev->src || !bdev->dest) + return -1; + + if (pipe(p) < 0) + return -1; + if ((pid = fork()) < 0) + return -1; + if (pid > 0) { + int status; + close(p[1]); + memset(type, 0, len); + ret = read(p[0], type, len-1); + close(p[0]); + if (ret < 0) { + SYSERROR("error reading from pipe"); + wait(&status); + return -1; + } else if (ret == 0) { + ERROR("child exited early - fstype not found"); + wait(&status); + return -1; + } + wait(&status); + type[len-1] = '\0'; + INFO("detected fstype %s for %s", type, bdev->src); + return ret; + } + + if (unshare(CLONE_NEWNS) < 0) + exit(1); + + ret = mount_unknow_fs(bdev->src, bdev->dest, 0); + if (ret < 0) { + ERROR("failed mounting %s onto %s to detect fstype", bdev->src, bdev->dest); + exit(1); + } + // if symlink, get the real dev name + char devpath[MAXPATHLEN]; + char *l = linkderef(bdev->src, devpath); + if (!l) + exit(1); + f = fopen("/proc/self/mounts", "r"); + if (!f) + exit(1); + while (getline(&line, &linelen, f) != -1) { + sp1 = index(line, ' '); + if (!sp1) + exit(1); + *sp1 = '\0'; + if (strcmp(line, l)) + continue; + sp2 = index(sp1+1, ' '); + if (!sp2) + exit(1); + *sp2 = '\0'; + sp3 = index(sp2+1, ' '); + if (!sp3) + exit(1); + *sp3 = '\0'; + sp2++; + if (write(p[1], sp2, strlen(sp2)) != strlen(sp2)) + exit(1); + exit(0); + } + exit(1); +} + +struct bdev_type { + char *name; + struct bdev_ops *ops; +}; + +static int is_dir(const char *path) +{ + struct stat statbuf; + int ret = stat(path, &statbuf); + if (ret == 0 && S_ISDIR(statbuf.st_mode)) + return 1; + return 0; +} + +static int dir_detect(const char *path) +{ + if (strncmp(path, "dir:", 4) == 0) + return 1; // take their word for it + if (is_dir(path)) + return 1; + return 0; +} + +// +// XXXXXXX plain directory bind mount ops +// +int dir_mount(struct bdev *bdev) +{ + if (strcmp(bdev->type, "dir")) + return -22; + if (!bdev->src || !bdev->dest) + return -22; + return mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC, NULL); +} + +int dir_umount(struct bdev *bdev) +{ + if (strcmp(bdev->type, "dir")) + return -22; + if (!bdev->src || !bdev->dest) + return -22; + return umount(bdev->dest); +} + +/* the bulk of this needs to become a common helper */ +static char *dir_new_path(char *src, const char *oldname, const char *name, + const char *oldpath, const char *lxcpath) +{ + char *ret, *p, *p2; + int l1, l2, nlen; + + nlen = strlen(src) + 1; + l1 = strlen(oldpath); + p = src; + /* if src starts with oldpath, look for oldname only after + * that path */ + if (strncmp(src, oldpath, l1) == 0) { + p += l1; + nlen += (strlen(lxcpath) - l1); + } + l2 = strlen(oldname); + while ((p = strstr(p, oldname)) != NULL) { + p += l2; + nlen += strlen(name) - l2; + } + + ret = malloc(nlen); + if (!ret) + return NULL; + + p = ret; + if (strncmp(src, oldpath, l1) == 0) { + p += sprintf(p, "%s", lxcpath); + src += l1; + } + + while ((p2 = strstr(src, oldname)) != NULL) { + strncpy(p, src, p2-src); // copy text up to oldname + p += p2-src; // move target pointer (p) + p += sprintf(p, "%s", name); // print new name in place of oldname + src = p2 + l2; // move src to end of oldname + } + sprintf(p, "%s", src); // copy the rest of src + return ret; +} + +/* + * for a simple directory bind mount, we substitute the old container + * name and paths for the new + */ +static int dir_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname, + const char *cname, const char *oldpath, const char *lxcpath, int snap, + unsigned long newsize) +{ + if (snap) { + ERROR("directories cannot be snapshotted. Try overlayfs."); + return -1; + } + + if (strcmp(orig->type, "dir")) { + ERROR("Directory clone from %s backing store is not supported", + orig->type); + return -1; + } + + if (!orig->dest || !orig->src) + return -1; + if (orig->data) { + new->data = strdup(orig->data); + if (!new->data) + return -1; + } + + new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath); + if (!new->dest) + return -1; + new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath); + if (!new->src) + return -1; + + return 0; +} + +struct bdev_ops dir_ops = { + .detect = &dir_detect, + .mount = &dir_mount, + .umount = &dir_umount, + .clone_paths = &dir_clonepaths, +}; + +// +// LVM ops +// + +/* + * Look at /sys/dev/block/maj:min/dm/uuid. If it contains the hardcoded LVM + * prefix "LVM-", then this is an lvm2 LV + */ +static int lvm_detect(const char *path) +{ + char devp[MAXPATHLEN], buf[4]; + FILE *fout; + int ret; + struct stat statbuf; + + if (strncmp(path, "lvm:", 4) == 0) + return 1; // take their word for it + + ret = stat(path, &statbuf); + if (ret != 0) + return 0; + if (!S_ISBLK(statbuf.st_mode)) + return 0; + + ret = snprintf(devp, MAXPATHLEN, "/sys/dev/block/%d:%d/dm/uuid", + major(statbuf.st_rdev), minor(statbuf.st_rdev)); + if (ret < 0 || ret >= MAXPATHLEN) { + ERROR("lvm uuid pathname too long"); + return 0; + } + fout = fopen(devp, "r"); + if (!fout) + return 0; + ret = fread(buf, 1, 4, fout); + fclose(fout); + if (ret != 4 || strncmp(buf, "LVM-", 4) != 0) + return 0; + return 1; +} + +static int lvm_mount(struct bdev *bdev) +{ + if (strcmp(bdev->type, "lvm")) + return -22; + if (!bdev->src || !bdev->dest) + return -22; + /* if we might pass in data sometime, then we'll have to enrich + * mount_unknow_fs */ + return mount_unknow_fs(bdev->src, bdev->dest, 0); +} + +static int lvm_umount(struct bdev *bdev) +{ + if (strcmp(bdev->type, "lvm")) + return -22; + if (!bdev->src || !bdev->dest) + return -22; + return umount(bdev->dest); +} + +/* + * path must be '/dev/$vg/$lv', $vg must be an existing VG, and $lv must + * not yet exist. This function will attempt to create /dev/$vg/$lv of + * size $size. + */ +static int lvm_create(const char *path, unsigned long size) +{ + int ret, pid; + char sz[24], *pathdup, *vg, *lv; + + if ((pid = fork()) < 0) { + SYSERROR("failed fork"); + return -1; + } + if (pid > 0) + return wait_for_pid(pid); + + // lvcreate default size is in M, not bytes. + ret = snprintf(sz, 24, "%lu", size/1000000); + if (ret < 0 || ret >= 24) + exit(1); + + pathdup = strdup(path); + if (!pathdup) + exit(1); + lv = rindex(pathdup, '/'); + if (!lv) { + free(pathdup); + exit(1); + } + *lv = '\0'; + lv++; + vg = rindex(pathdup, '/'); + if (!vg) + exit(1); + vg++; + ret = execlp("lvcreate", "lvcreate", "-L", sz, vg, "-n", lv, (char *)NULL); + free(pathdup); + return ret; +} + +static int lvm_snapshot(const char *orig, const char *path, unsigned long size) +{ + int ret, pid; + char sz[24], *pathdup, *lv; + + if ((pid = fork()) < 0) { + SYSERROR("failed fork"); + return -1; + } + if (pid > 0) + return wait_for_pid(pid); + // lvcreate default size is in M, not bytes. + ret = snprintf(sz, 24, "%lu", size/1000000); + if (ret < 0 || ret >= 24) + exit(1); + + pathdup = strdup(path); + if (!pathdup) + exit(1); + lv = rindex(pathdup, '/'); + if (!lv) { + free(pathdup); + exit(1); + } + *lv = '\0'; + lv++; + + ret = execlp("lvcreate", "lvcreate", "-s", "-L", sz, "-n", lv, orig, (char *)NULL); + free(pathdup); + return ret; +} + +static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname, + const char *cname, const char *oldpath, const char *lxcpath, int snap, + unsigned long newsize) +{ + char fstype[100]; + unsigned long size = newsize; + + if (!orig->src || !orig->dest) + return -1; + + if (strcmp(orig->type, "lvm")) { + ERROR("LVM clone from %s backing store is not supported", + orig->type); + return -1; + } + + if (orig->data) { + new->data = strdup(orig->data); + if (!new->data) + return -1; + } + new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath); + if (!new->dest) + return -1; + if (mkdir_p(new->dest, 0755) < 0) + return -1; + + + new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath); + if (!new->src) + return -1; + + if (!newsize && blk_getsize(orig->src, &size) < 0) { + ERROR("Error getting size of %s", orig->src); + return -1; + } + if (snap) { + if (lvm_snapshot(orig->src, new->src, size) < 0) { + ERROR("could not create %s snapshot of %s", new->src, orig->src); + return -1; + } + } else { + if (lvm_create(new->src, size) < 0) { + ERROR("Error creating new lvm blockdev"); + return -1; + } + if (detect_fs(orig, fstype, 100) < 0) { + ERROR("could not find fstype for %s", orig->src); + return -1; + } + if (do_mkfs(new->src, fstype) < 0) { + ERROR("Error creating filesystem type %s on %s", fstype, + new->src); + return -1; + } + } + + return 0; +} + +struct bdev_ops lvm_ops = { + .detect = &lvm_detect, + .mount = &lvm_mount, + .umount = &lvm_umount, + .clone_paths = &lvm_clonepaths, +}; + +// +// btrfs ops +// + +struct btrfs_ioctl_space_info { + unsigned long long flags; + unsigned long long total_bytes; + unsigned long long used_bytes; +}; + +struct btrfs_ioctl_space_args { + unsigned long long space_slots; + unsigned long long total_spaces; + struct btrfs_ioctl_space_info spaces[0]; +}; + +#define BTRFS_IOCTL_MAGIC 0x94 +#define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, unsigned long long) +#define BTRFS_IOC_SPACE_INFO _IOWR(BTRFS_IOCTL_MAGIC, 20, \ + struct btrfs_ioctl_space_args) + +static int btrfs_detect(const char *path) +{ + struct stat st; + int fd, ret; + struct btrfs_ioctl_space_args sargs; + + // make sure this is a btrfs filesystem + fd = open(path, O_RDONLY); + if (fd < 0) + return 0; + sargs.space_slots = 0; + sargs.total_spaces = 0; + ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, &sargs); + close(fd); + if (ret < 0) + return 0; + + // and make sure it's a subvolume. + ret = stat(path, &st); + if (ret < 0) + return 0; + + if (st.st_ino == 256 && S_ISDIR(st.st_mode)) + return 1; + + return 0; +} + +int btrfs_mount(struct bdev *bdev) +{ + if (strcmp(bdev->type, "btrfs")) + return -22; + if (!bdev->src || !bdev->dest) + return -22; + return mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC, NULL); +} + +int btrfs_umount(struct bdev *bdev) +{ + if (strcmp(bdev->type, "btrfs")) + return -22; + if (!bdev->src || !bdev->dest) + return -22; + return umount(bdev->dest); +} + +#define BTRFS_SUBVOL_NAME_MAX 4039 +#define BTRFS_PATH_NAME_MAX 4087 + +struct btrfs_ioctl_vol_args { + signed long long fd; + char name[BTRFS_PATH_NAME_MAX + 1]; +}; + +#define BTRFS_IOCTL_MAGIC 0x94 +#define BTRFS_IOC_SUBVOL_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 24, \ + struct btrfs_ioctl_vol_args_v2) +#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \ + struct btrfs_ioctl_vol_args_v2) +#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \ + struct btrfs_ioctl_vol_args) + +#define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0) + +struct btrfs_ioctl_vol_args_v2 { + signed long long fd; + unsigned long long transid; + unsigned long long flags; + union { + struct { + unsigned long long size; + //struct btrfs_qgroup_inherit *qgroup_inherit; + void *qgroup_inherit; + }; + unsigned long long unused[4]; + }; + char name[BTRFS_SUBVOL_NAME_MAX + 1]; +}; + +static int btrfs_subvolume_create(const char *path) +{ + int ret, fd = -1; + struct btrfs_ioctl_vol_args args; + char *p, *newfull = strdup(path); + + if (!newfull) { + ERROR("Error: out of memory"); + return -1; + } + + p = rindex(newfull, '/'); + if (!p) { + ERROR("bad path: %s", path); + return -1; + } + *p = '\0'; + + if ((fd = open(newfull, O_RDONLY)) < 0) { + ERROR("Error opening %s", newfull); + free(newfull); + return -1; + } + + memset(&args, 0, sizeof(args)); + strncpy(args.name, p+1, BTRFS_SUBVOL_NAME_MAX); + args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0; + ret = ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args); + INFO("btrfs: snapshot create ioctl returned %d", ret); +{ + FILE *f = fopen("/tmp/a", "a"); + fprintf(f, "ioctl returned %d\n", ret); + fclose(f); +} + + free(newfull); + close(fd); + return ret; +} + +static int btrfs_snapshot(const char *orig, const char *new) +{ + int fd = -1, fddst = -1, ret = -1; + struct btrfs_ioctl_vol_args_v2 args; + char *newdir, *newname, *newfull = NULL; + + newfull = strdup(new); + if (!newfull) { + ERROR("Error: out of memory"); + goto out; + } + // make sure the directory doesn't already exist + if (rmdir(newfull) < 0 && errno != -ENOENT) { + SYSERROR("Error removing empty new rootfs"); + goto out; + } + newname = basename(newfull); + newdir = dirname(newfull); + fd = open(orig, O_RDONLY); + if (fd < 0) { + SYSERROR("Error opening original rootfs %s", orig); + goto out; + } + fddst = open(newdir, O_RDONLY); + if (fddst < 0) { + SYSERROR("Error opening new container dir %s", newdir); + goto out; + } + + memset(&args, 0, sizeof(args)); + args.fd = fd; + strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX); + args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0; + ret = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args); + INFO("btrfs: snapshot create ioctl returned %d", ret); + +out: + if (fddst != -1) + close(fddst); + if (fd != -1) + close(fd); + if (newfull) + free(newfull); + return ret; +} + +static int btrfs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname, + const char *cname, const char *oldpath, const char *lxcpath, int snap, + unsigned long newsize) +{ + if (!orig->dest || !orig->src) + return -1; + + if (strcmp(orig->type, "btrfs")) { + ERROR("btrfs cloen from %s backing store is not supported", + orig->type); + return -1; + } + + if ((new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath)) == NULL) + return -1; + + if ((new->src = strdup(new->dest)) == NULL) + return -1; + + if (orig->data && (new->data = strdup(orig->data)) == NULL) + return -1; + + if (snap) + return btrfs_snapshot(orig->dest, new->dest); + + if (rmdir(new->dest) < 0 && errno != -ENOENT) { + SYSERROR("removing %s\n", new->dest); + return -1; + } + + return btrfs_subvolume_create(new->dest); +} + +struct bdev_ops btrfs_ops = { + .detect = &btrfs_detect, + .mount = &btrfs_mount, + .umount = &btrfs_umount, + .clone_paths = &btrfs_clonepaths, +}; + +// +// overlayfs ops +// + +static int overlayfs_detect(const char *path) +{ + if (strncmp(path, "overlayfs:", 10) == 0) + return 1; // take their word for it + return 0; +} + +// +// XXXXXXX plain directory bind mount ops +// +int overlayfs_mount(struct bdev *bdev) +{ + char *options, *dup, *lower, *upper; + int len; + int ret; + + if (strcmp(bdev->type, "overlayfs")) + return -22; + if (!bdev->src || !bdev->dest) + return -22; + + // separately mount it first + // mount -t overlayfs -oupperdir=${upper},lowerdir=${lower} lower dest + dup = strdupa(bdev->src); + if (!(lower = index(dup, ':'))) + return -22; + if (!(upper = index(++lower, ':'))) + return -22; + *upper = '\0'; + upper++; + + // TODO We should check whether bdev->src is a blockdev, and if so + // but for now, only support overlays of a basic directory + + len = strlen(lower) + strlen(upper) + strlen("upperdir=,lowerdir=") + 1; + options = alloca(len); + ret = snprintf(options, len, "upperdir=%s,lowerdir=%s", upper, lower); + if (ret < 0 || ret >= len) + return -1; + ret = mount(lower, bdev->dest, "overlayfs", MS_MGC_VAL, options); + if (ret < 0) + SYSERROR("overlayfs: error mounting %s onto %s options %s", + lower, bdev->dest, options); + else + INFO("overlayfs: mounted %s onto %s options %s", + lower, bdev->dest, options); + return ret; +} + +int overlayfs_umount(struct bdev *bdev) +{ + if (strcmp(bdev->type, "overlayfs")) + return -22; + if (!bdev->src || !bdev->dest) + return -22; + return umount(bdev->dest); +} + +static int overlayfs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname, + const char *cname, const char *oldpath, const char *lxcpath, int snap, + unsigned long newsize) +{ + if (!snap) { + ERROR("overlayfs is only for snapshot clones"); + return -22; + } + + if (!orig->src || !orig->dest) + return -1; + + new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath); + if (!new->dest) + return -1; + if (mkdir_p(new->dest, 0755) < 0) + return -1; + + if (strcmp(orig->type, "dir") == 0) { + char *delta; + int ret, len; + if (!snap) + return -1; + // if we have /var/lib/lxc/c2/rootfs, then delta will be + // /var/lib/lxc/c2/delta0 + delta = strdup(new->dest); + if (!delta) { + return -1; + } + if (strlen(delta) < 6) { + free(delta); + return -22; + } + strcpy(&delta[strlen(delta)-6], "delta0"); + if ((ret = mkdir(delta, 0755)) < 0) { + SYSERROR("error: mkdir %s", delta); + free(delta); + return -1; + } + + // the src will be 'overlayfs:lowerdir:upperdir' + len = strlen(delta) + strlen(orig->src) + 12; + new->src = malloc(len); + if (!new->src) { + free(delta); + return -ENOMEM; + } + ret = snprintf(new->src, len, "overlayfs:%s:%s", orig->src, delta); + free(delta); + if (ret < 0 || ret >= len) + return -ENOMEM; + } else if (strcmp(orig->type, "lvm") == 0) { + ERROR("overlayfs clone of lvm container is not yet supported"); + // Note, supporting this will require overlayfs_mount supporting + // mounting of the underlay. No big deal, just needs to be done. + return -1; + } else if (strcmp(orig->type, "overlayfs") == 0) { + // What exactly do we want to do here? + // I think we want to use the original lowerdir, with a + // private delta which is originally rsynced from the + // original delta + char *osrc, *odelta, *nsrc, *ndelta; + int len, ret; + if (!(osrc = strdup(orig->src))) + return -22; + nsrc = index(osrc, ':') + 1; + if (nsrc != osrc + 10 || (odelta = index(nsrc, ':')) == NULL) { + free(osrc); + return -22; + } + *odelta = '\0'; + odelta++; + ndelta = dir_new_path(odelta, oldname, cname, oldpath, lxcpath); + if (!ndelta) { + free(osrc); + return -ENOMEM; + } + if (do_rsync(odelta, ndelta) < 0) { + ERROR("copying overlayfs delta"); + return -1; + } + len = strlen(nsrc) + strlen(ndelta) + 12; + new->src = malloc(len); + if (!new->src) { + free(osrc); + free(ndelta); + return -ENOMEM; + } + ret = snprintf(new->src, len, "overlayfs:%s:%s", nsrc, ndelta); + free(osrc); + free(ndelta); + if (ret < 0 || ret >= len) + return -ENOMEM; + } + + return 0; +} + +struct bdev_ops overlayfs_ops = { + .detect = &overlayfs_detect, + .mount = &overlayfs_mount, + .umount = &overlayfs_umount, + .clone_paths = &overlayfs_clonepaths, +}; + +struct bdev_type bdevs[] = { + {.name = "lvm", .ops = &lvm_ops,}, + {.name = "btrfs", .ops = &btrfs_ops,}, + {.name = "dir", .ops = &dir_ops,}, + {.name = "overlayfs", .ops = &overlayfs_ops,}, +}; + +static const size_t numbdevs = sizeof(bdevs) / sizeof(struct bdev_type); + +void bdev_put(struct bdev *bdev) +{ + if (bdev->data) + free(bdev->data); + if (bdev->src) + free(bdev->src); + if (bdev->dest) + free(bdev->dest); + free(bdev); +} + +struct bdev *bdev_get(const char *type) +{ + int i; + struct bdev *bdev; + + for (i=0; iops = bdevs[i].ops; + bdev->type = bdevs[i].name; + return bdev; +} + +struct bdev *bdev_init(const char *src, const char *dst, const char *data) +{ + int i; + struct bdev *bdev; + + for (i=0; idetect(src); + if (r) + break; + } + if (i == numbdevs) + return NULL; + bdev = malloc(sizeof(struct bdev)); + if (!bdev) + return NULL; + memset(bdev, 0, sizeof(struct bdev)); + bdev->ops = bdevs[i].ops; + bdev->type = bdevs[i].name; + if (data) + bdev->data = strdup(data); + if (src) + bdev->src = strdup(src); + if (dst) + bdev->dest = strdup(dst); + + return bdev; +} + +/* + * If we're not snaphotting, then bdev_copy becomes a simple case of mount + * the original, mount the new, and rsync the contents. + */ +struct bdev *bdev_copy(const char *src, const char *oldname, const char *cname, + const char *oldpath, const char *lxcpath, const char *bdevtype, + int snap, const char *bdevdata, unsigned long newsize) +{ + struct bdev *orig, *new; + pid_t pid; + + /* if the container name doesn't show up in the rootfs path, then + * we don't know how to come up with a new name + */ + if (strstr(src, oldname) == NULL) { + ERROR("original rootfs path %s doesn't include container name %s", + src, oldname); + return NULL; + } + + orig = bdev_init(src, NULL, NULL); + if (!orig) { + ERROR("failed to detect blockdev type for %s\n", src); + return NULL; + } + + if (!orig->dest) { + int ret; + orig->dest = malloc(MAXPATHLEN); + if (!orig->dest) { + ERROR("out of memory"); + bdev_put(orig); + return NULL; + } + ret = snprintf(orig->dest, MAXPATHLEN, "%s/%s/rootfs", oldpath, oldname); + if (ret < 0 || ret >= MAXPATHLEN) { + ERROR("rootfs path too long"); + bdev_put(orig); + return NULL; + } + } + + new = bdev_get(bdevtype ? bdevtype : orig->type); + if (!new) { + ERROR("no such block device type: %s", bdevtype ? bdevtype : orig->type); + bdev_put(orig); + return NULL; + } + + if (new->ops->clone_paths(orig, new, oldname, cname, oldpath, lxcpath, snap, newsize) < 0) { + ERROR("failed getting pathnames for cloned storage: %s\n", src); + bdev_put(orig); + bdev_put(new); + return NULL; + } + + pid = fork(); + if (pid < 0) { + SYSERROR("fork"); + bdev_put(orig); + bdev_put(new); + return NULL; + } + + if (pid > 0) { + int ret = wait_for_pid(pid); + bdev_put(orig); + if (ret < 0) { + bdev_put(new); + return NULL; + } + return new; + } + + if (unshare(CLONE_NEWNS) < 0) { + SYSERROR("unshare CLONE_NEWNS"); + exit(1); + } + if (snap) + exit(0); + + // If not a snapshot, copy the fs. + if (orig->ops->mount(orig) < 0) { + ERROR("failed mounting %s onto %s\n", src, orig->dest); + exit(1); + } + if (new->ops->mount(new) < 0) { + ERROR("failed mounting %s onto %s\n", new->src, new->dest); + exit(1); + } + if (do_rsync(orig->dest, new->dest) < 0) { + ERROR("rsyncing %s to %s\n", orig->src, new->src); + exit(1); + } + // don't bother umounting, ns exit will do that + + exit(0); +} diff --git a/src/lxc/bdev.h b/src/lxc/bdev.h new file mode 100644 index 000000000..131f158c8 --- /dev/null +++ b/src/lxc/bdev.h @@ -0,0 +1,50 @@ +#ifndef __LXC_BDEV_H +#define __LXC_BDEV_H +/* blockdev operations for: + * dir, raw, btrfs, overlayfs, aufs, lvm, loop, zfs, btrfs + * someday: qemu-nbd, qcow2, qed + */ + +#include "config.h" +#include "lxccontainer.h" + +struct bdev; + +struct bdev_ops { + /* detect whether path is of this bdev type */ + int (*detect)(const char *path); + // mount requires src and dest to be set. + int (*mount)(struct bdev *bdev); + int (*umount)(struct bdev *bdev); + /* given original mount, rename the paths for cloned container */ + int (*clone_paths)(struct bdev *orig, struct bdev *new, const char *oldname, + const char *cname, const char *oldpath, const char *lxcpath, + int snap, unsigned long newsize); +}; + +struct bdev { + struct bdev_ops *ops; + char *type; + char *src; + char *dest; + char *data; +}; + +/* + * Instantiate a bdev object. The src is used to determine which blockdev + * type this should be. The dst and data are optional, and will be used + * in case of mount/umount. + * + * Optionally, src can be 'dir:/var/lib/lxc/c1' or 'lvm:/dev/lxc/c1'. For + * other backing stores, this will allow additional options. In particular, + * "overlayfs:/var/lib/lxc/canonical/rootfs:/var/lib/lxc/c1/delta" will mean + * use /var/lib/lxc/canonical/rootfs as lower dir, and /var/lib/lxc/c1/delta + * as the upper, writeable layer. + */ +struct bdev *bdev_init(const char *src, const char *dst, const char *data); + +struct bdev *bdev_copy(const char *src, const char *oldname, const char *cname, + const char *oldpath, const char *lxcpath, const char *bdevtype, + int snap, const char *bdevdata, unsigned long newsize); +void bdev_put(struct bdev *bdev); +#endif diff --git a/src/lxc/conf.c b/src/lxc/conf.c index f89505003..27b972755 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -64,6 +64,7 @@ #include "log.h" #include "lxc.h" /* for lxc_cgroup_set() */ #include "caps.h" /* for lxc_caps_last_cap() */ +#include "bdev.h" #if HAVE_APPARMOR #include @@ -590,8 +591,8 @@ int pin_rootfs(const char *rootfs) return -2; if (!realpath(rootfs, absrootfs)) { - SYSERROR("failed to get real path for '%s'", rootfs); - return -1; + INFO("failed to get real path for '%s', not pinning", rootfs); + return -2; } if (access(absrootfs, F_OK)) { @@ -1163,6 +1164,12 @@ static int setup_rootfs(struct lxc_conf *conf) } } + // First try mounting rootfs using a bdev + struct bdev *bdev = bdev_init(rootfs->path, rootfs->mount, NULL); + if (bdev && bdev->ops->mount(bdev) == 0) { + DEBUG("mounted '%s' on '%s'", rootfs->path, rootfs->mount); + return 0; + } if (mount_rootfs(rootfs->path, rootfs->mount)) { ERROR("failed to mount rootfs"); return -1; diff --git a/src/lxc/lxc-clone.in b/src/lxc/lxc-clone-sh.in similarity index 100% rename from src/lxc/lxc-clone.in rename to src/lxc/lxc-clone-sh.in diff --git a/src/lxc/lxc_clone.c b/src/lxc/lxc_clone.c new file mode 100644 index 000000000..b9c7a6eb7 --- /dev/null +++ b/src/lxc/lxc_clone.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "config.h" +#include "lxc.h" +#include "conf.h" +#include "state.h" +#include "lxccontainer.h" + +lxc_log_define(lxc_clone, lxc); + +void usage(char *me) +{ + printf("Usage: %s [-s] [-B backingstore] [-L size] [-K] [-M] [-H]\n", me); + printf(" [-p lxcpath] [-P newlxcpath] orig new\n"); + printf("\n"); + printf(" -s: snapshot rather than copy\n"); + printf(" -B: use specified new backingstore. Default is the same as\n"); + printf(" the original. Options include btrfs, lvm, overlayfs, \n"); + printf(" dir\n"); + printf(" -L: for blockdev-backed backingstore, use specified size\n"); + printf(" -K: Keep name - do not change the container name\n"); + printf(" -M: Keep macaddr - do not choose a random new mac address\n"); + printf(" -H: copy Hooks - copy mount hooks into container directory\n"); + printf(" and substitute container names and lxcpaths\n"); + printf(" -p: use container orig from custom lxcpath\n"); + printf(" -P: create container new in custom lxcpath\n"); + exit(1); +} + +static struct option options[] = { + { "snapshot", no_argument, 0, 's'}, + { "backingstore", required_argument, 0, 'B'}, + { "size", required_argument, 0, 'L'}, + { "orig", required_argument, 0, 'o'}, + { "new", required_argument, 0, 'n'}, + { "vgname", required_argument, 0, 'v'}, + { "keepname", no_argument, 0, 'K'}, + { "keepmac", no_argument, 0, 'M'}, + { "copyhooks", no_argument, 0, 'H'}, // should this be default? + { "lxcpath", required_argument, 0, 'p'}, + { "newpath", required_argument, 0, 'P'}, + { "fstype", required_argument, 0, 't'}, + { "help", no_argument, 0, 'h'}, + { 0, 0, 0, 0 }, +}; + +int main(int argc, char *argv[]) +{ + struct lxc_container *c1 = NULL, *c2 = NULL; + int snapshot = 0, keepname = 0, keepmac = 0, copyhooks = 0; + int flags = 0, option_index; + long newsize = 0; + char *bdevtype = NULL, *lxcpath = NULL, *newpath = NULL, *fstype = NULL; + char *orig = NULL, *new = NULL, *vgname = NULL; + char c; + + if (argc < 3) + usage(argv[0]); + + while (1) { + c = getopt_long(argc, argv, "sB:L:o:n:v:KMHp:P:t:h", options, &option_index); + if (c == -1) + break; + switch (c) { + case 's': snapshot = 1; break; + case 'B': bdevtype = optarg; break; + case 'L': newsize = atol(optarg); break; + case 'o': orig = optarg; break; + case 'n': new = optarg; break; + case 'v': vgname = optarg; break; + case 'K': keepname = 1; break; + case 'M': keepmac = 1; break; + case 'H': copyhooks = 1; break; + case 'p': lxcpath = optarg; break; + case 'P': newpath = optarg; break; + case 't': fstype = optarg; break; + case 'h': usage(argv[0]); + default: break; + } + } + if (optind == argc-2 && !orig) + orig = argv[optind++]; + if (optind == argc-1 && !new) + new = argv[optind++]; + if (optind < argc) { + printf("%d extraneous arguments\n", argc-optind); + usage(argv[0]); + } + if (!new || !orig) { + printf("Error: you must provide orig and new names\n"); + usage(argv[0]); + } + + if (snapshot) flags |= LXC_CLONE_SNAPSHOT; + if (keepname) flags |= LXC_CLONE_KEEPNAME; + if (keepmac) flags |= LXC_CLONE_KEEPMACADDR; + if (copyhooks) flags |= LXC_CLONE_COPYHOOKS; + + // vgname and fstype could be supported by sending them through the + // bdevdata. However, they currently are not yet. I'm not convinced + // they are worthwhile. + if (vgname) { + printf("Error: vgname not supported\n"); + usage(argv[0]); + } + if (fstype) { + printf("Error: fstype not supported\n"); + usage(argv[0]); + } + + c1 = lxc_container_new(orig, lxcpath); + if (!c1) + exit(1); + if (!c1->is_defined(c1)) { + fprintf(stderr, "Error: container %s is not defined\n", orig); + lxc_container_put(c1); + exit(1); + } + c2 = c1->clone(c1, new, newpath, flags, bdevtype, NULL, newsize); + if (c2 == NULL) { + lxc_container_put(c1); + fprintf(stderr, "clone failed\n"); + exit(1); + } + printf("Created container %s as %s of %s\n", new, + snapshot ? "snapshot" : "copy", orig); + lxc_container_put(c1); + lxc_container_put(c2); + return(0); +} diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 740a8d99e..312cc99e4 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -17,23 +17,42 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include "config.h" #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 "version.h" #include "log.h" -#include -#include -#include -#include +#include "bdev.h" #include #include +/* Define unshare() if missing from the C library */ +/* this is also in attach.c and lxccontainer.c: commonize it in utils.c */ +#ifndef HAVE_UNSHARE +static int unshare(int flags) +{ +#ifdef __NR_unshare + return syscall(__NR_unshare, flags); +#else + errno = ENOSYS; + return -1; +#endif +} +#else +int unshare(int); +#endif + lxc_log_define(lxc_container, lxc); /* LOCKING @@ -536,10 +555,8 @@ static bool lxcapi_create(struct lxc_container *c, char *t, char *const argv[]) { bool bret = false; pid_t pid; - int ret, status; - char *tpath = NULL; - int len, nargs = 0; - char **newargv; + char *tpath = NULL, **newargv; + int ret, len, nargs = 0; if (!c) return false; @@ -566,7 +583,7 @@ static bool lxcapi_create(struct lxc_container *c, char *t, char *const argv[]) goto out; /* we're going to fork. but since we'll wait for our child, we - don't need to lxc_container_get */ + * don't need to lxc_container_get */ if (lxclock(c->slock, 0)) { ERROR("failed to grab global container lock for %s\n", c->name); @@ -637,26 +654,8 @@ static bool lxcapi_create(struct lxc_container *c, char *t, char *const argv[]) 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)); + if (wait_for_pid(pid) != 0) { + ERROR("container creation template for %s failed\n", c->name); goto out_unlock; } @@ -822,7 +821,6 @@ static bool lxcapi_save_config(struct lxc_container *c, const char *alt_file) static bool lxcapi_destroy(struct lxc_container *c) { pid_t pid; - int ret, status; if (!c) return false; @@ -840,23 +838,12 @@ static bool lxcapi_destroy(struct lxc_container *c) 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 + if (wait_for_pid(pid) < 0) { + ERROR("Error destroying container %s", c->name); return false; } - return WEXITSTATUS(status) == 0; + return true; } static bool lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v) @@ -1027,6 +1014,518 @@ const char *lxc_get_version(void) return lxc_version(); } +static int copy_file(char *old, char *new) +{ + int in, out; + ssize_t len, ret; + char buf[8096]; + struct stat sbuf; + + if (file_exists(new)) { + ERROR("copy destination %s exists", new); + return -1; + } + ret = stat(old, &sbuf); + if (ret < 0) { + SYSERROR("stat'ing %s", old); + return -1; + } + + in = open(old, O_RDONLY); + if (in < 0) { + SYSERROR("opening original file %s", old); + return -1; + } + out = open(new, O_CREAT | O_EXCL | O_WRONLY, 0644); + if (out < 0) { + SYSERROR("opening new file %s", new); + close(in); + return -1; + } + + while (1) { + len = read(in, buf, 8096); + if (len < 0) { + SYSERROR("reading old file %s", old); + goto err; + } + if (len == 0) + break; + ret = write(out, buf, len); + if (ret < len) { // should we retry? + SYSERROR("write to new file %s was interrupted", new); + goto err; + } + } + close(in); + close(out); + + // we set mode, but not owner/group + ret = chmod(new, sbuf.st_mode); + if (ret) { + SYSERROR("setting mode on %s", new); + return -1; + } + + return 0; + +err: + close(in); + close(out); + return -1; +} + +/* + * we're being passed result of two strstrs(x, y). We want to write + * all data up to the first found string, or to end of the string if + * neither string was found. + * This function will return the earliest found string if any, or else + * NULL + */ +static const char *lowest_nonnull(const char *p1, const char *p2) +{ + if (!p1) + return p2; + if (!p2) + return p1; + return p1 < p2 ? p1 : p2; +} + +static int is_word_sep(char c) +{ + switch(c) { + case '\0': + case '\n': + case '\r': + case '\t': + case ' ': + case '=': + case '/': + return 1; + default: return 0; + } +} + +static const char *find_first_wholeword(const char *p, const char *word) +{ + if (!p) + return NULL; + + while ((p = strstr(p, word)) != NULL) { + if (is_word_sep(*(p-1)) && is_word_sep(p[strlen(word)])) + return p; + p++; + } + return NULL; +} + +static int update_name_and_paths(const char *path, struct lxc_container *oldc, + const char *newname, const char *newpath) +{ + FILE *f; + size_t flen; + char *contents; + const char *p0, *p1, *p2, *end; + const char *oldpath = oldc->get_config_path(oldc); + const char *oldname = oldc->name; + + f = fopen(path, "r"); + if (!f) { + SYSERROR("opening old config"); + return -1; + } + if (fseek(f, 0, SEEK_END) < 0) { + SYSERROR("seeking to end of old config"); + fclose(f); + return -1; + } + flen = ftell(f); + if (flen < 0) { + fclose(f); + SYSERROR("telling size of old config"); + return -1; + } + if (fseek(f, 0, SEEK_SET) < 0) { + fclose(f); + SYSERROR("rewinding old config"); + return -1; + } + contents = malloc(flen); + if (!contents) { + SYSERROR("out of memory"); + fclose(f); + } + if (fread(contents, 1, flen, f) != flen) { + free(contents); + fclose(f); + SYSERROR("reading old config"); + return -1; + } + if (fclose(f) < 0) { + free(contents); + SYSERROR("closing old config"); + return -1; + } + + f = fopen(path, "w"); + if (!f) { + SYSERROR("reopening config"); + free(contents); + return -1; + } + + p0 = contents; + end = contents + flen; + while (1) { + p1 = find_first_wholeword(p0, oldpath); + p2 = find_first_wholeword(p0, oldname); + if (!p1 && !p2) { + // write the rest and be done + if (fwrite(p0, 1, (end-p0), f) != (end-p0)) { + SYSERROR("writing new config"); + free(contents); + fclose(f); + return -1; + } + free(contents); + fclose(f); + // success + return 0; + } else { + const char *p = lowest_nonnull(p1, p2); + const char *new = (p == p2) ? newname : newpath; + if (fwrite(p0, 1, (p-p0), f) != (p-p0)) { + SYSERROR("writing new config"); + free(contents); + fclose(f); + return -1; + } + p0 = p; + // now write the newpath or newname + if (fwrite(new, 1, strlen(new), f) != strlen(new)) { + SYSERROR("writing new name or path in new config"); + free(contents); + fclose(f); + return -1; + } + p0 += (p == p2) ? strlen(oldname) : strlen(oldpath); + } + } +} + +static int copyhooks(struct lxc_container *oldc, struct lxc_container *c) +{ + int i; + int ret; + struct lxc_list *it; + + for (i=0; ilxc_conf->hooks[i]) { + char *hookname = it->elem; + char *fname = rindex(hookname, '/'); + char tmppath[MAXPATHLEN]; + if (!fname) // relative path - we don't support, but maybe we should + return 0; + // copy the script, and change the entry in confile + ret = snprintf(tmppath, MAXPATHLEN, "%s/%s/%s", + c->config_path, c->name, fname+1); + if (ret < 0 || ret >= MAXPATHLEN) + return -1; + ret = copy_file(it->elem, tmppath); + if (ret < 0) + return -1; + free(it->elem); + it->elem = strdup(tmppath); + if (!it->elem) { + ERROR("out of memory copying hook path"); + return -1; + } + update_name_and_paths(it->elem, oldc, c->name, c->get_config_path(c)); + } + } + + c->save_config(c, NULL); + return 0; +} + +static void new_hwaddr(char *hwaddr) +{ + FILE *f = fopen("/dev/urandom", "r"); + if (f) { + unsigned int seed; + int ret = fread(&seed, sizeof(seed), 1, f); + if (ret != 1) + seed = time(NULL); + fclose(f); + srand(seed); + } else + srand(time(NULL)); + snprintf(hwaddr, 18, "00:16:3e:%02x:%02x:%02x", + rand() % 255, rand() % 255, rand() % 255); +} + +static void network_new_hwaddrs(struct lxc_container *c) +{ + struct lxc_list *it; + + lxc_list_for_each(it, &c->lxc_conf->network) { + struct lxc_netdev *n = it->elem; + if (n->hwaddr) + new_hwaddr(n->hwaddr); + } +} + +static int copy_fstab(struct lxc_container *oldc, struct lxc_container *c) +{ + char newpath[MAXPATHLEN]; + char *oldpath = oldc->lxc_conf->fstab; + int ret; + + if (!oldpath) + return 0; + + char *p = rindex(oldpath, '/'); + if (!p) + return -1; + ret = snprintf(newpath, MAXPATHLEN, "%s/%s%s", + c->config_path, c->name, p); + if (ret < 0 || ret >= MAXPATHLEN) { + ERROR("error printing new path for %s", oldpath); + return -1; + } + if (file_exists(newpath)) { + ERROR("error: fstab file %s exists", newpath); + return -1; + } + + if (copy_file(oldpath, newpath) < 0) { + ERROR("error: copying %s to %s", oldpath, newpath); + return -1; + } + free(c->lxc_conf->fstab); + c->lxc_conf->fstab = strdup(newpath); + if (!c->lxc_conf->fstab) { + ERROR("error: allocating pathname"); + return -1; + } + + return 0; +} + +static int copy_storage(struct lxc_container *c0, struct lxc_container *c, + const char *newtype, int flags, const char *bdevdata, unsigned long newsize) +{ + struct bdev *bdev; + + bdev = bdev_copy(c0->lxc_conf->rootfs.path, c0->name, c->name, + c0->config_path, c->config_path, newtype, !!(flags & LXC_CLONE_SNAPSHOT), + bdevdata, newsize); + if (!bdev) { + ERROR("error copying storage"); + return -1; + } + free(c->lxc_conf->rootfs.path); + c->lxc_conf->rootfs.path = strdup(bdev->src); + bdev_put(bdev); + if (!c->lxc_conf->rootfs.path) + return -1; + // here we could also update all lxc.mount.entries or even + // items in the lxc.mount fstab list. As discussed on m-l, + // we could do either any source paths starting with the + // lxcpath/oldname, or simply anythign which is not a virtual + // fs or a bind mount. + return 0; +} + +static int clone_update_rootfs(struct lxc_container *c, int flags) +{ + int ret = -1; + char path[MAXPATHLEN]; + struct bdev *bdev; + FILE *fout; + pid_t pid; + + if (flags & LXC_CLONE_KEEPNAME) + return 0; + + /* update hostname in rootfs */ + /* we're going to mount, so run in a clean namespace to simplify cleanup */ + + pid = fork(); + if (pid < 0) + return -1; + if (pid > 0) + return wait_for_pid(pid); + + if (unshare(CLONE_NEWNS) < 0) { + ERROR("error unsharing mounts"); + exit(1); + } + bdev = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); + if (!bdev) + exit(1); + if (bdev->ops->mount(bdev) < 0) + exit(1); + ret = snprintf(path, MAXPATHLEN, "%s/etc/hostname", bdev->dest); + if (ret < 0 || ret >= MAXPATHLEN) + exit(1); + if (!(fout = fopen(path, "w"))) { + SYSERROR("unable to open %s: ignoring\n", path); + exit(0); + } + if (fprintf(fout, "%s", c->name) < 0) + exit(1); + if (fclose(fout) < 0) + exit(1); + exit(0); +} + +/* + * We want to support: +sudo lxc-clone -o o1 -n n1 -s -L|-fssize fssize -v|--vgname vgname \ + -p|--lvprefix lvprefix -t|--fstype fstype -B backingstore + +-s [ implies overlayfs] +-s -B overlayfs +-s -B aufs + +only rootfs gets converted (copied/snapshotted) on clone. +*/ + +static int create_file_dirname(char *path) +{ + char *p = rindex(path, '/'); + int ret; + + if (!p) + return -1; + *p = '\0'; + ret = mkdir(path, 0755); + if (ret && errno != EEXIST) + SYSERROR("creating container path %s\n", path); + *p = '/'; + return ret; +} + +struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname, + const char *lxcpath, int flags, + const char *bdevtype, const char *bdevdata, unsigned long newsize) +{ + struct lxc_container *c2 = NULL; + char newpath[MAXPATHLEN]; + int ret; + const char *n, *l; + FILE *fout; + + if (!c || !c->is_defined(c)) + return NULL; + + if (lxclock(c->privlock, 0)) + return NULL; + + if (c->is_running(c)) { + ERROR("error: Original container (%s) is running", c->name); + goto out; + } + + // Make sure the container doesn't yet exist. + n = newname ? newname : c->name; + l = lxcpath ? lxcpath : c->get_config_path(c); + ret = snprintf(newpath, MAXPATHLEN, "%s/%s/config", l, n); + if (ret < 0 || ret >= MAXPATHLEN) { + SYSERROR("clone: failed making config pathname"); + goto out; + } + if (file_exists(newpath)) { + ERROR("error: clone: %s exists", newpath); + goto out; + } + + if (create_file_dirname(newpath) < 0) { + ERROR("Error creating container dir for %s", newpath); + goto out; + } + + // copy the configuration, tweak it as needed, + fout = fopen(newpath, "w"); + if (!fout) { + SYSERROR("open %s", newpath); + goto out; + } + write_config(fout, c->lxc_conf); + fclose(fout); + + if (update_name_and_paths(newpath, c, n, l) < 0) { + ERROR("Error updating name in cloned config"); + goto out; + } + + sprintf(newpath, "%s/%s/rootfs", l, n); + if (mkdir(newpath, 0755) < 0) { + SYSERROR("error creating %s", newpath); + goto out; + } + + c2 = lxc_container_new(n, l); + if (!c) { + ERROR("clone: failed to create new container (%s %s)", n, l); + goto out; + } + + // copy hooks if requested + if (flags & LXC_CLONE_COPYHOOKS) { + ret = copyhooks(c, c2); + if (ret < 0) { + ERROR("error copying hooks"); + c2->destroy(c2); + lxc_container_put(c2); + goto out; + } + } + + if (copy_fstab(c, c2) < 0) { + ERROR("error copying fstab"); + c2->destroy(c2); + lxc_container_put(c2); + goto out; + } + + // update macaddrs + if (!(flags & LXC_CLONE_KEEPMACADDR)) + network_new_hwaddrs(c2); + + // copy/snapshot rootfs's + ret = copy_storage(c, c2, bdevtype, flags, bdevdata, newsize); + if (ret < 0) { + c2->destroy(c2); + lxc_container_put(c2); + goto out; + } + + if (!c2->save_config(c2, NULL)) { + c2->destroy(c2); + lxc_container_put(c2); + goto out; + } + + if (clone_update_rootfs(c2, flags) < 0) { + //c2->destroy(c2); + lxc_container_put(c2); + goto out; + } + + // TODO: update c's lxc.snapshot = count + lxcunlock(c->privlock); + return c2; + +out: + lxcunlock(c->privlock); + if (c2) + lxc_container_put(c2); + + return NULL; +} + struct lxc_container *lxc_container_new(const char *name, const char *configpath) { struct lxc_container *c; @@ -1103,6 +1602,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath c->set_cgroup_item = lxcapi_set_cgroup_item; c->get_config_path = lxcapi_get_config_path; c->set_config_path = lxcapi_set_config_path; + c->clone = lxcapi_clone; /* we'll allow the caller to update these later */ if (lxc_log_init(NULL, "none", NULL, "lxc_container", 0, c->config_path)) { diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index de9854c90..a4be7537c 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -1,9 +1,17 @@ +#ifndef __LXC_CONTAINER_H +#define __LXC_CONTAINER_H #include "lxclock.h" #include #include #include +#define LXC_CLONE_KEEPNAME (1 << 0) +#define LXC_CLONE_COPYHOOKS (1 << 1) +#define LXC_CLONE_KEEPMACADDR (1 << 2) +#define LXC_CLONE_SNAPSHOT (1 << 3) +#define LXC_CLONE_MAXFLAGS (1 << 4) + struct lxc_container { // private fields char *name; @@ -72,6 +80,33 @@ struct lxc_container { const char *(*get_config_path)(struct lxc_container *c); bool (*set_config_path)(struct lxc_container *c, const char *path); + /* + * @c: the original container + * @newname: new name for the container. If NULL, the same name is used, and + * a new lxcpath MUST be specified. + * @lxcpath: lxcpath in which to create the new container. If NULL, then the + * original container's lxcpath will be used. (Shoudl we use the default + * instead?) + * @flags: additional flags to modify cloning behavior. + * LXC_CLONE_KEEPNAME: don't edit the rootfs to change the hostname. + * LXC_CLONE_COPYHOOKS: copy all hooks into the container dir + * LXC_CLONE_KEEPMACADDR: don't change the mac address on network interfaces. + * LXC_CLONE_SNAPSHOT: snapshot the original filesystem(s). If @devtype was not + * specified, then do so with the native bdevtype if possible, else use an + * overlayfs. + * @bdevtype: optionally force the cloned bdevtype to a specified plugin. By + * default the original is used (subject to snapshot requirements). + * @bdevdata: information about how to create the new storage (i.e. fstype and + * fsdata) + * @newsize: in case of a block device backing store, an optional size. If 0, + * then the original backing store's size will be used if possible. Note this + * only applies to the rootfs. For any other filesystems, the original size + * will be duplicated. + */ + struct lxc_container *(*clone)(struct lxc_container *c, const char *newname, + const char *lxcpath, int flags, const char *bdevtype, + const char *bdevdata, unsigned long newsize); + #if 0 bool (*commit_cgroups)(struct lxc_container *c); bool (*reread_cgroups)(struct lxc_container *c); @@ -93,3 +128,4 @@ const char *lxc_get_version(void); char ** lxc_get_valid_keys(); char ** lxc_get_valid_values(char *key); #endif +#endif diff --git a/src/lxc/utils.c b/src/lxc/utils.c index 6a154f916..2fa2b0cc8 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include "log.h" @@ -172,3 +174,21 @@ out: fclose(fin); return default_lxcpath; } + +int wait_for_pid(pid_t pid) +{ + int status, ret; + +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == -EINTR) + goto again; + return -1; + } + if (ret != pid) + goto again; + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + return -1; + return 0; +} diff --git a/src/lxc/utils.h b/src/lxc/utils.h index 8e6a7489c..ee463a7f8 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -58,4 +58,9 @@ extern int __build_bug_on_failed; } while(0) #endif +/* + * wait on a child we forked + */ +extern int wait_for_pid(pid_t pid); + #endif diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 4cbeeb3fe..c0ce6484a 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -13,6 +13,7 @@ lxc_test_get_item_SOURCES = get_item.c lxc_test_getkeys_SOURCES = getkeys.c lxc_test_lxcpath_SOURCES = lxcpath.c lxc_test_cgpath_SOURCES = cgpath.c +lxc_test_clonetest_SOURCES = clonetest.c AM_CFLAGS=-I$(top_srcdir)/src \ -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ @@ -23,7 +24,7 @@ AM_CFLAGS=-I$(top_srcdir)/src \ 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 lxc-test-lxcpath \ - lxc-test-cgpath + lxc-test-cgpath lxc-test-clonetest endif @@ -38,4 +39,5 @@ EXTRA_DIST = \ lxcpath.c \ saveconfig.c \ shutdowntest.c \ + clonetest.c \ startone.c diff --git a/src/tests/clonetest.c b/src/tests/clonetest.c new file mode 100644 index 000000000..fcb5ea66c --- /dev/null +++ b/src/tests/clonetest.c @@ -0,0 +1,178 @@ +/* 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 "clonetest1" +#define MYNAME2 "clonetest2" + +int main(int argc, char *argv[]) +{ + struct lxc_container *c = NULL, *c2 = NULL, *c3 = NULL; + int ret = 1; + + c = lxc_container_new(MYNAME, NULL); + c2 = lxc_container_new(MYNAME2, NULL); + if (c) { + c->destroy(c); + lxc_container_put(c); + c = NULL; + } + if (c2) { + c2->destroy(c2); + lxc_container_put(c2); + c2 = NULL; + } + + if ((c = lxc_container_new(MYNAME, NULL)) == NULL) { + fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME); + ret = 1; + goto out; + } + c->save_config(c, NULL); + if (!c->createl(c, "ubuntu", NULL)) { + fprintf(stderr, "%d: failed to create a container\n", __LINE__); + goto out; + } + c->load_config(c, NULL); + + if (!c->is_defined(c)) { + fprintf(stderr, "%d: %s thought it was not defined\n", __LINE__, MYNAME); + goto out; + } + + c2 = c->clone(c, MYNAME2, NULL, 0, NULL, NULL, 0); + if (!c2) { + fprintf(stderr, "%d: %s clone returned NULL\n", __LINE__, MYNAME2); + goto out; + } + + if (!c2->is_defined(c)) { + fprintf(stderr, "%d: %s not defined after clone\n", __LINE__, MYNAME2); + goto out; + } + + fprintf(stderr, "directory backing store tests passed\n"); + + // now test with lvm + // Only do this if clonetestlvm1 exists - user has to set this up + // in advance + //c2->destroy(c2); + lxc_container_put(c2); + //c->destroy(c); + lxc_container_put(c); + c = NULL; + + c2 = lxc_container_new("clonetestlvm2", NULL); + if (c2) { + if (c2->is_defined(c2)) + c2->destroy(c2); + lxc_container_put(c2); + } + c2 = lxc_container_new("clonetest-o1", NULL); + if (c2) { + if (c2->is_defined(c2)) + c2->destroy(c2); + lxc_container_put(c2); + } + c2 = lxc_container_new("clonetest-o2", NULL); + if (c2) { + if (c2->is_defined(c2)) + c2->destroy(c2); + lxc_container_put(c2); + } + c2 = NULL; + + // lvm-copied + c = lxc_container_new("clonetestlvm1", NULL); + if (!c) { + fprintf(stderr, "failed loading clonetestlvm1\n"); + goto out; + } + if (!c->is_defined(c)) { + fprintf(stderr, "clonetestlvm1 does not exist, skipping lvm tests\n"); + ret = 0; + goto out; + } + + if ((c2 = c->clone(c, "clonetestlvm2", NULL, 0, NULL, NULL, 0)) == NULL) { + fprintf(stderr, "lvm clone failed\n"); + goto out; + } + + lxc_container_put(c2); + + // lvm-snapshot + c2 = lxc_container_new("clonetestlvm3", NULL); + if (c2) { + if (c2->is_defined(c2)) + c2->destroy(c2); + lxc_container_put(c2); + c2 = NULL; + } + + if ((c2 = c->clone(c, "clonetestlvm3", NULL, LXC_CLONE_SNAPSHOT, NULL, NULL, 0)) == NULL) { + fprintf(stderr, "lvm clone failed\n"); + goto out; + } + lxc_container_put(c2); + lxc_container_put(c); + c = c2 = NULL; + + if ((c = lxc_container_new(MYNAME, NULL)) == NULL) { + fprintf(stderr, "error opening original container for overlay test\n"); + goto out; + } + + // Now create an overlayfs clone of a dir-backed container + if ((c2 = c->clone(c, "clonetest-o1", NULL, LXC_CLONE_SNAPSHOT, "overlayfs", NULL, 0)) == NULL) { + fprintf(stderr, "overlayfs clone of dir failed\n"); + goto out; + } + + // Now create an overlayfs clone of the overlayfs clone + if ((c3 = c2->clone(c2, "clonetest-o2", NULL, LXC_CLONE_SNAPSHOT, "overlayfs", NULL, 0)) == NULL) { + fprintf(stderr, "overlayfs clone of overlayfs failed\n"); + goto out; + } + + fprintf(stderr, "all clone tests passed for %s\n", c->name); + ret = 0; + +out: + if (c3) { + lxc_container_put(c3); + } + if (c2) { + //c2->destroy(c2); // keep around to verify manuall + lxc_container_put(c2); + } + if (c) { + //c->destroy(c); + lxc_container_put(c); + } + exit(ret); +} From 3baa76fe36bd2b59645a952c3a47a960090c38d2 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 26 Apr 2013 00:14:37 +0200 Subject: [PATCH 072/325] implement zfs bdev and clone Signed-off-by: Serge Hallyn --- src/lxc/bdev.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index 990e8c577..3df53a5d4 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -427,6 +427,182 @@ struct bdev_ops dir_ops = { .clone_paths = &dir_clonepaths, }; + +// +// XXXXXXX zfs ops +// There are two ways we could do this. We could always specify the +// 'zfs device' (i.e. tank/lxc lxc/container) as rootfs. But instead +// (at least right now) we have lxc-create specify $lxcpath/$lxcname/rootfs +// as the mountpoint, so that it is always mounted. +// +// That means 'mount' is really never needed and could be noop, but for the +// sake of flexibility let's always bind-mount. +// + +static int zfs_list_entry(const char *path, char *output) +{ + FILE *f; + int found=0; + + if ((f = popen("zfs list", "r")) == NULL) { + SYSERROR("popen failed"); + return 0; + } + while (fgets(output, LXC_LOG_BUFFER_SIZE, f)) { + if (strstr(output, path)) { + found = 1; + break; + } + } + (void) pclose(f); + + return found; +} + +static int zfs_detect(const char *path) +{ + char *output = malloc(LXC_LOG_BUFFER_SIZE); + int found; + + if (!output) { + ERROR("out of memory"); + return 0; + } + found = zfs_list_entry(path, output); + free(output); + return found; +} + +int zfs_mount(struct bdev *bdev) +{ + if (strcmp(bdev->type, "zfs")) + return -22; + if (!bdev->src || !bdev->dest) + return -22; + return mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC, NULL); +} + +int zfs_umount(struct bdev *bdev) +{ + if (strcmp(bdev->type, "zfs")) + return -22; + if (!bdev->src || !bdev->dest) + return -22; + return umount(bdev->dest); +} + +static int zfs_clone(const char *opath, const char *npath, const char *oname, + const char *nname, const char *lxcpath, int snapshot) +{ + // use the 'zfs list | grep opath' entry to get the zfsroot + char output[MAXPATHLEN], option[MAXPATHLEN], *p; + int ret; + pid_t pid; + + if (zfs_list_entry(opath, output) < 0) + return -1; + + if ((p = index(output, ' ')) == NULL) + return -1; + *p = '\0'; + if ((p = rindex(output, '/')) == NULL) + return -1; + *p = '\0'; + + ret = snprintf(option, MAXPATHLEN, "-omountpoint=%s/%s/rootfs", + lxcpath, nname); + if (ret < 0 || ret >= MAXPATHLEN) + return -1; + + // zfsroot is output up to ' ' + // zfs create -omountpoint=$lxcpath/$lxcname $zfsroot/$nname + if (!snapshot) { + if ((pid = fork()) < 0) + return -1; + if (!pid) { + char dev[MAXPATHLEN]; + ret = snprintf(dev, MAXPATHLEN, "%s/%s", output, nname); + if (ret < 0 || ret >= MAXPATHLEN) + exit(1); + return execlp("zfs", "zfs", "create", option, dev, NULL); + } + return wait_for_pid(pid); + } else { + // if snapshot, do + // 'zfs snapshot zfsroot/oname@nname + // zfs clone zfsroot/oname@nname zfsroot/nname + char path1[MAXPATHLEN], path2[MAXPATHLEN]; + + ret = snprintf(path1, MAXPATHLEN, "%s/%s@%s", output, + oname, nname); + if (ret < 0 || ret >= MAXPATHLEN) + return -1; + (void) snprintf(path2, MAXPATHLEN, "%s/%s", output, nname); + + // if the snapshot exists, delete it + if ((pid = fork()) < 0) + return -1; + if (!pid) { + return execlp("zfs", "zfs", "destroy", path1, NULL); + } + // it probably doesn't exist so destroy probably will fail. + (void) wait_for_pid(pid); + + // run first (snapshot) command + if ((pid = fork()) < 0) + return -1; + if (!pid) { + return execlp("zfs", "zfs", "snapshot", path1, NULL); + } + if (wait_for_pid(pid) < 0) + return -1; + + // run second (clone) command + if ((pid = fork()) < 0) + return -1; + if (!pid) { + return execlp("zfs", "zfs", "clone", option, path1, path2, NULL); + } + return wait_for_pid(pid); + } +} + +static int zfs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname, + const char *cname, const char *oldpath, const char *lxcpath, int snap, + unsigned long newsize) +{ + if (!orig->src || !orig->dest) + return -1; + + if (strcmp(orig->type, "zfs")) { + ERROR("zfs clone from %s backing store is not supported", + orig->type); + return -1; + } + + if (orig->data) { + new->data = strdup(orig->data); + if (!new->data) + return -1; + } + new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath); + if (!new->dest) + return -1; + + new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath); + if (!new->src) + return -1; + + return zfs_clone(orig->src, new->src, oldname, cname, lxcpath, snap); +} + +struct bdev_ops zfs_ops = { + .detect = &zfs_detect, + .mount = &zfs_mount, + .umount = &zfs_umount, + .clone_paths = &zfs_clonepaths, +}; + // // LVM ops // @@ -1021,6 +1197,7 @@ struct bdev_ops overlayfs_ops = { }; struct bdev_type bdevs[] = { + {.name = "zfs", .ops = &zfs_ops,}, {.name = "lvm", .ops = &lvm_ops,}, {.name = "btrfs", .ops = &btrfs_ops,}, {.name = "dir", .ops = &dir_ops,}, From ca52dcb55961d75e0163f237c92d225964c786bd Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 26 Apr 2013 18:00:28 +0200 Subject: [PATCH 073/325] Several backing store improvements allow copy clones from other bdevs for lvm and zfs, as we don't yet support passing options, only default VG of 'lxc' and default zfsroot of 'tank' are supported when converting another backing store type. refuse deletion of container which has lvm or zfs snapshots. Note that since a zfs clone must be made from a zfs snapshot, which is made from the original zfs fs, even after we lxc-destroy the snapshotted container we still must manually remove the snapshot. This can be handled automatically, by looking for snapshots where c1 is the original, c2 is the clone, tank/c2 no longer exists, but tank/c1@c2 does. We can then remove tank/c1@c2 and feel free to remove tank/c1. This patch does NOT do that yet. Make sure not to return when we're a forked child. Signed-off-by: Serge Hallyn --- src/lxc/bdev.c | 167 ++++++++++++++++++++++++++--------------- src/lxc/bdev.h | 7 ++ src/lxc/lxc-destroy.in | 52 +++++++++++-- 3 files changed, 158 insertions(+), 68 deletions(-) diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index 3df53a5d4..4ba550cf2 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -81,7 +81,8 @@ static int do_rsync(const char *src, const char *dest) s[l-2] = '/'; s[l-1] = '\0'; - return execlp("rsync", "rsync", "-a", s, dest, (char *)NULL); + execlp("rsync", "rsync", "-a", s, dest, (char *)NULL); + exit(1); } static int blk_getsize(const char *path, unsigned long *size) @@ -189,7 +190,8 @@ static int do_mkfs(const char *path, const char *fstype) if (pid > 0) return wait_for_pid(pid); - return execlp("mkfs", "mkfs", "-t", fstype, path, NULL); + execlp("mkfs", "mkfs", "-t", fstype, path, NULL); + exit(1); } static char *linkderef(char *path, char *dest) @@ -391,31 +393,25 @@ static int dir_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna const char *cname, const char *oldpath, const char *lxcpath, int snap, unsigned long newsize) { + int len, ret; + if (snap) { ERROR("directories cannot be snapshotted. Try overlayfs."); return -1; } - if (strcmp(orig->type, "dir")) { - ERROR("Directory clone from %s backing store is not supported", - orig->type); - return -1; - } - if (!orig->dest || !orig->src) return -1; - if (orig->data) { - new->data = strdup(orig->data); - if (!new->data) - return -1; - } - new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath); - if (!new->dest) - return -1; - new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath); + len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3; + new->src = malloc(len); if (!new->src) return -1; + ret = snprintf(new->src, len, "%s/%s/rootfs", lxcpath, cname); + if (ret < 0 || ret >= len) + return -1; + if ((new->dest = strdup(new->src)) == NULL) + return -1; return 0; } @@ -499,8 +495,10 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname, int ret; pid_t pid; - if (zfs_list_entry(opath, output) < 0) - return -1; + if (!zfs_list_entry(opath, output)) + // default is tank. I'd prefer lxc, but apparently this is + // tradition. + sprintf(output, "tank"); if ((p = index(output, ' ')) == NULL) return -1; @@ -524,7 +522,8 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname, ret = snprintf(dev, MAXPATHLEN, "%s/%s", output, nname); if (ret < 0 || ret >= MAXPATHLEN) exit(1); - return execlp("zfs", "zfs", "create", option, dev, NULL); + execlp("zfs", "zfs", "create", option, dev, NULL); + exit(1); } return wait_for_pid(pid); } else { @@ -543,7 +542,8 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname, if ((pid = fork()) < 0) return -1; if (!pid) { - return execlp("zfs", "zfs", "destroy", path1, NULL); + execlp("zfs", "zfs", "destroy", path1, NULL); + exit(1); } // it probably doesn't exist so destroy probably will fail. (void) wait_for_pid(pid); @@ -552,7 +552,8 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname, if ((pid = fork()) < 0) return -1; if (!pid) { - return execlp("zfs", "zfs", "snapshot", path1, NULL); + execlp("zfs", "zfs", "snapshot", path1, NULL); + exit(1); } if (wait_for_pid(pid) < 0) return -1; @@ -561,7 +562,8 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname, if ((pid = fork()) < 0) return -1; if (!pid) { - return execlp("zfs", "zfs", "clone", option, path1, path2, NULL); + execlp("zfs", "zfs", "clone", option, path1, path2, NULL); + exit(1); } return wait_for_pid(pid); } @@ -571,27 +573,26 @@ static int zfs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna const char *cname, const char *oldpath, const char *lxcpath, int snap, unsigned long newsize) { + int len, ret; + if (!orig->src || !orig->dest) return -1; - if (strcmp(orig->type, "zfs")) { - ERROR("zfs clone from %s backing store is not supported", + if (snap && strcmp(orig->type, "zfs")) { + ERROR("zfs snapshot from %s backing store is not supported", orig->type); return -1; } - if (orig->data) { - new->data = strdup(orig->data); - if (!new->data) - return -1; - } - new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath); - if (!new->dest) - return -1; - - new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath); + len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3; + new->src = malloc(len); if (!new->src) return -1; + ret = snprintf(new->src, len, "%s/%s/rootfs", lxcpath, cname); + if (ret < 0 || ret >= len) + return -1; + if ((new->dest = strdup(new->src)) == NULL) + return -1; return zfs_clone(orig->src, new->src, oldname, cname, lxcpath, snap); } @@ -699,9 +700,9 @@ static int lvm_create(const char *path, unsigned long size) if (!vg) exit(1); vg++; - ret = execlp("lvcreate", "lvcreate", "-L", sz, vg, "-n", lv, (char *)NULL); + execlp("lvcreate", "lvcreate", "-L", sz, vg, "-n", lv, (char *)NULL); free(pathdup); - return ret; + exit(1); } static int lvm_snapshot(const char *orig, const char *path, unsigned long size) @@ -733,7 +734,16 @@ static int lvm_snapshot(const char *orig, const char *path, unsigned long size) ret = execlp("lvcreate", "lvcreate", "-s", "-L", sz, "-n", lv, orig, (char *)NULL); free(pathdup); - return ret; + exit(1); +} + +// this will return 1 for physical disks, qemu-nbd, loop, etc +// right now only lvm is a block device +static int is_blktype(struct bdev *b) +{ + if (strcmp(b->type, "lvm") == 0) + return 1; + return 0; } static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname, @@ -742,14 +752,30 @@ static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna { char fstype[100]; unsigned long size = newsize; + int len, ret; if (!orig->src || !orig->dest) return -1; if (strcmp(orig->type, "lvm")) { - ERROR("LVM clone from %s backing store is not supported", - orig->type); - return -1; + if (snap) { + ERROR("LVM snapshot from %s backing store is not supported", + orig->type); + return -1; + } + // Use VG 'lxc' by default + // We will want to support custom VGs, at least as specified through + // /etc/lxc/lxc.conf, preferably also over cmdline + len = strlen("/dev/lxc/") + strlen(cname) + 1; + if ((new->src = malloc(len)) == NULL) + return -1; + ret = snprintf(new->src, len, "/dev/lxc/%s", cname); + if (ret < 0 || ret >= len) + return -1; + } else { + new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath); + if (!new->src) + return -1; } if (orig->data) { @@ -757,21 +783,32 @@ static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna if (!new->data) return -1; } - new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath); + + len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3; + new->dest = malloc(len); if (!new->dest) return -1; + ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname); + if (ret < 0 || ret >= len) + return -1; if (mkdir_p(new->dest, 0755) < 0) return -1; - - new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath); - if (!new->src) - return -1; - - if (!newsize && blk_getsize(orig->src, &size) < 0) { - ERROR("Error getting size of %s", orig->src); - return -1; + if (is_blktype(orig)) { + if (!newsize && blk_getsize(orig->src, &size) < 0) { + ERROR("Error getting size of %s", orig->src); + return -1; + } + if (detect_fs(orig, fstype, 100) < 0) { + INFO("could not find fstype for %s, using ext3", orig->src); + return -1; + } + } else { + sprintf(fstype, "ext3"); + if (!newsize) + size = 1000000000; // default to 1G } + if (snap) { if (lvm_snapshot(orig->src, new->src, size) < 0) { ERROR("could not create %s snapshot of %s", new->src, orig->src); @@ -782,10 +819,6 @@ static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna ERROR("Error creating new lvm blockdev"); return -1; } - if (detect_fs(orig, fstype, 100) < 0) { - ERROR("could not find fstype for %s", orig->src); - return -1; - } if (do_mkfs(new->src, fstype) < 0) { ERROR("Error creating filesystem type %s on %s", fstype, new->src); @@ -997,15 +1030,27 @@ static int btrfs_clonepaths(struct bdev *orig, struct bdev *new, const char *old return -1; if (strcmp(orig->type, "btrfs")) { - ERROR("btrfs cloen from %s backing store is not supported", - orig->type); - return -1; + int len, ret; + if (snap) { + ERROR("btrfs snapshot from %s backing store is not supported", + orig->type); + return -1; + } + len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3; + new->src = malloc(len); + if (!new->src) + return -1; + ret = snprintf(new->src, len, "%s/%s/rootfs", lxcpath, cname); + if (ret < 0 || ret >= len) + return -1; + } else { + // in case rootfs is in custom path, reuse it + if ((new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath)) == NULL) + return -1; + } - if ((new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath)) == NULL) - return -1; - - if ((new->src = strdup(new->dest)) == NULL) + if ((new->dest = strdup(new->src)) == NULL) return -1; if (orig->data && (new->data = strdup(orig->data)) == NULL) diff --git a/src/lxc/bdev.h b/src/lxc/bdev.h index 131f158c8..cc0359250 100644 --- a/src/lxc/bdev.h +++ b/src/lxc/bdev.h @@ -22,6 +22,13 @@ struct bdev_ops { int snap, unsigned long newsize); }; +/* + * When lxc-start (conf.c) is mounting a rootfs, then src will be the + * 'lxc.rootfs' value, dest will be mount dir (i.e. $libdir/lxc) When clone + * or create is doing so, then dest will be $lxcpath/$lxcname/rootfs, since + * we may need to rsync from one to the other. + * data is so far unused. + */ struct bdev { struct bdev_ops *ops; char *type; diff --git a/src/lxc/lxc-destroy.in b/src/lxc/lxc-destroy.in index 2e7a486f6..de29909f1 100644 --- a/src/lxc/lxc-destroy.in +++ b/src/lxc/lxc-destroy.in @@ -40,6 +40,8 @@ help() { echo " -P lxcpath container is in specified lxcpath" >&2 } +. @DATADIR@/lxc/lxc.functions + usage_err() { [ -n "$1" ] && echo "$1" >&2 usage @@ -47,21 +49,46 @@ usage_err() { } verify_zfs() { - path=$1 - if which zfs >/dev/null 2>&1 && zfs list | grep -q $path; then + local path=$1 + which zfs > /dev/null 2>&1 || { echo no; return; } + if zfs list -H $path >/dev/null 2>&1; then echo zfs else echo no fi } +busy_zfs() { + local path=$1 + local dev + dev=`zfs list -H $path 2>/dev/null | awk '{ print $1 }'` + if zfs list -t snapshot | grep -q "$dev"; then + echo busy + else + echo zfs + fi +} + +verify_lvm() { + local path=$1 + if [ -b $path -o -h $path ]; then + lvdisplay $path > /dev/null 2>&1 && { echo lvm; return; } + fi + echo no +} + +busy_lvm() { + local path=$1 + lvdisplay $path | grep -q "LV snapshot status.*source of" && { echo busy; return; } + echo lvm +} + optarg_check() { if [ -z "$2" ]; then usage_err "option '$1' requires an argument" fi } -. @DATADIR@/lxc/lxc.functions force=0 while [ $# -gt 0 ]; do @@ -134,14 +161,25 @@ 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 + if [ `verify_lvm $rootdev` = "lvm" ]; then + if [ `busy_lvm $rootdev` = "busy" ]; then + echo "$rootdev has lvm snapshots - not deleting" + exit 1 + else echo "removing backing store: $rootdev" lvremove -f $rootdev fi elif [ `verify_zfs $rootdev` = "zfs" ]; then - zfs destroy $(zfs list | grep $rootdev | awk '{ print $1 }') + if [ `busy_zfs $rootdev` = "busy" ]; then + echo "$rootdev has zfs snapshots - not deleting" + exit 1 + else + zfs destroy $(zfs list | grep $rootdev | awk '{ print $1 }') + if [ $? -ne 0 ]; then + echo "zfs destroy failed - please wait a bit and try again" + exit 1 + fi + fi elif [ -h "$rootdev" -o -d "$rootdev" ]; then if which btrfs >/dev/null 2>&1 && btrfs subvolume list "$rootdev" >/dev/null 2>&1; then From 31a95fecd2e0b1408e9a97e3ae36a7770544d1a2 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Sat, 27 Apr 2013 04:59:11 +0200 Subject: [PATCH 074/325] allow site-wide customization of zfsroot and lvm vg /etc/lxc/lxc.conf can contain zfsroot = custom1 lvm_vg = vg0 (Otherwise the defaults are 'lxc' for lvm_vg, and 'lxc' for zfsroot) Signed-off-by: Serge Hallyn --- src/lxc/bdev.c | 39 +++++++++++++------------- src/lxc/utils.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lxc/utils.h | 2 ++ 3 files changed, 94 insertions(+), 20 deletions(-) diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index 4ba550cf2..a3577a207 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -492,34 +492,33 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname, { // use the 'zfs list | grep opath' entry to get the zfsroot char output[MAXPATHLEN], option[MAXPATHLEN], *p; + const char *zfsroot = output; int ret; pid_t pid; - if (!zfs_list_entry(opath, output)) - // default is tank. I'd prefer lxc, but apparently this is - // tradition. - sprintf(output, "tank"); - - if ((p = index(output, ' ')) == NULL) - return -1; - *p = '\0'; - if ((p = rindex(output, '/')) == NULL) - return -1; - *p = '\0'; + if (zfs_list_entry(opath, output)) { + // zfsroot is output up to ' ' + if ((p = index(output, ' ')) == NULL) + return -1; + *p = '\0'; + if ((p = rindex(output, '/')) == NULL) + return -1; + *p = '\0'; + } else + zfsroot = default_zfs_root(); ret = snprintf(option, MAXPATHLEN, "-omountpoint=%s/%s/rootfs", lxcpath, nname); if (ret < 0 || ret >= MAXPATHLEN) return -1; - // zfsroot is output up to ' ' // zfs create -omountpoint=$lxcpath/$lxcname $zfsroot/$nname if (!snapshot) { if ((pid = fork()) < 0) return -1; if (!pid) { char dev[MAXPATHLEN]; - ret = snprintf(dev, MAXPATHLEN, "%s/%s", output, nname); + ret = snprintf(dev, MAXPATHLEN, "%s/%s", zfsroot, nname); if (ret < 0 || ret >= MAXPATHLEN) exit(1); execlp("zfs", "zfs", "create", option, dev, NULL); @@ -532,11 +531,11 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname, // zfs clone zfsroot/oname@nname zfsroot/nname char path1[MAXPATHLEN], path2[MAXPATHLEN]; - ret = snprintf(path1, MAXPATHLEN, "%s/%s@%s", output, + ret = snprintf(path1, MAXPATHLEN, "%s/%s@%s", zfsroot, oname, nname); if (ret < 0 || ret >= MAXPATHLEN) return -1; - (void) snprintf(path2, MAXPATHLEN, "%s/%s", output, nname); + (void) snprintf(path2, MAXPATHLEN, "%s/%s", zfsroot, nname); // if the snapshot exists, delete it if ((pid = fork()) < 0) @@ -758,18 +757,18 @@ static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna return -1; if (strcmp(orig->type, "lvm")) { + const char *vg; + if (snap) { ERROR("LVM snapshot from %s backing store is not supported", orig->type); return -1; } - // Use VG 'lxc' by default - // We will want to support custom VGs, at least as specified through - // /etc/lxc/lxc.conf, preferably also over cmdline - len = strlen("/dev/lxc/") + strlen(cname) + 1; + vg = default_lvm_vg(); + len = strlen("/dev/") + strlen(vg) + strlen(cname) + 2; if ((new->src = malloc(len)) == NULL) return -1; - ret = snprintf(new->src, len, "/dev/lxc/%s", cname); + ret = snprintf(new->src, len, "/dev/%s/%s", vg, cname); if (ret < 0 || ret >= len) return -1; } else { diff --git a/src/lxc/utils.c b/src/lxc/utils.c index 2fa2b0cc8..be1ce8860 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -137,7 +137,80 @@ static char *copypath(char *p) } char *default_lxcpath; +#define DEFAULT_VG "lxc" +char *default_lvmvg; +#define DEFAULT_ZFSROOT "lxc" +char *default_zfsroot; +const char *default_lvm_vg(void) +{ + char buf[1024], *p; + FILE *fin; + + if (default_lvmvg) + return default_lvmvg; + + fin = fopen(LXC_GLOBAL_CONF, "r"); + if (fin) { + while (fgets(buf, 1024, fin)) { + if (buf[0] == '#') + continue; + p = strstr(buf, "lvm_vg"); + if (!p) + continue; + p = strchr(p, '='); + if (!p) + continue; + p++; + while (*p && (*p == ' ' || *p == '\t')) p++; + if (!*p) + continue; + default_lvmvg = copypath(p); + goto out; + } + } + default_lvmvg = DEFAULT_VG; + +out: + if (fin) + fclose(fin); + return default_lvmvg; +} + +const char *default_zfs_root(void) +{ + char buf[1024], *p; + FILE *fin; + + if (default_zfsroot) + return default_zfsroot; + + fin = fopen(LXC_GLOBAL_CONF, "r"); + if (fin) { + while (fgets(buf, 1024, fin)) { + if (buf[0] == '#') + continue; + p = strstr(buf, "zfsroot"); + if (!p) + continue; + p = strchr(p, '='); + if (!p) + continue; + p++; + while (*p && (*p == ' ' || *p == '\t')) p++; + if (!*p) + continue; + default_zfsroot = copypath(p); + goto out; + } + } + default_zfsroot = DEFAULT_ZFSROOT; + +out: + if (fin) + fclose(fin); + return default_zfsroot; +} const char *default_lxc_path(void) { char buf[1024], *p; diff --git a/src/lxc/utils.h b/src/lxc/utils.h index ee463a7f8..09af34ac3 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -31,6 +31,8 @@ extern int mkdir_p(const char *dir, mode_t mode); * path. Caller must free this buffer. */ extern const char *default_lxc_path(void); +extern const char *default_zfs_root(void); +extern const char *default_lvm_vg(void); /** * BUILD_BUG_ON - break compile if a condition is true. From 1e1bb42a8fca68d9fa9391e6644aeff296479499 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Mon, 29 Apr 2013 14:50:30 +0200 Subject: [PATCH 075/325] add vg and zfsroot options to lxc.functions and use in lxc-create also make sure to drop spaces between = and variable in lxc.conf Signed-off-by: Serge Hallyn --- src/lxc/lxc-create.in | 3 ++- src/lxc/lxc.functions.in | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in index 1858c119a..286ed6a00 100644 --- a/src/lxc/lxc-create.in +++ b/src/lxc/lxc-create.in @@ -134,7 +134,8 @@ optarg_check() { backingstore=_unset fstype=ext4 fssize=500M -vgname=lxc +vgname=$lxc_vg +zfsroot=$lxc_zfsroot custom_rootfs="" while [ $# -gt 0 ]; do diff --git a/src/lxc/lxc.functions.in b/src/lxc/lxc.functions.in index aa5717d0b..416267f74 100644 --- a/src/lxc/lxc.functions.in +++ b/src/lxc/lxc.functions.in @@ -34,4 +34,24 @@ get_default_lxcpath() { fi } +get_default_vg() { + LXC_VG=$(grep -v "^#" "$globalconf" 2>/dev/null | grep "[ \t]*lvm_vg[ \t]*=") || true + if [ -n "$LXC_VG" ]; then + echo $LXC_VG | awk -F= '{ print $2 }' + else + echo "lxc" + fi +} + +get_default_zfsroot() { + LXC_ZFSROOT=$(grep -v "^#" "$globalconf" 2>/dev/null | grep "[ \t]*zfsroot[ \t]*=") || true + if [ -n "$LXC_ZFSROOT" ]; then + echo $LXC_ZFSROOT | awk -F= '{ print $2 }' + else + echo "tank/lxc" + fi +} + lxc_path=`get_default_lxcpath` +lxc_vg=`get_default_vg` +lxc_zfsroot=`get_default_zfsroot` From a8428dfa2c6a43ee195f4be3e04a519ca1fc6ec0 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Mon, 29 Apr 2013 22:09:06 +0200 Subject: [PATCH 076/325] introduce lxc_config It's a tiny program (exported through the api) wrapping the util.c helpers for reading /etc/lxc/lxc.conf variables, and replaces the kludgy shell duplication in lxc.functions.in Changelog: Apr 30: address feedback from Dwight (exit error on failure, and use 'lxcpath' as name, not 'default_path'). Signed-off-by: Serge Hallyn Acked-by: Dwight Engen --- src/lxc/Makefile.am | 4 +++- src/lxc/lxc.functions.in | 33 +++----------------------- src/lxc/lxc_config.c | 50 ++++++++++++++++++++++++++++++++++++++++ src/lxc/lxccontainer.c | 10 ++++++++ src/lxc/lxccontainer.h | 2 ++ 5 files changed, 68 insertions(+), 31 deletions(-) create mode 100644 src/lxc/lxc_config.c diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 580b41d88..f392bf549 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -162,7 +162,8 @@ bin_PROGRAMS = \ lxc-unfreeze \ lxc-checkpoint \ lxc-restart \ - lxc-kill + lxc-kill \ + lxc-config pkglibexec_PROGRAMS = \ lxc-init @@ -179,6 +180,7 @@ LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@ @SECCOMP_LIBS@ lxc_attach_SOURCES = lxc_attach.c lxc_cgroup_SOURCES = lxc_cgroup.c lxc_checkpoint_SOURCES = lxc_checkpoint.c +lxc_config_SOURCES = lxc_config.c lxc_console_SOURCES = lxc_console.c lxc_execute_SOURCES = lxc_execute.c lxc_freeze_SOURCES = lxc_freeze.c diff --git a/src/lxc/lxc.functions.in b/src/lxc/lxc.functions.in index 416267f74..ad3d42f1e 100644 --- a/src/lxc/lxc.functions.in +++ b/src/lxc/lxc.functions.in @@ -25,33 +25,6 @@ bindir=@BINDIR@ templatedir=@LXCTEMPLATEDIR@ lxcinitdir=@LXCINITDIR@ -get_default_lxcpath() { - LXC_PATH=$(grep -v "^#" "$globalconf" 2>/dev/null | grep "[ \t]*lxcpath[ \t]*=") || true - if [ -n "$LXC_PATH" ]; then - echo $LXC_PATH | awk -F= '{ print $2 }' - else - echo @LXCPATH@ - fi -} - -get_default_vg() { - LXC_VG=$(grep -v "^#" "$globalconf" 2>/dev/null | grep "[ \t]*lvm_vg[ \t]*=") || true - if [ -n "$LXC_VG" ]; then - echo $LXC_VG | awk -F= '{ print $2 }' - else - echo "lxc" - fi -} - -get_default_zfsroot() { - LXC_ZFSROOT=$(grep -v "^#" "$globalconf" 2>/dev/null | grep "[ \t]*zfsroot[ \t]*=") || true - if [ -n "$LXC_ZFSROOT" ]; then - echo $LXC_ZFSROOT | awk -F= '{ print $2 }' - else - echo "tank/lxc" - fi -} - -lxc_path=`get_default_lxcpath` -lxc_vg=`get_default_vg` -lxc_zfsroot=`get_default_zfsroot` +lxc_path=`lxc-config default_path` +lxc_vg=`lxc-config lvm_vg` +lxc_zfsroot=`lxc-config zfsroot` diff --git a/src/lxc/lxc_config.c b/src/lxc/lxc_config.c new file mode 100644 index 000000000..a454e07bd --- /dev/null +++ b/src/lxc/lxc_config.c @@ -0,0 +1,50 @@ +#include +#include "config.h" +#include "lxccontainer.h" + +struct lxc_config_items { + char *name; + const char *(*fn)(void); +}; + +struct lxc_config_items items[] = +{ + { .name = "lxcpath", .fn = &lxc_get_default_config_path, }, + { .name = "lvm_vg", .fn = &lxc_get_default_lvm_vg, }, + { .name = "zfsroot", .fn = &lxc_get_default_zfs_root, }, + { .name = NULL, }, +}; + +void usage(char *me) +{ + printf("Usage: %s -l: list all available configuration items\n", me); + printf(" %s item: print configuration item\n", me); + exit(1); +} + +void list_config_items(void) +{ + struct lxc_config_items *i; + + for (i = &items[0]; i->name; i++) + printf("%s\n", i->name); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct lxc_config_items *i; + + if (argc < 2) + usage(argv[0]); + if (strcmp(argv[1], "-l") == 0) + list_config_items(); + for (i = &items[0]; i->name; i++) { + if (strcmp(argv[1], i->name) == 0) { + printf("%s\n", i->fn()); + exit(0); + } + } + printf("Unknown configuration item: %s\n", argv[1]); + exit(-1); +} diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 312cc99e4..10f188ea1 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -1009,6 +1009,16 @@ const char *lxc_get_default_config_path(void) return default_lxc_path(); } +const char *lxc_get_default_lvm_vg(void) +{ + return default_lvm_vg(); +} + +const char *lxc_get_default_zfs_root(void) +{ + return default_zfs_root(); +} + const char *lxc_get_version(void) { return lxc_version(); diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index a4be7537c..b6bd97c2e 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -122,6 +122,8 @@ int lxc_container_get(struct lxc_container *c); int lxc_container_put(struct lxc_container *c); int lxc_get_wait_states(const char **states); const char *lxc_get_default_config_path(void); +const char *lxc_get_default_lvm_vg(void); +const char *lxc_get_default_zfs_root(void); const char *lxc_get_version(void); #if 0 From 0fc0d057c34f3ee10eeb87e3f11405aa79c3b4df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=2E=C3=87a=C4=9Flar=20Onur?= Date: Tue, 30 Apr 2013 14:55:03 -0400 Subject: [PATCH 077/325] silence "sh: 1: zfs: not found" errors on systems without ZFS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: S.ÇaÄŸlar Onur Signed-off-by: Serge E. Hallyn --- src/lxc/bdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index a3577a207..1de302f17 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -440,7 +440,7 @@ static int zfs_list_entry(const char *path, char *output) FILE *f; int found=0; - if ((f = popen("zfs list", "r")) == NULL) { + if ((f = popen("zfs list 2> /dev/null", "r")) == NULL) { SYSERROR("popen failed"); return 0; } From ec471210d97ba23b2de618349bdb6dd4145e53e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=2E=C3=87a=C4=9Flar=20Onur?= Date: Tue, 30 Apr 2013 14:55:04 -0400 Subject: [PATCH 078/325] Update .gitignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: S.ÇaÄŸlar Onur Signed-off-by: Serge E. Hallyn --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c614a7539..0cec29a83 100644 --- a/.gitignore +++ b/.gitignore @@ -42,7 +42,9 @@ src/lxc/lxc-cgroup src/lxc/lxc-checkconfig src/lxc/lxc-checkpoint src/lxc/lxc-clone +src/lxc/lxc-clone-sh src/lxc/lxc-console +src/lxc/lxc-config src/lxc/lxc-create src/lxc/lxc-destroy src/lxc/lxc-execute From b164a17f9bfcc3f067dad33d0c38834aa22ca2b1 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 30 Apr 2013 14:20:40 -0500 Subject: [PATCH 079/325] remove lxc-clone-sh Signed-off-by: Serge Hallyn --- .gitignore | 1 - configure.ac | 1 - src/lxc/Makefile.am | 1 - src/lxc/lxc-clone-sh.in | 324 ---------------------------------------- 4 files changed, 327 deletions(-) delete mode 100755 src/lxc/lxc-clone-sh.in diff --git a/.gitignore b/.gitignore index 0cec29a83..a661bccd2 100644 --- a/.gitignore +++ b/.gitignore @@ -42,7 +42,6 @@ src/lxc/lxc-cgroup src/lxc/lxc-checkconfig src/lxc/lxc-checkpoint src/lxc/lxc-clone -src/lxc/lxc-clone-sh src/lxc/lxc-console src/lxc/lxc-config src/lxc/lxc-create diff --git a/configure.ac b/configure.ac index 6b2703876..f27fb87b6 100644 --- a/configure.ac +++ b/configure.ac @@ -382,7 +382,6 @@ AC_CONFIG_FILES([ src/lxc/lxc-checkconfig src/lxc/lxc-version src/lxc/lxc-create - src/lxc/lxc-clone-sh src/lxc/lxc-shutdown src/lxc/lxc-start-ephemeral src/lxc/lxc-destroy diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index f392bf549..4a1906101 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -124,7 +124,6 @@ bin_SCRIPTS = \ lxc-checkconfig \ lxc-version \ lxc-create \ - lxc-clone-sh \ lxc-shutdown \ lxc-destroy diff --git a/src/lxc/lxc-clone-sh.in b/src/lxc/lxc-clone-sh.in deleted file mode 100755 index 4c8acb491..000000000 --- a/src/lxc/lxc-clone-sh.in +++ /dev/null @@ -1,324 +0,0 @@ -#!/bin/sh - -# -# lxc: linux Container library - -# Authors: -# Serge Hallyn -# Daniel Lezcano - -# 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 - -set -e - -usage() { - echo "usage: $(basename $0) -o ORIG_NAME -n NEW_NAME [-s] [-h] [-L FS_SIZE]" >&2 - echo " [-v VG_NAME] [-p LV_PREFIX] [-t FS_TYPE]" >&2 -} - -help() { - usage - echo >&2 - echo "Clone an existing container on the system." >&2 - echo >&2 - echo "Options:" >&2 - echo " -o ORIG_NAME specify the name of the original container" >&2 - echo " -n NEW_NAME specify the name of the new container" >&2 - echo " -s make the new rootfs a snapshot of the original" >&2 - echo " -L FS_SIZE specify the new filesystem size (default: same as original)" >&2 - echo " -v VG_NAME specify the new LVM volume group name (default: lxc)" >&2 - echo " -p LV_PREFIX add a prefix to new LVM logical volume names" >&2 - echo " -t FS_TYPE specify the new filesystem type (default: ext3;" >&2 - echo " only works for non-snapshot LVM)" >&2 -} - -usage_err() { - [ -n "$1" ] && echo "$1" >&2 - usage - exit 1 -} - -optarg_check() { - [ -n "$2" ] || usage_err "option $1 requires an argument" -} - -. @DATADIR@/lxc/lxc.functions -snapshot=no -lxc_size=_unset -lxc_vg=lxc -lxc_lv_prefix="" -fstype=ext3 - -while [ $# -gt 0 ]; do - opt="$1" - shift - case "$opt" in - -h|--help) - help - exit 1 - ;; - -s|--snapshot) - snapshot=yes - snapshot_opt="-s" - ;; - -o|--orig) - optarg_check $opt $1 - lxc_orig=$1 - shift - ;; - -L|--fssize) - optarg_check $opt $1 - lxc_size=$1 - shift - ;; - -t|--fstype) - optarg_check $opt $1 - fstype=$1 - shift - ;; - -v|--vgname) - optarg_check $opt $1 - lxc_vg=$1 - shift - ;; - -n|--name) - optarg_check $opt $1 - lxc_new=$1 - shift - ;; - -p|--lvprefix) - optarg_check $opt $1 - lxc_lv_prefix=$1 - shift - ;; - --) - break - ;; - -?) - usage_err "Unknown option: '$opt'" - ;; - -*) - # split opts -abc into -a -b -c - set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@" - ;; - *) - usage_err - ;; - esac -done - -if [ -z "$lxc_path" ]; then - echo "$(basename $0): no configuration path defined" >&2 - exit 1 -fi - -if [ ! -r $lxc_path ]; then - echo "$(basename $0): configuration path '$lxc_path' not found" >&2 - exit 1 -fi - -if [ -z "$lxc_orig" ]; then - echo "$(basename $0): no original container name specified" >&2 - usage - exit 1 -fi - -if [ -z "$lxc_new" ]; then - echo "$(basename $0): no new container name specified" >&2 - usage - exit 1 -fi - -if [ "$(id -u)" != "0" ]; then - echo "$(basename $0): must be run as root" >&2 - exit 1 -fi - -if [ ! -d "$lxc_path/$lxc_orig" ]; then - echo "$(basename $0): '$lxc_orig' does not exist" >&2 - exit 1 -fi - -if [ -d "$lxc_path/$lxc_new" ]; then - echo "$(basename $0): '$lxc_new' already exists" >&2 - exit 1 -fi - -mounted=0 -frozen=0 -oldroot=`grep lxc.rootfs $lxc_path/$lxc_orig/config | awk -F= '{ print $2 '}` - -cleanup() { - if [ -b $oldroot ]; then - if [ $mounted -eq 1 ]; then - umount $rootfs || true - fi - lvremove -f $rootdev || true - fi - ${bindir}/lxc-destroy -n $lxc_new || true - if [ $frozen -eq 1 ]; then - lxc-unfreeze -n $lxc_orig - fi - echo "$(basename $0): aborted" >&2 - exit 1 -} -trap cleanup HUP INT TERM - -mkdir -p $lxc_path/$lxc_new -hostname=$lxc_new - -echo "Tweaking configuration" -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; } - -if [ -e $lxc_path/$lxc_orig/fstab ];then - cp $lxc_path/$lxc_orig/fstab $lxc_path/$lxc_new/fstab - sed -i "s@$lxc_path/$lxc_orig@$lxc_path/$lxc_new@" $lxc_path/$lxc_new/fstab -fi - -echo "Copying rootfs..." -oldroot=`grep lxc.rootfs $lxc_path/$lxc_orig/config | awk -F'[= \t]+' '{ print $2 }'` -rootfs=`echo $oldroot |sed "s/$lxc_orig/$lxc_new/"` - -container_running=True -lxc-info -n $lxc_orig --state-is RUNNING || container_running=False - -sed -i '/lxc.rootfs/d' $lxc_path/$lxc_new/config -if [ -b $oldroot ]; then - which vgscan >/dev/null 2>&1 || { echo "$(basename $0): lvm is not installed" >&2; false; } - lvdisplay $oldroot > /dev/null 2>&1 || { echo "$(basename $0): non-lvm blockdev cloning is not supported" >&2; false; } - lvm=TRUE - # ok, create a snapshot of the lvm device - if [ $container_running = "True" ]; then - lxc-freeze -n $lxc_orig - frozen=1 - fi - if [ $lxc_size = "_unset" ]; then - lxc_size=`lvdisplay $oldroot | grep Size | awk '{ print $3 $4 }'` - fi - newlv="${lxc_lv_prefix}${lxc_new}_snapshot" - lvcreate -s -L $lxc_size -n $newlv $oldroot - which xfs_admin > /dev/null 2>&1 && { - # change filesystem UUID if it is an xfs filesystem - xfs_admin -u /dev/$lxc_vg/$newlv && xfs_admin -U generate /dev/$lxc_vg/$newlv - } - - if [ $container_running = "True" ]; then - lxc-unfreeze -n $lxc_orig - frozen=0 - fi - if [ $snapshot = "no" ]; then - #mount snapshot - mkdir -p ${rootfs}_snapshot - mount /dev/$lxc_vg/${lxc_lv_prefix}${lxc_new}_snapshot ${rootfs}_snapshot || { echo "$(basename $0): failed to mount new rootfs_snapshot" >&2; false; } - #create a new lv - lvcreate -L $lxc_size $lxc_vg -n ${lxc_lv_prefix}$lxc_new - echo "lxc.rootfs = /dev/$lxc_vg/${lxc_lv_prefix}$lxc_new" >> $lxc_path/$lxc_new/config - # and mount it so we can tweak it - mkdir -p $rootfs - mkfs -t $fstype /dev/$lxc_vg/${lxc_lv_prefix}$lxc_new - mount /dev/$lxc_vg/${lxc_lv_prefix}$lxc_new $rootfs || { echo "$(basename $0): failed to mount new rootfs" >&2; false; } - mounted=1 - rsync -Hax ${rootfs}_snapshot/ ${rootfs}/ || { echo "$(basename $0): copying data to new lv failed" >&2; false; } - umount ${rootfs}_snapshot - rmdir ${rootfs}_snapshot - lvremove -f $lxc_vg/${lxc_lv_prefix}${lxc_new}_snapshot - else - lvrename $lxc_vg/${lxc_lv_prefix}${lxc_new}_snapshot $lxc_vg/${lxc_lv_prefix}$lxc_new - echo "lxc.rootfs = /dev/$lxc_vg/${lxc_lv_prefix}$lxc_new" >> $lxc_path/$lxc_new/config - # and mount it so we can tweak it - mkdir -p $rootfs - mount /dev/$lxc_vg/${lxc_lv_prefix}$lxc_new $rootfs || { echo "$(basename $0): failed to mount new rootfs" >&2; false; } - mounted=1 - fi - -elif which btrfs >/dev/null 2>&1 && btrfs subvolume list $oldroot >/dev/null 2>&1; then - # if oldroot is a btrfs subvolume, assume they want a snapshot - btrfs subvolume snapshot "$oldroot" "$rootfs" 2>&1 || { echo "$(basename $0): btrfs snapshot failed" >&2; false; } - echo "lxc.rootfs = $rootfs" >> "$lxc_path/$lxc_new/config" -elif [ -d $lxc_path/$lxc_orig/delta0 ]; then # this is a quasi-ephemeral container - if [ $container_running = "True" ]; then - echo "$(basename $0): container $lxc_orig is running." >&2 - cleanup - fi - rsync -Hax $lxc_path/$lxc_orig/delta0 $lxc_path/$lxc_new/ - touch $lxc_path/$lxc_new/configured - cp -f $lxc_path/$lxc_orig/pre-mount $lxc_path/$lxc_new/ - sed -i "s@$lxc_path/$lxc_orig@$lxc_path/$lxc_new@g" $lxc_path/$lxc_new/config - sed -i "s@$lxc_path/$lxc_orig@$lxc_path/$lxc_new@g" $lxc_path/$lxc_new/pre-mount - sed -i "s@LXC_NAME=\"$lxc_orig@LXC_NAME=\"$lxc_new@" $lxc_path/$lxc_new/pre-mount - # lxc-start-ephemeral will have updated /etc/hostname and such under the - # delta0, so just mounting the delta should suffice. - mkdir -p $rootfs - mount --bind $lxc_path/$lxc_new/delta0 $rootfs - mounted=1 - echo "lxc.rootfs = $rootfs" >> "$lxc_path/$lxc_new/config" -else - if [ $snapshot = "yes" ]; then - echo "$(basename $0): cannot snapshot a directory" >&2 - cleanup - fi - if [ $container_running = "True" ]; then - lxc-freeze -n $lxc_orig - frozen=1 - fi - mkdir -p $rootfs/ - rsync -Hax $oldroot/ $rootfs/ - echo "lxc.rootfs = $rootfs" >> $lxc_path/$lxc_new/config - if [ $container_running = "True" ]; then - lxc-unfreeze -n $lxc_orig - frozen=0 - fi -fi - -echo "Updating rootfs..." - -# so you can 'ssh $hostname.' or 'ssh $hostname.local' -if [ -f $rootfs/etc/dhcp/dhclient.conf ] && ! grep -q "^send host-name.*hostname" $rootfs/etc/dhcp/dhclient.conf; then - sed -i "s/send host-name.*$/send host-name \"$hostname\";/" $rootfs/etc/dhcp/dhclient.conf -fi - -c=$lxc_path/$lxc_new/config -# change hwaddrs -mv ${c} ${c}.old -( -while read line; do - if echo $line | grep -q -w '^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 - -# set the hostname -cat < $rootfs/etc/hostname -$hostname -EOF -# set minimal hosts -cat < $rootfs/etc/hosts -127.0.0.1 localhost $hostname -EOF - -# if this was a block device, then umount it now -if [ $mounted -eq 1 ]; then - umount $rootfs -fi - -echo "'$lxc_new' created" From 385e7a431a1865017211478741408d505396f9a7 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 30 Apr 2013 14:23:08 -0500 Subject: [PATCH 080/325] lxc.functions.in: use the right parameter to lxc-config to get lxcpath MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: S.ÇaÄŸlar Onur Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/lxc.functions.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/lxc.functions.in b/src/lxc/lxc.functions.in index ad3d42f1e..8c25bd5cf 100644 --- a/src/lxc/lxc.functions.in +++ b/src/lxc/lxc.functions.in @@ -25,6 +25,6 @@ bindir=@BINDIR@ templatedir=@LXCTEMPLATEDIR@ lxcinitdir=@LXCINITDIR@ -lxc_path=`lxc-config default_path` +lxc_path=`lxc-config lxcpath lxc_vg=`lxc-config lvm_vg` lxc_zfsroot=`lxc-config zfsroot` From b338c81b9f0130106eee4b2ff70959c2e62a1fac Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 30 Apr 2013 14:45:32 -0500 Subject: [PATCH 081/325] lxc.functions.in: add missing backquote MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported by both Dwight and S.ÇaÄŸlar - thanks. Signed-off-by: Serge Hallyn --- src/lxc/lxc.functions.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/lxc.functions.in b/src/lxc/lxc.functions.in index 8c25bd5cf..de268dfa4 100644 --- a/src/lxc/lxc.functions.in +++ b/src/lxc/lxc.functions.in @@ -25,6 +25,6 @@ bindir=@BINDIR@ templatedir=@LXCTEMPLATEDIR@ lxcinitdir=@LXCINITDIR@ -lxc_path=`lxc-config lxcpath +lxc_path=`lxc-config lxcpath` lxc_vg=`lxc-config lvm_vg` lxc_zfsroot=`lxc-config zfsroot` From ee25a44fd389ed450e3d7ef9513eec19668f2de7 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 30 Apr 2013 16:33:18 -0400 Subject: [PATCH 082/325] log.c: always use dir when lxcpath is not default Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/log.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/lxc/log.c b/src/lxc/log.c index 61280bc0b..8d87a5180 100644 --- a/src/lxc/log.c +++ b/src/lxc/log.c @@ -187,31 +187,41 @@ static int log_open(const char *name) static char *build_log_path(const char *name, const char *lxcpath) { char *p; - int len, ret; + int len, ret, use_dir; + +#if USE_CONFIGPATH_LOGS + use_dir = 1; +#else + use_dir = 0; +#endif /* - * If USE_CONFIGPATH_LOGS is true the resulting path will be: + * If USE_CONFIGPATH_LOGS is true or lxcpath is given, the resulting + * path will be: * '$logpath' + '/' + '$name' + '/' + '$name' + '.log' + '\0' * * If USE_CONFIGPATH_LOGS is false the resulting path will be: * '$logpath' + '/' + '$name' + '.log' + '\0' */ len = strlen(name) + 6; /* 6 == '/' + '.log' + '\0' */ - if (!lxcpath) + if (lxcpath) + use_dir = 1; + else lxcpath = LOGPATH; -#if USE_CONFIGPATH_LOGS - len += strlen(lxcpath) + 1 + strlen(name) + 1; /* add "/$container_name/" */ -#else - len += strlen(lxcpath) + 1; -#endif + + if (use_dir) + len += strlen(lxcpath) + 1 + strlen(name) + 1; /* add "/$container_name/" */ + else + len += strlen(lxcpath) + 1; p = malloc(len); if (!p) return p; -#if USE_CONFIGPATH_LOGS - ret = snprintf(p, len, "%s/%s/%s.log", lxcpath, name, name); -#else - ret = snprintf(p, len, "%s/%s.log", lxcpath, name); -#endif + + if (use_dir) + ret = snprintf(p, len, "%s/%s/%s.log", lxcpath, name, name); + else + ret = snprintf(p, len, "%s/%s.log", lxcpath, name); + if (ret < 0 || ret >= len) { free(p); return NULL; From eee3ba81c88e64b8a732694fc4843a39d5bde491 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Wed, 1 May 2013 08:33:12 -0500 Subject: [PATCH 083/325] templates: deny writes to host's clock (v2) Don't allow write to /dev/rtc0, and remove sys_time. Thanks, Christoph. v2: drop sys_time, sys_module, mac_admin and mac_override in all templates. Reported-by: Christoph Mitasch Signed-off-by: Serge Hallyn --- templates/lxc-alpine.in | 3 ++- templates/lxc-altlinux.in | 1 + templates/lxc-archlinux.in | 2 +- templates/lxc-busybox.in | 1 + templates/lxc-debian.in | 3 ++- templates/lxc-fedora.in | 3 ++- templates/lxc-opensuse.in | 4 ++-- templates/lxc-sshd.in | 1 + templates/lxc-ubuntu-cloud.in | 4 ++-- templates/lxc-ubuntu.in | 4 ++-- 10 files changed, 16 insertions(+), 10 deletions(-) diff --git a/templates/lxc-alpine.in b/templates/lxc-alpine.in index 962d274e8..98347ed67 100644 --- a/templates/lxc-alpine.in +++ b/templates/lxc-alpine.in @@ -109,6 +109,7 @@ EOF lxc.tty = 4 lxc.pts = 1024 lxc.utsname = $hostname +lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined @@ -129,7 +130,7 @@ lxc.cgroup.devices.allow = c 1:8 rwm lxc.cgroup.devices.allow = c 136:* rwm lxc.cgroup.devices.allow = c 5:2 rwm # rtc -lxc.cgroup.devices.allow = c 254:0 rwm +lxc.cgroup.devices.allow = c 254:0 rm # mounts point lxc.mount.entry=proc proc proc nodev,noexec,nosuid 0 0 diff --git a/templates/lxc-altlinux.in b/templates/lxc-altlinux.in index da66ae78c..cce214c0f 100644 --- a/templates/lxc-altlinux.in +++ b/templates/lxc-altlinux.in @@ -243,6 +243,7 @@ lxc.utsname = $name lxc.tty = 4 lxc.pts = 1024 lxc.mount = $config_path/fstab +lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined diff --git a/templates/lxc-archlinux.in b/templates/lxc-archlinux.in index ed5fb46ed..98d54242a 100644 --- a/templates/lxc-archlinux.in +++ b/templates/lxc-archlinux.in @@ -127,7 +127,7 @@ lxc.tty=1 lxc.pts=1024 lxc.rootfs=${rootfs_path} lxc.mount=${config_path}/fstab -lxc.cap.drop=mknod sys_module mac_admin mac_override +lxc.cap.drop=mknod sys_module mac_admin mac_override sys_time lxc.kmsg=0 lxc.stopsignal=SIGRTMIN+4 #networking diff --git a/templates/lxc-busybox.in b/templates/lxc-busybox.in index 2ca2bfd70..81e9566c5 100644 --- a/templates/lxc-busybox.in +++ b/templates/lxc-busybox.in @@ -261,6 +261,7 @@ cat <> $path/config lxc.utsname = $name lxc.tty = 1 lxc.pts = 1 +lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined diff --git a/templates/lxc-debian.in b/templates/lxc-debian.in index 568bc2cfb..d4ea3de59 100644 --- a/templates/lxc-debian.in +++ b/templates/lxc-debian.in @@ -218,6 +218,7 @@ copy_configuration() lxc.tty = 4 lxc.pts = 1024 lxc.utsname = $hostname +lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined @@ -237,7 +238,7 @@ lxc.cgroup.devices.allow = c 1:8 rwm lxc.cgroup.devices.allow = c 136:* rwm lxc.cgroup.devices.allow = c 5:2 rwm # rtc -lxc.cgroup.devices.allow = c 254:0 rwm +lxc.cgroup.devices.allow = c 254:0 rm # mounts point lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0 diff --git a/templates/lxc-fedora.in b/templates/lxc-fedora.in index 6f31e997e..59f453be9 100644 --- a/templates/lxc-fedora.in +++ b/templates/lxc-fedora.in @@ -252,6 +252,7 @@ lxc.utsname = $name lxc.tty = 4 lxc.pts = 1024 lxc.mount = $config_path/fstab +lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined @@ -272,7 +273,7 @@ lxc.cgroup.devices.allow = c 1:8 rwm lxc.cgroup.devices.allow = c 136:* rwm lxc.cgroup.devices.allow = c 5:2 rwm # rtc -lxc.cgroup.devices.allow = c 254:0 rwm +lxc.cgroup.devices.allow = c 254:0 rm EOF cat < $config_path/fstab diff --git a/templates/lxc-opensuse.in b/templates/lxc-opensuse.in index af92cf5d1..7d3dd1cad 100644 --- a/templates/lxc-opensuse.in +++ b/templates/lxc-opensuse.in @@ -275,7 +275,7 @@ lxc.autodev=1 lxc.tty = 4 lxc.pts = 1024 lxc.mount = $path/fstab -lxc.cap.drop = sys_module mac_admin mac_override mknod +lxc.cap.drop = sys_module mac_admin mac_override mknod sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined @@ -295,7 +295,7 @@ lxc.cgroup.devices.allow = c 1:8 rwm lxc.cgroup.devices.allow = c 136:* rwm lxc.cgroup.devices.allow = c 5:2 rwm # rtc -lxc.cgroup.devices.allow = c 254:0 rwm +lxc.cgroup.devices.allow = c 254:0 rm EOF cat < $path/fstab diff --git a/templates/lxc-sshd.in b/templates/lxc-sshd.in index b704723b4..2927c9295 100644 --- a/templates/lxc-sshd.in +++ b/templates/lxc-sshd.in @@ -112,6 +112,7 @@ copy_configuration() cat <> $path/config lxc.utsname = $name lxc.pts = 1024 +lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined diff --git a/templates/lxc-ubuntu-cloud.in b/templates/lxc-ubuntu-cloud.in index d60f2c74f..9f5cf1993 100644 --- a/templates/lxc-ubuntu-cloud.in +++ b/templates/lxc-ubuntu-cloud.in @@ -55,7 +55,7 @@ lxc.pts = 1024 lxc.utsname = $name lxc.arch = $arch -lxc.cap.drop = sys_module mac_admin mac_override +lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined @@ -76,7 +76,7 @@ lxc.cgroup.devices.allow = c 1:8 rwm lxc.cgroup.devices.allow = c 136:* rwm lxc.cgroup.devices.allow = c 5:2 rwm # rtc -lxc.cgroup.devices.allow = c 254:0 rwm +lxc.cgroup.devices.allow = c 254:0 rm # fuse lxc.cgroup.devices.allow = c 10:229 rwm # tun diff --git a/templates/lxc-ubuntu.in b/templates/lxc-ubuntu.in index 7100acc87..37a1b9c13 100644 --- a/templates/lxc-ubuntu.in +++ b/templates/lxc-ubuntu.in @@ -378,7 +378,7 @@ lxc.pts = 1024 lxc.utsname = $name lxc.arch = $arch -lxc.cap.drop = sys_module mac_admin mac_override +lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined @@ -399,7 +399,7 @@ lxc.cgroup.devices.allow = c 1:8 rwm lxc.cgroup.devices.allow = c 136:* rwm lxc.cgroup.devices.allow = c 5:2 rwm # rtc -lxc.cgroup.devices.allow = c 254:0 rwm +lxc.cgroup.devices.allow = c 254:0 rm # fuse lxc.cgroup.devices.allow = c 10:229 rwm # tun From e0b0b533feed683ce12c94e11174019a5dac64fc Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 1 May 2013 19:07:16 -0400 Subject: [PATCH 084/325] allow lxc-init to log when rootfs not given On Mon, 29 Apr 2013 14:44:47 -0500 Serge Hallyn wrote: > Quoting Dwight Engen (dwight.engen@oracle.com): > > So I did this, only to realize that lxc-init is passing "none" for > > the file anyway, so it currently doesn't intend to log. This makes > > me think that passing NULL for lxcpath is the right thing to do in > > this patch. If you want me to make it so lxc-init can log, I can do > > that but I think it should be in a different change :) > > That actually would be very useful, but as you say that's a different > feature - thanks. ... and here is said change. Signed-off-by: Serge Hallyn --- src/lxc/execute.c | 27 +++++++++++++++--- src/lxc/log.c | 3 ++ src/lxc/lxc_init.c | 68 +++++++++++++++++++++++++++++++++------------- 3 files changed, 75 insertions(+), 23 deletions(-) diff --git a/src/lxc/execute.c b/src/lxc/execute.c index c1f6526d8..d93e8e17c 100644 --- a/src/lxc/execute.c +++ b/src/lxc/execute.c @@ -27,6 +27,7 @@ #include #include +#include "conf.h" #include "log.h" #include "start.h" @@ -85,23 +86,37 @@ static int execute_start(struct lxc_handler *handler, void* data) int j, i = 0; struct execute_args *my_args = data; char **argv; - int argc = 0; + int argc = 0, argc_add; char *initpath; while (my_args->argv[argc++]); - argv = malloc((argc + my_args->quiet ? 5 : 4) * sizeof(*argv)); + argc_add = 4; + if (my_args->quiet) + argc_add++; + if (!handler->conf->rootfs.path) + argc_add+=6; + + argv = malloc((argc + argc_add) * sizeof(*argv)); if (!argv) - return 1; + goto out1; initpath = choose_init(); if (!initpath) { ERROR("Failed to find an lxc-init"); - return 1; + goto out2; } argv[i++] = initpath; if (my_args->quiet) argv[i++] = "--quiet"; + if (!handler->conf->rootfs.path) { + argv[i++] = "--name"; + argv[i++] = (char *)handler->name; + argv[i++] = "--lxcpath"; + argv[i++] = (char *)handler->lxcpath; + argv[i++] = "--logpriority"; + argv[i++] = (char *)lxc_log_priority_to_string(lxc_log_get_level()); + } argv[i++] = "--"; for (j = 0; j < argc; j++) argv[i++] = my_args->argv[j]; @@ -111,6 +126,10 @@ static int execute_start(struct lxc_handler *handler, void* data) execvp(argv[0], argv); SYSERROR("failed to exec %s", argv[0]); + free(initpath); +out2: + free(argv); +out1: return 1; } diff --git a/src/lxc/log.c b/src/lxc/log.c index 8d87a5180..d49a54458 100644 --- a/src/lxc/log.c +++ b/src/lxc/log.c @@ -318,6 +318,9 @@ extern int lxc_log_init(const char *name, const char *file, } else { ret = -1; + if (!lxcpath) + lxcpath = LOGPATH; + /* try LOGPATH if lxcpath is the default */ if (strcmp(lxcpath, default_lxc_path()) == 0) ret = _lxc_log_set_file(name, NULL, 0); diff --git a/src/lxc/lxc_init.c b/src/lxc/lxc_init.c index 663875b37..f772f0dec 100644 --- a/src/lxc/lxc_init.c +++ b/src/lxc/lxc_init.c @@ -43,7 +43,10 @@ lxc_log_define(lxc_init, lxc); static int quiet; static struct option options[] = { - { "quiet", no_argument, &quiet, 1 }, + { "name", required_argument, NULL, 'n' }, + { "logpriority", required_argument, NULL, 'l' }, + { "quiet", no_argument, NULL, 'q' }, + { "lxcpath", required_argument, NULL, 'P' }, { 0, 0, 0, 0 }, }; @@ -55,39 +58,66 @@ static void interrupt_handler(int sig) was_interrupted = sig; } +static void usage(void) { + fprintf(stderr, "Usage: lxc-init [OPTION]...\n\n" + "Common options :\n" + " -n, --name=NAME NAME for name of the container\n" + " -l, --logpriority=LEVEL Set log priority to LEVEL\n" + " -q, --quiet Don't produce any output\n" + " -P, --lxcpath=PATH Use specified container path\n" + " -?, --help Give this help list\n" + "\n" + "Mandatory or optional arguments to long options are also mandatory or optional\n" + "for any corresponding short options.\n" + "\n" + "NOTE: lxc-init is intended for use by lxc internally\n" + " and does not need to be run by hand\n\n"); +} + int main(int argc, char *argv[]) { pid_t pid; - int nbargs = 0; - int err = -1; + int err; char **aargv; sigset_t mask, omask; int i, have_status = 0, shutdown = 0; + int opt; + char *lxcpath = NULL, *name = NULL, *logpriority = NULL; - while (1) { - int ret = getopt_long_only(argc, argv, "", options, NULL); - if (ret == -1) { + while ((opt = getopt_long(argc, argv, "n:l:qP:", options, NULL)) != -1) { + switch(opt) { + case 'n': + name = optarg; break; + case 'l': + logpriority = optarg; + break; + case 'q': + quiet = 1; + break; + case 'P': + lxcpath = optarg; + break; + default: /* '?' */ + usage(); + exit(EXIT_FAILURE); } - if (ret == '?') - exit(err); - - nbargs++; } if (lxc_caps_init()) - exit(err); + exit(EXIT_FAILURE); - if (lxc_log_init(NULL, "none", 0, basename(argv[0]), quiet, NULL)) - exit(err); + err = lxc_log_init(name, name ? NULL : "none", logpriority, + basename(argv[0]), quiet, lxcpath); + if (err < 0) + exit(EXIT_FAILURE); if (!argv[optind]) { ERROR("missing command to launch"); - exit(err); + exit(EXIT_FAILURE); } aargv = &argv[optind]; - argc -= nbargs; /* * mask all the signals so we are safe to install a @@ -125,15 +155,15 @@ int main(int argc, char *argv[]) } if (lxc_setup_fs()) - exit(err); + exit(EXIT_FAILURE); if (lxc_caps_reset()) - exit(err); + exit(EXIT_FAILURE); pid = fork(); if (pid < 0) - exit(err); + exit(EXIT_FAILURE); if (!pid) { @@ -158,7 +188,7 @@ int main(int argc, char *argv[]) close(fileno(stdin)); close(fileno(stdout)); - err = 0; + err = EXIT_SUCCESS; for (;;) { int status; pid_t waited_pid; From 375c2258b24b233832c9ec43ab9c7b3f5dce25fb Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Wed, 1 May 2013 23:37:05 -0500 Subject: [PATCH 085/325] clone: a few fixes clean up error case in clone, which in particular could cause double lxc_container_put(c2) for overlayfs, handle (with error message) all bdev types. Signed-off-by: Serge Hallyn --- src/lxc/bdev.c | 11 ++++++----- src/lxc/lxccontainer.c | 25 +++++++------------------ 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index 1de302f17..940891874 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -1186,11 +1186,6 @@ static int overlayfs_clonepaths(struct bdev *orig, struct bdev *new, const char free(delta); if (ret < 0 || ret >= len) return -ENOMEM; - } else if (strcmp(orig->type, "lvm") == 0) { - ERROR("overlayfs clone of lvm container is not yet supported"); - // Note, supporting this will require overlayfs_mount supporting - // mounting of the underlay. No big deal, just needs to be done. - return -1; } else if (strcmp(orig->type, "overlayfs") == 0) { // What exactly do we want to do here? // I think we want to use the original lowerdir, with a @@ -1228,6 +1223,12 @@ static int overlayfs_clonepaths(struct bdev *orig, struct bdev *new, const char free(ndelta); if (ret < 0 || ret >= len) return -ENOMEM; + } else { + ERROR("overlayfs clone of %s container is not yet supported", + orig->type); + // Note, supporting this will require overlayfs_mount supporting + // mounting of the underlay. No big deal, just needs to be done. + return -1; } return 0; diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 10f188ea1..452323ca1 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -1477,7 +1477,7 @@ struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname, } c2 = lxc_container_new(n, l); - if (!c) { + if (!c2) { ERROR("clone: failed to create new container (%s %s)", n, l); goto out; } @@ -1487,16 +1487,12 @@ struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname, ret = copyhooks(c, c2); if (ret < 0) { ERROR("error copying hooks"); - c2->destroy(c2); - lxc_container_put(c2); goto out; } } if (copy_fstab(c, c2) < 0) { ERROR("error copying fstab"); - c2->destroy(c2); - lxc_container_put(c2); goto out; } @@ -1506,23 +1502,14 @@ struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname, // copy/snapshot rootfs's ret = copy_storage(c, c2, bdevtype, flags, bdevdata, newsize); - if (ret < 0) { - c2->destroy(c2); - lxc_container_put(c2); + if (ret < 0) goto out; - } - if (!c2->save_config(c2, NULL)) { - c2->destroy(c2); - lxc_container_put(c2); + if (!c2->save_config(c2, NULL)) goto out; - } - if (clone_update_rootfs(c2, flags) < 0) { - //c2->destroy(c2); - lxc_container_put(c2); + if (clone_update_rootfs(c2, flags) < 0) goto out; - } // TODO: update c's lxc.snapshot = count lxcunlock(c->privlock); @@ -1530,8 +1517,10 @@ struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname, out: lxcunlock(c->privlock); - if (c2) + if (c2) { + c2->destroy(c2); lxc_container_put(c2); + } return NULL; } From b85ab7989ebe24629267048cb269b278eeb50490 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Thu, 2 May 2013 16:28:10 -0500 Subject: [PATCH 086/325] ubuntu templates: add comments to show how to enable nesting Signed-off-by: Serge Hallyn --- templates/lxc-ubuntu-cloud.in | 3 +++ templates/lxc-ubuntu.in | 3 +++ 2 files changed, 6 insertions(+) diff --git a/templates/lxc-ubuntu-cloud.in b/templates/lxc-ubuntu-cloud.in index 9f5cf1993..7a56398d0 100644 --- a/templates/lxc-ubuntu-cloud.in +++ b/templates/lxc-ubuntu-cloud.in @@ -59,6 +59,9 @@ lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined +# To support container nesting on an Ubuntu host, uncomment next two lines: +#lxc.aa_profile = lxc-container-default-with-nesting +#lxc.hook.mount = /usr/share/lxc/hooks/mountcgroups lxc.cgroup.devices.deny = a # Allow any mknod (but not using the node) diff --git a/templates/lxc-ubuntu.in b/templates/lxc-ubuntu.in index 37a1b9c13..02ffa199d 100644 --- a/templates/lxc-ubuntu.in +++ b/templates/lxc-ubuntu.in @@ -382,6 +382,9 @@ lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined +# To support container nesting on an Ubuntu host, uncomment next two lines: +#lxc.aa_profile = lxc-container-default-with-nesting +#lxc.hook.mount = /usr/share/lxc/hooks/mountcgroups lxc.cgroup.devices.deny = a # Allow any mknod (but not using the node) From fc7e88640cbdb402aaa048dd74829c8d09dda850 Mon Sep 17 00:00:00 2001 From: Weng Meiling Date: Fri, 3 May 2013 11:02:40 +0800 Subject: [PATCH 087/325] add free conf->rcfile in lxc_conf_free when releasing the conf, add free conf->rcfile which is from malloc Signed-off-by: Weng Meiling 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 27b972755..125e8a6d2 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -3115,6 +3115,8 @@ void lxc_conf_free(struct lxc_conf *conf) free(conf->ttydir); if (conf->fstab) free(conf->fstab); + if (conf->rcfile) + free(conf->rcfile); lxc_clear_config_network(conf); #if HAVE_APPARMOR if (conf->aa_profile) From 2d4bcb96155c0e4a5d2734017f889b993144e876 Mon Sep 17 00:00:00 2001 From: Weng Meiling Date: Fri, 3 May 2013 11:02:48 +0800 Subject: [PATCH 088/325] lxc_start: free the conf if starting the container fails When running lxc-start command with valgrind, it reports a memory leak error. When lxc-start command fails, the conf which is from malloc has not been released. This patch fix the problem. Signed-off-by: Weng Meiling Signed-off-by: Serge Hallyn --- src/lxc/lxc_start.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/lxc/lxc_start.c b/src/lxc/lxc_start.c index d923a7eb5..12aacb8cc 100644 --- a/src/lxc/lxc_start.c +++ b/src/lxc/lxc_start.c @@ -196,25 +196,25 @@ int main(int argc, char *argv[]) if (rcfile && lxc_config_read(rcfile, conf)) { ERROR("failed to read configuration file"); - return err; + goto out; } if (lxc_config_define_load(&defines, conf)) - return err; + goto out; if (!rcfile && !strcmp("/sbin/init", args[0])) { ERROR("no configuration file for '/sbin/init' (may crash the host)"); - return err; + goto out; } if (ensure_path(&conf->console.path, my_args.console) < 0) { ERROR("failed to ensure console path '%s'", my_args.console); - return err; + goto out; } if (ensure_path(&conf->console.log_path, my_args.console_log) < 0) { ERROR("failed to ensure console log '%s'", my_args.console_log); - return err; + goto out; } if (my_args.pidfile != NULL) { @@ -222,7 +222,7 @@ int main(int argc, char *argv[]) if (pid_fp == NULL) { SYSERROR("failed to create pidfile '%s' for '%s'", my_args.pidfile, my_args.name); - return err; + goto out; } } @@ -232,19 +232,19 @@ int main(int argc, char *argv[]) if (!lxc_caps_check()) { ERROR("Not running with sufficient privilege"); - return err; + goto out; } if (daemon(0, 0)) { SYSERROR("failed to daemonize '%s'", my_args.name); - return err; + goto out; } } if (pid_fp != NULL) { if (fprintf(pid_fp, "%d\n", getpid()) < 0) { SYSERROR("failed to write '%s'", my_args.pidfile); - return err; + goto out; } fclose(pid_fp); } @@ -267,7 +267,8 @@ int main(int argc, char *argv[]) if (my_args.pidfile) unlink(my_args.pidfile); - +out: + lxc_conf_free(conf); return err; } From a2eea3c1974d70bdef74a0af6a14ca3a6fa41704 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Fri, 3 May 2013 11:29:39 -0400 Subject: [PATCH 089/325] coverity: ensure string is null terminated, return in error case Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/lxccontainer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 452323ca1..73c347d46 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -1160,10 +1160,11 @@ static int update_name_and_paths(const char *path, struct lxc_container *oldc, SYSERROR("rewinding old config"); return -1; } - contents = malloc(flen); + contents = malloc(flen+1); if (!contents) { SYSERROR("out of memory"); fclose(f); + return -1; } if (fread(contents, 1, flen, f) != flen) { free(contents); @@ -1171,6 +1172,7 @@ static int update_name_and_paths(const char *path, struct lxc_container *oldc, SYSERROR("reading old config"); return -1; } + contents[flen] = '\0'; if (fclose(f) < 0) { free(contents); SYSERROR("closing old config"); From 8950ee8ebfc9a7f34003f6892b5a7da6aef9fff9 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Fri, 3 May 2013 12:04:01 -0400 Subject: [PATCH 090/325] coverity: fix leak in error case Since lxc_execute() is available through the library and is exposed via the API we cannot be sure the caller will immediately exit, so we should take care to free the allocated memory. Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/execute.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lxc/execute.c b/src/lxc/execute.c index d93e8e17c..9bf33cae6 100644 --- a/src/lxc/execute.c +++ b/src/lxc/execute.c @@ -55,7 +55,7 @@ static char *choose_init(void) ret = snprintf(retv, PATH_MAX, LXCINITDIR "/lxc/lxc-init"); if (ret < 0 || ret >= PATH_MAX) { ERROR("pathname too long"); - return NULL; + goto out1; } ret = stat(retv, &mystat); @@ -65,7 +65,7 @@ static char *choose_init(void) ret = snprintf(retv, PATH_MAX, "/usr/lib/lxc/lxc-init"); if (ret < 0 || ret >= PATH_MAX) { ERROR("pathname too long"); - return NULL; + goto out1; } ret = stat(retv, &mystat); if (ret == 0) @@ -73,11 +73,13 @@ static char *choose_init(void) ret = snprintf(retv, PATH_MAX, "/sbin/lxc-init"); if (ret < 0 || ret >= PATH_MAX) { ERROR("pathname too long"); - return NULL; + goto out1; } ret = stat(retv, &mystat); if (ret == 0) return retv; +out1: + free(retv); return NULL; } From bec695f3ec43972ad38f06f92ff2db03d8405562 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Fri, 3 May 2013 12:04:07 -0400 Subject: [PATCH 091/325] coverity: fix leak when ipv6 gw is auto Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/confile.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lxc/confile.c b/src/lxc/confile.c index d0a404f4a..fbae861bf 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -782,12 +782,6 @@ static int config_network_ipv6_gateway(const char *key, const char *value, if (!netdev) return -1; - gw = malloc(sizeof(*gw)); - if (!gw) { - SYSERROR("failed to allocate ipv6 gateway address"); - return -1; - } - if (!value) { ERROR("no ipv6 gateway address specified"); return -1; @@ -797,6 +791,12 @@ static int config_network_ipv6_gateway(const char *key, const char *value, netdev->ipv6_gateway = NULL; netdev->ipv6_gateway_auto = true; } else { + gw = malloc(sizeof(*gw)); + if (!gw) { + SYSERROR("failed to allocate ipv6 gateway address"); + return -1; + } + if (!inet_pton(AF_INET6, value, gw)) { SYSERROR("invalid ipv6 gateway address: %s", value); free(gw); From 8fb86a37daecd05e9ef7f291dd4762be881f88e4 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 3 May 2013 12:28:06 -0500 Subject: [PATCH 092/325] confile.c:config_network_ipv6_gateway: only define gw in needed scope 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 fbae861bf..aec817769 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -776,7 +776,6 @@ static int config_network_ipv6_gateway(const char *key, const char *value, struct lxc_conf *lxc_conf) { struct lxc_netdev *netdev; - struct in6_addr *gw; netdev = network_netdev(key, value, &lxc_conf->network); if (!netdev) @@ -791,6 +790,8 @@ static int config_network_ipv6_gateway(const char *key, const char *value, netdev->ipv6_gateway = NULL; netdev->ipv6_gateway_auto = true; } else { + struct in6_addr *gw; + gw = malloc(sizeof(*gw)); if (!gw) { SYSERROR("failed to allocate ipv6 gateway address"); From a747894428ea38c4a908acacb610fc3de714e0c0 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Fri, 3 May 2013 13:41:40 -0400 Subject: [PATCH 093/325] coverity: ftell returns a signed value The check for flen < 0 could never have been true since flen was declared to be size_t (unsigned). Declare flen to be long since that is what ftell returns. Signed-off-by: Dwight Engen 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 73c347d46..04a920826 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -1133,7 +1133,7 @@ static int update_name_and_paths(const char *path, struct lxc_container *oldc, const char *newname, const char *newpath) { FILE *f; - size_t flen; + long flen; char *contents; const char *p0, *p1, *p2, *end; const char *oldpath = oldc->get_config_path(oldc); From 3c73b55472c096f06fd037c3c0af011be62a432b Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 3 May 2013 16:36:08 -0500 Subject: [PATCH 094/325] remove leftover debug cruft (thanks, Dwight) Signed-off-by: Serge Hallyn --- src/lxc/bdev.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index 940891874..35351a9ae 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -964,11 +964,6 @@ static int btrfs_subvolume_create(const char *path) args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0; ret = ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args); INFO("btrfs: snapshot create ioctl returned %d", ret); -{ - FILE *f = fopen("/tmp/a", "a"); - fprintf(f, "ioctl returned %d\n", ret); - fclose(f); -} free(newfull); close(fd); From a9bafa108521ac785e846f2ace105c327371c106 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Fri, 3 May 2013 16:50:20 -0400 Subject: [PATCH 095/325] coverity: fix dereference before NULL check also fixed some error strings while here Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/tests/cgpath.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/tests/cgpath.c b/src/tests/cgpath.c index de2d13bb3..d8c36248e 100644 --- a/src/tests/cgpath.c +++ b/src/tests/cgpath.c @@ -51,8 +51,7 @@ int main() printf("Basic cgroup path tests...\n"); path = lxc_cgroup_path_create(NULL, MYNAME); - len = strlen(path); - if (!path || !len) { + if (!path || !(len = strlen(path))) { TSTERR("zero result from lxc_cgroup_path_create"); exit(1); } @@ -110,7 +109,7 @@ int main() /* start second container */ if ((c2 = lxc_container_new(MYNAME2, ALTBASE)) == NULL) { - TSTERR("instantiating first container"); + TSTERR("instantiating second container"); goto out; } if (c2->is_defined(c2)) { @@ -120,14 +119,14 @@ int main() } c2->set_config_item(c2, "lxc.network.type", "empty"); if (!c2->createl(c2, "ubuntu", NULL)) { - TSTERR("creating first container"); + TSTERR("creating second container"); goto out; } c2->load_config(c2, NULL); c2->want_daemonize(c2); if (!c2->startl(c2, 0, NULL)) { - TSTERR("starting first container"); + TSTERR("starting second container"); goto out; } From d2c8186b4d185d75e81aec02d5a62dde4192c16d Mon Sep 17 00:00:00 2001 From: Harald Dunkel Date: Fri, 3 May 2013 10:53:40 +0200 Subject: [PATCH 096/325] support alternate container path in lxc-shutdown Signed-off-by: Harald Dunkel Signed-off-by: Serge Hallyn --- src/lxc/lxc-shutdown.in | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/lxc/lxc-shutdown.in b/src/lxc/lxc-shutdown.in index ee07f7595..953ee6eca 100644 --- a/src/lxc/lxc-shutdown.in +++ b/src/lxc/lxc-shutdown.in @@ -18,13 +18,16 @@ set -e +. @DATADIR@/lxc/lxc.functions + usage() { - echo "usage: lxc-shutdown -n name [-w] [-r]" + echo "usage: lxc-shutdown -n name [-w] [-r] [-P lxcpath]" echo " Cleanly shut down a container." echo " -w: wait for shutdown to complete." echo " -r: reboot (ignore -w)." echo " -t timeout: wait at most timeout seconds (implies -w), then kill" echo " the container." + echo " -P lxcpath: path to the lxc container directories." } alarm() { @@ -38,7 +41,7 @@ alarm() { dolxcstop() { echo "Calling lxc-stop on $lxc_name" - lxc-stop -n $lxc_name + lxc-stop -n $lxc_name -P "$lxcpath" exit 0 } @@ -82,6 +85,12 @@ while [ $# -gt 0 ]; do dowait=1 shift ;; + -P|--lxcpath) + optarg_check $opt "$1" + lxcpath=$1 + dowait=1 + shift + ;; --) break;; -?) @@ -104,6 +113,11 @@ if [ -z "$lxc_name" ]; then exit 1 fi +if [ ! -d "$lxcpath" ]; then + echo "$lxcpath: no such directory" + exit 1 +fi + if [ "$(id -u)" != "0" ]; then echo "This command has to be run as root" exit 1 @@ -112,7 +126,7 @@ fi which lxc-info > /dev/null 2>&1 || { echo "lxc-info not found."; exit 1; } which lxc-wait > /dev/null 2>&1 || { echo "lxc-wait not found."; exit 1; } -pid=`lxc-info -n $lxc_name -p 2>/dev/null | awk '{ print $2 }'` +pid=`lxc-info -n $lxc_name -P "$lxcpath" -p 2>/dev/null | awk '{ print $2 }'` if [ "$pid" = "-1" ]; then echo "$lxc_name is not running" exit 1 @@ -135,7 +149,7 @@ if [ $timeout != "-1" ]; then alarmpid=$! fi -while ! lxc-info -n $lxc_name --state-is STOPPED; do +while ! lxc-info -n $lxc_name -P "$lxcpath" --state-is STOPPED; do sleep 1 done From 8ee3042a5419ea4c9bb0d1c264715f9d9c39bfa3 Mon Sep 17 00:00:00 2001 From: Harald Dunkel Date: Fri, 3 May 2013 10:53:41 +0200 Subject: [PATCH 097/325] lxc-create: add missing -P option for running lxc-destroy Signed-off-by: Harald Dunkel Signed-off-by: Serge Hallyn --- src/lxc/lxc-create.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in index 286ed6a00..d85450157 100644 --- a/src/lxc/lxc-create.in +++ b/src/lxc/lxc-create.in @@ -316,7 +316,7 @@ cleanup() { zfs destroy "$zfs_root/$lxc_name" || true fi - ${bindir}/lxc-destroy -n $lxc_name + ${bindir}/lxc-destroy -n $lxc_name -P "$lxc_path" echo "$(basename $0): aborted" >&2 exit 1 } From 7c7ec7a8eded3d3864631165503fedb456e1b779 Mon Sep 17 00:00:00 2001 From: Harald Dunkel Date: Fri, 3 May 2013 10:53:43 +0200 Subject: [PATCH 098/325] support alternate container path in lxc-netstat.in Signed-off-by: Harald Dunkel Signed-off-by: Serge Hallyn --- src/lxc/lxc-netstat.in | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/lxc/lxc-netstat.in b/src/lxc/lxc-netstat.in index 2fa2d23ae..d3eee1c22 100644 --- a/src/lxc/lxc-netstat.in +++ b/src/lxc/lxc-netstat.in @@ -17,8 +17,10 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +. @DATADIR@/lxc/lxc.functions + usage() { - echo "usage: $(basename $0) -n|--name -- [netstat_options]" >&2 + echo "usage: $(basename $0) -n|--name [-P|--lxcpath ] -- [netstat_options]" >&2 } help() { @@ -26,8 +28,9 @@ help() { 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 + echo " --name NAME specify the container name" >&2 + echo " --lxcpath LXC_PATH use an alternate container path" >&2 + echo " NETSTAT_OPTIONS netstat command options (see \`netstat --help')" >&2 } get_parent_cgroup() @@ -73,6 +76,8 @@ while true; do help; exit 1;; -n|--name) name=$2; shift 2;; + -P|--lxcpath) + lxc_path="$2"; shift 2;; --exec) exec="exec"; shift;; --) @@ -92,11 +97,17 @@ if [ -z "$name" ]; then exit 1 fi -if [ -z "$exec" ]; then - exec @BINDIR@/lxc-unshare -s MOUNT -- $0 -n $name --exec "$@" +if [ -z "$lxc_path" ]; then + echo "$(basename $0): no configuration path defined" >&2 + usage + exit 1 fi -if lxc-info -n $name --state-is 'STOPPED'; then +if [ -z "$exec" ]; then + exec @BINDIR@/lxc-unshare -s MOUNT -- $0 -n $name -P "$lxc_path" --exec "$@" +fi + +if lxc-info -n $name -P "$lxc_path" --state-is 'STOPPED'; then echo "$(basename $0): container '$name' is not running" >&2 exit 1 fi From ab81cef05338e7a553aacca141287034d6daf167 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Fri, 3 May 2013 16:50:32 -0400 Subject: [PATCH 099/325] coverity: fix dereference NULL return value also break once we have found root, no need to search the rest of the mounts Changelog: May 6: Serge: don't add the break. (see m-l) Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/conf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 125e8a6d2..827626f1d 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1067,10 +1067,11 @@ int detect_shared_rootfs(void) if (strcmp(p+1, "/") == 0) { // this is '/'. is it shared? p = index(p2+1, ' '); - if (strstr(p, "shared:")) { + if (p && strstr(p, "shared:")) { fclose(f); return 1; } + break; } } fclose(f); From 91c908ee8ea5aada054cbb7f4203d486c2e9a09e Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 7 May 2013 10:57:03 -0400 Subject: [PATCH 100/325] coverity: free malloc'ed memory in error case Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/bdev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index 35351a9ae..1a611f9a5 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -1203,6 +1203,8 @@ static int overlayfs_clonepaths(struct bdev *orig, struct bdev *new, const char return -ENOMEM; } if (do_rsync(odelta, ndelta) < 0) { + free(osrc); + free(ndelta); ERROR("copying overlayfs delta"); return -1; } From 42fb4b1585d5f2073fbfe984acd46b625fd3c6a1 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 7 May 2013 10:57:09 -0400 Subject: [PATCH 101/325] coverity: open can return 0 as an fd, change error check to < 0 Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/bdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index 1a611f9a5..181e98ea1 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -90,7 +90,7 @@ static int blk_getsize(const char *path, unsigned long *size) int fd, ret; fd = open(path, O_RDONLY); - if (!fd) + if (fd < 0) return -1; ret = ioctl(fd, BLKGETSIZE64, size); close(fd); From 5ca6c34bdeb02ea355a0e5ef9ff51581b58c1ee7 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 7 May 2013 10:57:16 -0400 Subject: [PATCH 102/325] coverity: condition already checked for Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/bdev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index 181e98ea1..fcde16b51 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -1151,8 +1151,7 @@ static int overlayfs_clonepaths(struct bdev *orig, struct bdev *new, const char if (strcmp(orig->type, "dir") == 0) { char *delta; int ret, len; - if (!snap) - return -1; + // if we have /var/lib/lxc/c2/rootfs, then delta will be // /var/lib/lxc/c2/delta0 delta = strdup(new->dest); From 3856bc9ff50f2cbd6cb2830619f3594ffea0b344 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 7 May 2013 10:57:26 -0400 Subject: [PATCH 103/325] coverity: clonetest: check correct container is cloned Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/tests/clonetest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/clonetest.c b/src/tests/clonetest.c index fcb5ea66c..5644405e9 100644 --- a/src/tests/clonetest.c +++ b/src/tests/clonetest.c @@ -70,7 +70,7 @@ int main(int argc, char *argv[]) goto out; } - if (!c2->is_defined(c)) { + if (!c2->is_defined(c2)) { fprintf(stderr, "%d: %s not defined after clone\n", __LINE__, MYNAME2); goto out; } From f2bbe86da4044c8db39e6eae19541fe2d117bae7 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 7 May 2013 10:57:33 -0400 Subject: [PATCH 104/325] coverity: check return from waitpid Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/monitor.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lxc/monitor.c b/src/lxc/monitor.c index 0521e9ab3..5648b86b5 100644 --- a/src/lxc/monitor.c +++ b/src/lxc/monitor.c @@ -207,7 +207,8 @@ int lxc_monitord_spawn(const char *lxcpath) } if (pid1) { - waitpid(pid1, NULL, 0); + if (waitpid(pid1, NULL, 0) != pid1) + return -1; return 0; } From dd66e5adb38c76e6eecf0e54c5418fd9f7ac3b3b Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 7 May 2013 10:57:38 -0400 Subject: [PATCH 105/325] coverity: fix potential dereference NULL returned from malloc Signed-off-by: Dwight Engen Acked-by: Serge E. Hallyn --- src/lxc/network.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lxc/network.c b/src/lxc/network.c index 93fc169c2..d1ccc0d3d 100644 --- a/src/lxc/network.c +++ b/src/lxc/network.c @@ -783,8 +783,11 @@ static int ifa_get_local_ip(int family, struct ip_req *ip_info, void** res) { /* We might have found an IFA_ADDRESS before, * which we now overwrite with an IFA_LOCAL. */ - if (!*res) + if (!*res) { *res = malloc(addrlen); + if (!*res) + return -1; + } memcpy(*res, RTA_DATA(rta), addrlen); From 7f4717c293fd5ecb9d605bed890cb412314aa8e2 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 7 May 2013 15:33:42 -0500 Subject: [PATCH 106/325] conf.c: remove a break commit ab81cef05338e7a553aacca141287034d6daf167 meant to remove the added break, but apparently i had not done 'git add' before commit --amend. Remove the added break. Signed-off-by: Serge Hallyn --- src/lxc/conf.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 827626f1d..746a2dbd7 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1071,7 +1071,6 @@ int detect_shared_rootfs(void) fclose(f); return 1; } - break; } } fclose(f); From 566c0d6dce82ee573da01e325c53179ed74350f1 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 7 May 2013 19:28:32 -0500 Subject: [PATCH 107/325] lxc-ps: handle cgroup collisions A few months ago cgroup handling in lxc was updated so that if /sys/fs/cgroup/$cgroup/lxc/$container already exists (most often due to another container by the same name under a different lxcpath), then /sys/fs/cgroup/$cgroup/lxc/${container}-N would be used. lxc-ps was never updated to handle this. Fix that. (Note, the ns cgroup is being special cased there, but I don't really believe ns cgroup works any more.) It would be preferable to rewrite lxc-ps in python or in C, but this at least makes the basic lxc-ps work in the case of multiple containers with the same name. Changelog: fix missing fi. replace 'z1' with '$container' as pointed out by Christian Signed-off-by: Serge Hallyn --- src/lxc/lxc-ps.in | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/lxc/lxc-ps.in b/src/lxc/lxc-ps.in index 55a05ce48..29e8097d9 100644 --- a/src/lxc/lxc-ps.in +++ b/src/lxc/lxc-ps.in @@ -17,9 +17,11 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +. @DATADIR@/lxc/lxc.functions + usage() { - echo "usage: $(basename $0) [--lxc | --host | --name NAME] [--] [PS_OPTIONS...]" >&2 + echo "usage: $(basename $0) [-P PATH] [--lxc | --host | --name NAME] [[--] [PS_OPTIONS...]" >&2 } help() { @@ -31,6 +33,7 @@ help() { echo " --host show processes not related to any container, i.e. to the host" >&2 echo " --name NAME show processes in the specified container" >&2 echo " (multiple containers can be separated by commas)" >&2 + echo " -P PATH show container in lxcpath PATH" >&2 echo " PS_OPTIONS ps command options (see \`ps --help')" >&2 } @@ -65,7 +68,7 @@ get_parent_cgroup() # (do not append '/lxc' if the hierarchy contains the 'ns' subsystem) case ",$subsystems," in *,ns,*) parent_cgroup="${mountpoint}${init_cgroup%/}";; - *) parent_cgroup="${mountpoint}${init_cgroup%/}/lxc";; + *) parent_cgroup="${mountpoint}${init_cgroup%/}";; esac break done @@ -83,6 +86,8 @@ while true; do list_container_processes=1; shift;; --host) list_container_processes=-1; shift;; + -P|--lxcpath) + lxc_path=$2; shift 2;; --) shift; break;; *) @@ -111,8 +116,12 @@ for container in ${containers}; do container_field_width=${#container} fi - if [ -f "$parent_cgroup/$container/tasks" ]; then - tasks_files="$tasks_files $parent_cgroup/$container/tasks" + if lxc-info -P $lxc_path -t RUNNING -n $container; then + initpid=`lxc-info -P $lxc_path -p -n $container | awk -F: '{ print $2 }' | awk '{ print $1 }'` + cgroup=`head -n 1 /proc/$initpid/cgroup | awk -F: '{ print $3}'` + if [ -f "$parent_cgroup/$cgroup/tasks" ]; then + tasks_files="$tasks_files $parent_cgroup$cgroup/tasks" + fi fi done From 8d06bd135af4852f24660be965aba2d781223af4 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 7 May 2013 16:40:49 -0400 Subject: [PATCH 108/325] lxc-monitor multiple paths Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- doc/lxc-monitor.sgml.in | 7 +++++ src/lxc/arguments.c | 34 ++++++++++++++++++++++-- src/lxc/arguments.h | 5 +++- src/lxc/lxc.h | 31 ++++++++++++++++++---- src/lxc/lxc_attach.c | 8 +++--- src/lxc/lxc_cgroup.c | 6 ++--- src/lxc/lxc_checkpoint.c | 2 +- src/lxc/lxc_console.c | 4 +-- src/lxc/lxc_execute.c | 6 ++--- src/lxc/lxc_freeze.c | 4 +-- src/lxc/lxc_info.c | 6 ++--- src/lxc/lxc_kill.c | 4 +-- src/lxc/lxc_monitor.c | 35 +++++++++++++++++------- src/lxc/lxc_restart.c | 6 ++--- src/lxc/lxc_start.c | 6 ++--- src/lxc/lxc_stop.c | 4 +-- src/lxc/lxc_unfreeze.c | 4 +-- src/lxc/lxc_wait.c | 5 ++-- src/lxc/monitor.c | 57 ++++++++++++++++++++++++++-------------- 19 files changed, 165 insertions(+), 69 deletions(-) diff --git a/doc/lxc-monitor.sgml.in b/doc/lxc-monitor.sgml.in index 336061dd4..b460843c4 100644 --- a/doc/lxc-monitor.sgml.in +++ b/doc/lxc-monitor.sgml.in @@ -63,6 +63,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to monitor all the containers, several of them or just one. + + The =PATH option may be specified multiple + times to monitor more than one container path. Note however that + containers with the same name in multiple paths will be + indistinguishable in the output. + + &commonoptions; diff --git a/src/lxc/arguments.c b/src/lxc/arguments.c index f61c6eb84..5f1c1af66 100644 --- a/src/lxc/arguments.c +++ b/src/lxc/arguments.c @@ -150,13 +150,32 @@ See the %s man page for further information.\n\n", exit(code); } +static int lxc_arguments_lxcpath_add(struct lxc_arguments *args, + const char *lxcpath) +{ + if (args->lxcpath_additional != -1 && + args->lxcpath_cnt > args->lxcpath_additional) { + fprintf(stderr, "This command only accepts %d -P,--lxcpath arguments\n", + args->lxcpath_additional + 1); + exit(EXIT_FAILURE); + } + + args->lxcpath = realloc(args->lxcpath, (args->lxcpath_cnt + 1) * + sizeof(args->lxcpath[0])); + if (args->lxcpath == NULL) { + lxc_error(args, "no memory"); + return ENOMEM; + } + args->lxcpath[args->lxcpath_cnt++] = lxcpath; + return 0; +} + extern int lxc_arguments_parse(struct lxc_arguments *args, int argc, char * const argv[]) { char shortopts[256]; int ret = 0; - args->lxcpath = default_lxc_path(); ret = build_shortopts(args->options, shortopts, sizeof(shortopts)); if (ret < 0) { lxc_error(args, "build_shortopts() failed : %s", @@ -176,7 +195,11 @@ extern int lxc_arguments_parse(struct lxc_arguments *args, case 'l': args->log_priority = optarg; break; case 'c': args->console = optarg; break; case 'q': args->quiet = 1; break; - case 'P': args->lxcpath = optarg; break; + case 'P': + ret = lxc_arguments_lxcpath_add(args, optarg); + if (ret < 0) + return ret; + break; case OPT_USAGE: print_usage(args->options, args); case '?': print_help(args, 1); case 'h': print_help(args, 0); @@ -195,6 +218,13 @@ extern int lxc_arguments_parse(struct lxc_arguments *args, args->argv = &argv[optind]; args->argc = argc - optind; + /* If no lxcpaths were given, use default */ + if (!args->lxcpath_cnt) { + ret = lxc_arguments_lxcpath_add(args, default_lxc_path()); + if (ret < 0) + return ret; + } + /* Check the command options */ if (!args->name) { diff --git a/src/lxc/arguments.h b/src/lxc/arguments.h index 6f6826b75..002a91961 100644 --- a/src/lxc/arguments.h +++ b/src/lxc/arguments.h @@ -47,7 +47,10 @@ struct lxc_arguments { const char *console; const char *console_log; const char *pidfile; - const char *lxcpath; + const char **lxcpath; + int lxcpath_cnt; + /* set to 0 to accept only 1 lxcpath, -1 for unlimited */ + int lxcpath_additional; /* for lxc-checkpoint/restart */ const char *statefile; diff --git a/src/lxc/lxc.h b/src/lxc/lxc.h index db921f0ff..9057757c5 100644 --- a/src/lxc/lxc.h +++ b/src/lxc/lxc.h @@ -28,6 +28,7 @@ extern "C" { #endif #include +#include #include struct lxc_msg; @@ -77,16 +78,36 @@ extern int lxc_execute(const char *name, char *const argv[], int quiet, extern int lxc_monitor_open(const char *lxcpath); /* - * Read the state of the container if this one has changed - * The function will block until there is an event available - * @fd : the file descriptor provided by lxc_monitor_open - * @state : the variable which will be filled with the state + * Blocking read for the next container state change + * @fd : the file descriptor provided by lxc_monitor_open + * @msg : the variable which will be filled with the state * Returns 0 if the monitored container has exited, > 0 if - * data was readen, < 0 otherwise + * data was read, < 0 otherwise */ extern int lxc_monitor_read(int fd, struct lxc_msg *msg); + +/* + * Blocking read for the next container state change with timeout + * @fd : the file descriptor provided by lxc_monitor_open + * @msg : the variable which will be filled with the state + * @timeout : the timeout in seconds to wait for a state change + * Returns 0 if the monitored container has exited, > 0 if + * data was read, < 0 otherwise + */ extern int lxc_monitor_read_timeout(int fd, struct lxc_msg *msg, int timeout); +/* + * Blocking read from multiple monitors for the next container state + * change with timeout + * @rfds : an fd_set of file descriptors provided by lxc_monitor_open + * @nfds : the maximum fd number in rfds + 1 + * @msg : the variable which will be filled with the state + * @timeout : the timeout in seconds to wait for a state change + * Returns 0 if the monitored container has exited, > 0 if + * data was read, < 0 otherwise + */ +extern int lxc_monitor_read_fdset(fd_set *rfds, int nfds, struct lxc_msg *msg, int timeout); + /* * Close the fd associated with the monitoring * @fd : the file descriptor provided by lxc_monitor_open diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c index 300bc922d..e0c253b4b 100644 --- a/src/lxc/lxc_attach.c +++ b/src/lxc/lxc_attach.c @@ -292,11 +292,11 @@ int main(int argc, char *argv[]) return ret; ret = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath); + my_args.progname, my_args.quiet, my_args.lxcpath[0]); if (ret) return ret; - init_pid = get_init_pid(my_args.name, my_args.lxcpath); + init_pid = get_init_pid(my_args.name, my_args.lxcpath[0]); if (init_pid < 0) { ERROR("failed to get the init pid"); return -1; @@ -314,7 +314,7 @@ int main(int argc, char *argv[]) * by asking lxc-start */ if (namespace_flags == -1) { - namespace_flags = lxc_get_clone_flags(my_args.name, my_args.lxcpath); + namespace_flags = lxc_get_clone_flags(my_args.name, my_args.lxcpath[0]); /* call failed */ if (namespace_flags == -1) { ERROR("failed to automatically determine the " @@ -387,7 +387,7 @@ int main(int argc, char *argv[]) } if (!elevated_privileges) { - ret = lxc_cgroup_attach(grandchild, my_args.name, my_args.lxcpath); + ret = lxc_cgroup_attach(grandchild, my_args.name, my_args.lxcpath[0]); if (ret < 0) { ERROR("failed to attach process to cgroup"); return -1; diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c index 7684f1b1f..094686da9 100644 --- a/src/lxc/lxc_cgroup.c +++ b/src/lxc/lxc_cgroup.c @@ -69,7 +69,7 @@ int main(int argc, char *argv[]) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath)) + my_args.progname, my_args.quiet, my_args.lxcpath[0])) return -1; state_object = my_args.argv[0]; @@ -78,7 +78,7 @@ int main(int argc, char *argv[]) value = my_args.argv[1]; if (value) { - if (lxc_cgroup_set(my_args.name, state_object, value, my_args.lxcpath)) { + if (lxc_cgroup_set(my_args.name, state_object, value, my_args.lxcpath[0])) { ERROR("failed to assign '%s' value to '%s' for '%s'", value, state_object, my_args.name); return -1; @@ -88,7 +88,7 @@ int main(int argc, char *argv[]) int ret; char buffer[len]; - ret = lxc_cgroup_get(my_args.name, state_object, buffer, len, my_args.lxcpath); + ret = lxc_cgroup_get(my_args.name, state_object, buffer, len, my_args.lxcpath[0]); if (ret < 0) { ERROR("failed to retrieve value of '%s' for '%s'", state_object, my_args.name); diff --git a/src/lxc/lxc_checkpoint.c b/src/lxc/lxc_checkpoint.c index 8b3a02abe..ee41ba12f 100644 --- a/src/lxc/lxc_checkpoint.c +++ b/src/lxc/lxc_checkpoint.c @@ -116,7 +116,7 @@ int main(int argc, char *argv[]) return ret; ret = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath); + my_args.progname, my_args.quiet, my_args.lxcpath[0]); if (ret) return ret; diff --git a/src/lxc/lxc_console.c b/src/lxc/lxc_console.c index 1d779a161..795b54c24 100644 --- a/src/lxc/lxc_console.c +++ b/src/lxc/lxc_console.c @@ -192,7 +192,7 @@ int main(int argc, char *argv[]) return -1; err = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath); + my_args.progname, my_args.quiet, my_args.lxcpath[0]); if (err) return -1; @@ -202,7 +202,7 @@ int main(int argc, char *argv[]) return -1; } - err = lxc_console(my_args.name, my_args.ttynum, &master, my_args.lxcpath); + err = lxc_console(my_args.name, my_args.ttynum, &master, my_args.lxcpath[0]); if (err) goto out; diff --git a/src/lxc/lxc_execute.c b/src/lxc/lxc_execute.c index 0fa2d2516..3c070cf96 100644 --- a/src/lxc/lxc_execute.c +++ b/src/lxc/lxc_execute.c @@ -101,7 +101,7 @@ int main(int argc, char *argv[]) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath)) + my_args.progname, my_args.quiet, my_args.lxcpath[0])) return -1; /* rcfile is specified in the cli option */ @@ -110,7 +110,7 @@ int main(int argc, char *argv[]) else { int rc; - rc = asprintf(&rcfile, "%s/%s/config", my_args.lxcpath, my_args.name); + rc = asprintf(&rcfile, "%s/%s/config", my_args.lxcpath[0], my_args.name); if (rc == -1) { SYSERROR("failed to allocate memory"); return -1; @@ -137,5 +137,5 @@ int main(int argc, char *argv[]) if (lxc_config_define_load(&defines, conf)) return -1; - return lxc_execute(my_args.name, my_args.argv, my_args.quiet, conf, my_args.lxcpath); + return lxc_execute(my_args.name, my_args.argv, my_args.quiet, conf, my_args.lxcpath[0]); } diff --git a/src/lxc/lxc_freeze.c b/src/lxc/lxc_freeze.c index ff3cdd577..3bd5e2866 100644 --- a/src/lxc/lxc_freeze.c +++ b/src/lxc/lxc_freeze.c @@ -55,9 +55,9 @@ int main(int argc, char *argv[]) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath)) + my_args.progname, my_args.quiet, my_args.lxcpath[0])) return -1; - return lxc_freeze(my_args.name, my_args.lxcpath); + return lxc_freeze(my_args.name, my_args.lxcpath[0]); } diff --git a/src/lxc/lxc_info.c b/src/lxc/lxc_info.c index b19b1d02f..f815b0f47 100644 --- a/src/lxc/lxc_info.c +++ b/src/lxc/lxc_info.c @@ -80,14 +80,14 @@ int main(int argc, char *argv[]) return 1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath)) + my_args.progname, my_args.quiet, my_args.lxcpath[0])) return 1; if (!state && !pid) state = pid = true; if (state || test_state) { - ret = lxc_getstate(my_args.name, my_args.lxcpath); + ret = lxc_getstate(my_args.name, my_args.lxcpath[0]); if (ret < 0) return 1; if (test_state) @@ -97,7 +97,7 @@ int main(int argc, char *argv[]) } if (pid) - printf("pid:%10d\n", get_init_pid(my_args.name, my_args.lxcpath)); + printf("pid:%10d\n", get_init_pid(my_args.name, my_args.lxcpath[0])); return 0; } diff --git a/src/lxc/lxc_kill.c b/src/lxc/lxc_kill.c index e08993139..9a24209dd 100644 --- a/src/lxc/lxc_kill.c +++ b/src/lxc/lxc_kill.c @@ -62,7 +62,7 @@ int main(int argc, char *argv[], char *envp[]) return ret; ret = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath); + my_args.progname, my_args.quiet, my_args.lxcpath[0]); if (ret) return ret; @@ -76,7 +76,7 @@ int main(int argc, char *argv[], char *envp[]) } else sig=SIGKILL; - pid = get_init_pid(my_args.name, my_args.lxcpath); + pid = get_init_pid(my_args.name, my_args.lxcpath[0]); if (pid < 0) { ERROR("failed to get the init pid"); return -1; diff --git a/src/lxc/lxc_monitor.c b/src/lxc/lxc_monitor.c index 5ddb2913a..e41686fbd 100644 --- a/src/lxc/lxc_monitor.c +++ b/src/lxc/lxc_monitor.c @@ -52,6 +52,7 @@ Options :\n\ .options = my_longopts, .parser = NULL, .checker = NULL, + .lxcpath_additional = -1, }; int main(int argc, char *argv[]) @@ -59,14 +60,14 @@ int main(int argc, char *argv[]) char *regexp; struct lxc_msg msg; regex_t preg; - int fd; - int len, rc; + fd_set rfds, rfds_save; + int len, rc, i, nfds = -1; if (lxc_arguments_parse(&my_args, argc, argv)) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath)) + my_args.progname, my_args.quiet, my_args.lxcpath[0])) return -1; len = strlen(my_args.name) + 3; @@ -87,16 +88,33 @@ int main(int argc, char *argv[]) return -1; } - lxc_monitord_spawn(my_args.lxcpath); + if (my_args.lxcpath_cnt > FD_SETSIZE) { + ERROR("too many paths requested, only the first %d will be monitored", FD_SETSIZE); + my_args.lxcpath_cnt = FD_SETSIZE; + } - fd = lxc_monitor_open(my_args.lxcpath); - if (fd < 0) - return -1; + FD_ZERO(&rfds); + for (i = 0; i < my_args.lxcpath_cnt; i++) { + int fd; + + lxc_monitord_spawn(my_args.lxcpath[i]); + + fd = lxc_monitor_open(my_args.lxcpath[i]); + if (fd < 0) + return -1; + FD_SET(fd, &rfds); + if (fd > nfds) + nfds = fd; + } + memcpy(&rfds_save, &rfds, sizeof(rfds_save)); + nfds++; setlinebuf(stdout); for (;;) { - if (lxc_monitor_read(fd, &msg) < 0) + memcpy(&rfds, &rfds_save, sizeof(rfds)); + + if (lxc_monitor_read_fdset(&rfds, nfds, &msg, -1) < 0) return -1; msg.name[sizeof(msg.name)-1] = '\0'; @@ -118,4 +136,3 @@ int main(int argc, char *argv[]) return 0; } - diff --git a/src/lxc/lxc_restart.c b/src/lxc/lxc_restart.c index 77099b149..3b8eedc17 100644 --- a/src/lxc/lxc_restart.c +++ b/src/lxc/lxc_restart.c @@ -124,7 +124,7 @@ int main(int argc, char *argv[]) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath)) + my_args.progname, my_args.quiet, my_args.lxcpath[0])) return -1; /* rcfile is specified in the cli option */ @@ -133,7 +133,7 @@ int main(int argc, char *argv[]) else { int rc; - rc = asprintf(&rcfile, "%s/%s/config", my_args.lxcpath, my_args.name); + rc = asprintf(&rcfile, "%s/%s/config", my_args.lxcpath[0], my_args.name); if (rc == -1) { SYSERROR("failed to allocate memory"); return -1; @@ -172,7 +172,7 @@ int main(int argc, char *argv[]) } } - ret = lxc_restart(my_args.name, sfd, conf, my_args.flags, my_args.lxcpath); + ret = lxc_restart(my_args.name, sfd, conf, my_args.flags, my_args.lxcpath[0]); if (my_args.statefile) close(sfd); diff --git a/src/lxc/lxc_start.c b/src/lxc/lxc_start.c index 12aacb8cc..490dbadd8 100644 --- a/src/lxc/lxc_start.c +++ b/src/lxc/lxc_start.c @@ -165,7 +165,7 @@ int main(int argc, char *argv[]) args = my_args.argv; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath)) + my_args.progname, my_args.quiet, my_args.lxcpath[0])) return err; /* rcfile is specified in the cli option */ @@ -174,7 +174,7 @@ int main(int argc, char *argv[]) else { int rc; - rc = asprintf(&rcfile, "%s/%s/config", my_args.lxcpath, my_args.name); + rc = asprintf(&rcfile, "%s/%s/config", my_args.lxcpath[0], my_args.name); if (rc == -1) { SYSERROR("failed to allocate memory"); return err; @@ -252,7 +252,7 @@ int main(int argc, char *argv[]) if (my_args.close_all_fds) conf->close_all_fds = 1; - err = lxc_start(my_args.name, args, conf, my_args.lxcpath); + err = lxc_start(my_args.name, args, conf, my_args.lxcpath[0]); /* * exec ourself, that requires to have all opened fd diff --git a/src/lxc/lxc_stop.c b/src/lxc/lxc_stop.c index 0967a156f..d7c728373 100644 --- a/src/lxc/lxc_stop.c +++ b/src/lxc/lxc_stop.c @@ -55,8 +55,8 @@ int main(int argc, char *argv[]) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath)) + my_args.progname, my_args.quiet, my_args.lxcpath[0])) return -1; - return lxc_stop(my_args.name, my_args.lxcpath); + return lxc_stop(my_args.name, my_args.lxcpath[0]); } diff --git a/src/lxc/lxc_unfreeze.c b/src/lxc/lxc_unfreeze.c index 44d3cc021..095f29084 100644 --- a/src/lxc/lxc_unfreeze.c +++ b/src/lxc/lxc_unfreeze.c @@ -54,9 +54,9 @@ int main(int argc, char *argv[]) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath)) + my_args.progname, my_args.quiet, my_args.lxcpath[0])) return -1; - return lxc_unfreeze(my_args.name, my_args.lxcpath); + return lxc_unfreeze(my_args.name, my_args.lxcpath[0]); } diff --git a/src/lxc/lxc_wait.c b/src/lxc/lxc_wait.c index d21578b6c..f1a065c1f 100644 --- a/src/lxc/lxc_wait.c +++ b/src/lxc/lxc_wait.c @@ -84,8 +84,9 @@ int main(int argc, char *argv[]) return -1; if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, - my_args.progname, my_args.quiet, my_args.lxcpath)) + my_args.progname, my_args.quiet, my_args.lxcpath[0])) return -1; - return lxc_wait(strdup(my_args.name), my_args.states, my_args.timeout, my_args.lxcpath); + return lxc_wait(strdup(my_args.name), my_args.states, my_args.timeout, + my_args.lxcpath[0]); } diff --git a/src/lxc/monitor.c b/src/lxc/monitor.c index 5648b86b5..c04bb73d3 100644 --- a/src/lxc/monitor.c +++ b/src/lxc/monitor.c @@ -147,33 +147,50 @@ err1: return ret; } -int lxc_monitor_read_timeout(int fd, struct lxc_msg *msglxc, int timeout) +int lxc_monitor_read_fdset(fd_set *rfds, int nfds, struct lxc_msg *msg, + int timeout) { - fd_set rfds; - struct timeval tv; - int ret; + struct timeval tval,*tv = NULL; + int ret,i; 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 + tv = &tval; + tv->tv_sec = timeout; + tv->tv_usec = 0; } - ret = recv(fd, msglxc, sizeof(*msglxc), 0); - if (ret <= 0) { - SYSERROR("client failed to recv (monitord died?) %s", - strerror(errno)); + ret = select(nfds, rfds, NULL, NULL, tv); + if (ret == -1) return -1; + else if (ret == 0) + return -2; // timed out + + /* only read from the first ready fd, the others will remain ready + * for when this routine is called again + */ + for (i = 0; i < nfds; i++) { + if (FD_ISSET(i, rfds)) { + ret = recv(i, msg, sizeof(*msg), 0); + if (ret <= 0) { + SYSERROR("client failed to recv (monitord died?) %s", + strerror(errno)); + return -1; + } + return ret; + } } - return ret; + SYSERROR("no ready fd found?"); + return -1; +} + +int lxc_monitor_read_timeout(int fd, struct lxc_msg *msg, int timeout) +{ + fd_set rfds; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + return lxc_monitor_read_fdset(&rfds, fd+1, msg, timeout); } int lxc_monitor_read(int fd, struct lxc_msg *msg) From 304143a823ede4eca52f1d11ae1449995ad503ff Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 8 May 2013 16:44:10 -0400 Subject: [PATCH 109/325] lxc-shutdown: fix lxc_path variable Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/lxc-shutdown.in | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lxc/lxc-shutdown.in b/src/lxc/lxc-shutdown.in index 953ee6eca..c81e736b6 100644 --- a/src/lxc/lxc-shutdown.in +++ b/src/lxc/lxc-shutdown.in @@ -41,7 +41,7 @@ alarm() { dolxcstop() { echo "Calling lxc-stop on $lxc_name" - lxc-stop -n $lxc_name -P "$lxcpath" + lxc-stop -n $lxc_name -P "$lxc_path" exit 0 } @@ -87,7 +87,7 @@ while [ $# -gt 0 ]; do ;; -P|--lxcpath) optarg_check $opt "$1" - lxcpath=$1 + lxc_path=$1 dowait=1 shift ;; @@ -113,8 +113,8 @@ if [ -z "$lxc_name" ]; then exit 1 fi -if [ ! -d "$lxcpath" ]; then - echo "$lxcpath: no such directory" +if [ ! -d "$lxc_path" ]; then + echo "$lxc_path: no such directory" exit 1 fi @@ -126,7 +126,7 @@ fi which lxc-info > /dev/null 2>&1 || { echo "lxc-info not found."; exit 1; } which lxc-wait > /dev/null 2>&1 || { echo "lxc-wait not found."; exit 1; } -pid=`lxc-info -n $lxc_name -P "$lxcpath" -p 2>/dev/null | awk '{ print $2 }'` +pid=`lxc-info -n $lxc_name -P "$lxc_path" -p 2>/dev/null | awk '{ print $2 }'` if [ "$pid" = "-1" ]; then echo "$lxc_name is not running" exit 1 @@ -149,7 +149,7 @@ if [ $timeout != "-1" ]; then alarmpid=$! fi -while ! lxc-info -n $lxc_name -P "$lxcpath" --state-is STOPPED; do +while ! lxc-info -n $lxc_name -P "$lxc_path" --state-is STOPPED; do sleep 1 done From 714540763b8b1ac12c029d7760b4e4fe13a69b43 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Mon, 13 May 2013 12:03:14 -0400 Subject: [PATCH 110/325] serialize multiple threads doing lxcapi_start() The problem is that the fd table is shared between threads and if a thread forks() while another thread has an open fd to the monitor, the duped fd in the fork()ed child will not get closed, thus causing monitord to stay around since it thinks it still has a client. This only happened when calling lxcapi_start() in the daemonized case since that is the only time we try to get the status from the monitor. Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/lxccontainer.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 04a920826..bac94c234 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -18,6 +18,7 @@ */ #define _GNU_SOURCE +#include #include #include #include @@ -37,6 +38,8 @@ #include #include +static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; + /* Define unshare() if missing from the C library */ /* this is also in attach.c and lxccontainer.c: commonize it in utils.c */ #ifndef HAVE_UNSHARE @@ -58,6 +61,7 @@ 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 + * thread_mutex protects process data (ex: fd table) from multiple threads * 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 @@ -391,13 +395,24 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv if (!lxc_container_get(c)) return false; lxc_monitord_spawn(c->config_path); + + ret = pthread_mutex_lock(&thread_mutex); + if (ret != 0) { + ERROR("pthread_mutex_lock returned:%d %s", ret, strerror(ret)); + return false; + } pid_t pid = fork(); if (pid < 0) { lxc_container_put(c); + pthread_mutex_unlock(&thread_mutex); return false; } - if (pid != 0) - return wait_on_daemonized_start(c); + if (pid != 0) { + ret = wait_on_daemonized_start(c); + pthread_mutex_unlock(&thread_mutex); + return ret; + } + pthread_mutex_unlock(&thread_mutex); /* second fork to be reparented by init */ pid = fork(); if (pid < 0) { From 58a46e06210a6321c530735f15f66eb648c4657d Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 10 May 2013 00:52:22 -0500 Subject: [PATCH 111/325] add lxc-cirros Add a template to create a cirros container. One great thing about cirros is that the image you download is 3.5M. Thanks smoser! Note by default /etc/inittab doesn't have a /dev/console entry, so you don't get a login on the lxc-start console. Adding console::respawn:/sbin/getty 115200 console makes that work, but ctrl-c still gets forwarded to init which then reboots. So I didn't bother adding console as part of the template (yet). Instead I simply lxc-start -d, then lxc-console. Signed-off-by: Scott Moser Signed-off-by: Serge Hallyn --- configure.ac | 1 + templates/Makefile.am | 3 +- templates/lxc-cirros.in | 288 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 templates/lxc-cirros.in diff --git a/configure.ac b/configure.ac index f27fb87b6..414d71b68 100644 --- a/configure.ac +++ b/configure.ac @@ -363,6 +363,7 @@ AC_CONFIG_FILES([ hooks/Makefile templates/Makefile + templates/lxc-cirros templates/lxc-debian templates/lxc-ubuntu templates/lxc-ubuntu-cloud diff --git a/templates/Makefile.am b/templates/Makefile.am index 0c3066710..98d6d72c3 100644 --- a/templates/Makefile.am +++ b/templates/Makefile.am @@ -11,4 +11,5 @@ templates_SCRIPTS = \ lxc-busybox \ lxc-sshd \ lxc-archlinux \ - lxc-alpine + lxc-alpine \ + lxc-cirros diff --git a/templates/lxc-cirros.in b/templates/lxc-cirros.in new file mode 100644 index 000000000..45fd40fbe --- /dev/null +++ b/templates/lxc-cirros.in @@ -0,0 +1,288 @@ +#!/bin/bash + +# template script for generating ubuntu container for LXC +# +# This script consolidates and extends the existing lxc ubuntu scripts +# + +# Copyright © 2013 Canonical Ltd. +# Author: Scott Moser +# +# 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. +VERBOSITY=0 +TEMP_D="" +DOWNLOAD_URL="http://download.cirros-cloud.net/" +CACHE_D="/var/cache/lxc/cirros" +CACHE_D="@LOCALSTATEDIR@/cache/lxc/cirros" +TMP_FILE="" + +UNAME_M=$(uname -m) +ARCHES=( i386 x86_64 amd64 arm ) +STREAMS=( released devel ) +BUILD="standard" + +DEF_VERSION="released" +case "${UNAME_M}" in + i?86) DEF_ARCH="i386";; + x86_64) DEF_ARCH="x86_64";; + arm*) DEF_ARCH="arm";; + *) DEF_ARCH="i386";; +esac + + +error() { echo "$@" 1>&2; } +errorp() { printf "$@" 1>&2; } +fail() { [ $# -eq 0 ] || error "$@"; exit 1; } +failp() { [ $# -eq 0 ] || errorp "$@"; exit 1; } +inargs() { + local needle="$1" x="" + shift + for x in "$@"; do + [ "$needle" = "$x" ] && return 0 + done + return 1 +} + +Usage() { + cat <&2; [ $# -eq 0 ] || error "$@"; exit 1; } +cleanup() { + [ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}" + [ -z "${TEMP_FILE}" ] || rm -Rf "${TEMP_FILE}" +} + +debug() { + local level=${1}; shift; + [ "${level}" -gt "${VERBOSITY}" ] && return + error "${@}" +} +jsondict() { + local k="" v="" ret="{" + for arg in "$@"; do + k="${arg%%=*}" + v="${arg#*=}" + ret="${ret} \"${k}\": \"$v\"," + done + ret="${ret%,} }" + echo "$ret" +} + +copy_configuration() +{ + local path=$1 rootfs=$2 name=$3 arch=$4 release=$5 +cat >> "$path/config" < "$sdir/meta-data" || + { error "failed to write metadata to $sdir/meta-data"; return 1; } + + [ -z "$userdata" ] || + echo "$userdata" > "$sdir/user-data" || + { error "failed to write user-data to $sdir"; return 1; } +} + +extract_rootfs() { + local tarball="$1" rootfs_d="$2" + mkdir -p "${rootfs_d}" || + { error "failed to make rootfs dir ${rootfs_d}"; return 1; } + + tar -C "${rootfs_d}" -Sxzf "${tarball}" || + { error "failed to populate ${rootfs_d}"; return 1; } + return 0 +} + +download_tarball() { + local arch="$1" ver="$2" cached="$3" baseurl="$4" + local out="" outd="" file="" dlpath="" + file="cirros-$ver-$arch-lxc.tar.gz" + dlpath="$ver/$file" + outd="${cached}/${dlpath%/*}" + if [ -f "$cached/$dlpath" ]; then + _RET="$cached/$dlpath" + return 0 + fi + + mkdir -p "${outd}" || + { error "failed to create ${outd}"; return 1; } + + wget "${baseurl%/}/$dlpath" -O "$cached/${dlpath}.$$" && + mv "$cached/$dlpath.$$" "$cached/$dlpath" || { + rm -f "$cached/$dlpath.$$"; + error "failed to download $dlpath"; + return 1; + } + _RET="$cached/$dlpath" +} + +short_opts="a:n:p:S:huV" +long_opts="arch:,auth-key:,name:,path:,userdata:,verbose,version:" +getopt_out=$(getopt --name "${0##*/}" \ + --options "${short_opts}" --long "${long_opts}" -- "$@") && + eval set -- "${getopt_out}" || + bad_Usage + +arch="${DEF_ARCH}" +version="${DEF_VERSION}" +authkey_f="" +authkeys="" +userdata="" +seed=true +path="" + +while [ $# -ne 0 ]; do + cur=$1; next=$2; + case "$cur" in + -a|--arch) arch="$next"; shift;; + -h|--help) Usage ; exit 0;; + -n|--name) name="$next"; shift;; + -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));; + -S|--auth-key) authkey_f="$next"; shift;; + -p|--path) path=$next; shift;; + -v|--version) version=$next; shift;; + -u|--userdata) userdata="$next"; shift;; + --) shift; break;; + esac + shift; +done + +[ $# -eq 0 ] || bad_Usage "unexpected arguments: $*" +[ -n "$path" ] || fail "'path' parameter is required" + +if [ "$(id -u)" != "0" ]; then + fail "must be run as root" +fi + +case "$arch" in + i?86) arch="i386";; + amd64) arch="x86_65";; +esac + +inargs "$arch" "${ARCHES[@]}" || + fail "bad arch '$arch'. allowed: ${ARCHES[*]}" + +if inargs "$version" "${STREAMS[@]}"; then + out=$(wget -O - -q "${DOWNLOAD_URL%/}/version/$version") || + fail "failed to convert 'version=$version'" + version="$out" +fi + +if [ -n "$authkey_f" ]; then + if [ ! -f "$authkey_f" ]; then + echo "--auth-key=${authkey_f} must reference a file" + exit 1 + fi + authkeys=$(cat "$authkey") +fi + +trap cleanup EXIT + +download_tarball "$arch" "$version" "${CACHE_D}" "${DOWNLOAD_URL}" || fail +tarball="$_RET" + +rootfs_d="$path/rootfs" +extract_rootfs "${tarball}" "${rootfs_d}" || fail + +# oopsie - some cirros tarballs ship random as a blockdev, breaking +# dropbear. +if [ -b ${rootfs_d}/dev/random ]; then + rm -f ${rootfs_d}/dev/random + rm -f ${rootfs_d}/dev/urandom + mknod ${rootfs_d}/dev/random c 1 8 + mknod ${rootfs_d}/dev/urandom c 1 9 + chmod 777 ${rootfs_d}/dev/random ${rootfs_d}/dev/urandom +fi + +if false; then + insert_ds "$path/rootfs" "nocloud" "$authkeys" "$userdata" || + fail "failed to insert userdata to $path/rootfs" +else + # disable ec2, because its annoying + sed -i 's,ec2,,' "$path/rootfs/etc/cirros-init/config" +fi + +copy_configuration "$path" "$path/rootfs" "$name" "$arch" "$release" + + +# vi: ts=4 expandtab From 807732062eab6cd44fb033bfbb37fbb38907aa66 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 14 May 2013 08:24:27 -0500 Subject: [PATCH 112/325] lxc-cirros updates fix userdata consumption patch for console issue Signed-off-by: Scott Moser Signed-off-by: Serge Hallyn --- templates/lxc-cirros.in | 219 +++++++++++++++++++++++----------------- 1 file changed, 125 insertions(+), 94 deletions(-) diff --git a/templates/lxc-cirros.in b/templates/lxc-cirros.in index 45fd40fbe..ee5b91f8a 100644 --- a/templates/lxc-cirros.in +++ b/templates/lxc-cirros.in @@ -21,18 +21,17 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. VERBOSITY=0 -TEMP_D="" DOWNLOAD_URL="http://download.cirros-cloud.net/" -CACHE_D="/var/cache/lxc/cirros" CACHE_D="@LOCALSTATEDIR@/cache/lxc/cirros" -TMP_FILE="" UNAME_M=$(uname -m) ARCHES=( i386 x86_64 amd64 arm ) STREAMS=( released devel ) +SOURCES=( nocloud none ) BUILD="standard" DEF_VERSION="released" +DEF_SOURCE="nocloud" case "${UNAME_M}" in i?86) DEF_ARCH="i386";; x86_64) DEF_ARCH="x86_64";; @@ -40,11 +39,7 @@ case "${UNAME_M}" in *) DEF_ARCH="i386";; esac - error() { echo "$@" 1>&2; } -errorp() { printf "$@" 1>&2; } -fail() { [ $# -eq 0 ] || error "$@"; exit 1; } -failp() { [ $# -eq 0 ] || errorp "$@"; exit 1; } inargs() { local needle="$1" x="" shift @@ -58,7 +53,7 @@ Usage() { cat <&2; [ $# -eq 0 ] || error "$@"; exit 1; } -cleanup() { - [ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}" - [ -z "${TEMP_FILE}" ] || rm -Rf "${TEMP_FILE}" -} +bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; return 1; } debug() { local level=${1}; shift; @@ -144,8 +139,8 @@ lxc.cgroup.devices.allow = c 10:232 rwm EOF } -insert_ds() { - local root_d="$1" dstype="$2" authkey="$3" userdata="$4" +insert_ds_nocloud() { + local root_d="$1" authkey="$2" udfile="$3" local sdir="$root_d/var/lib/cloud/seed/nocloud" mkdir -p "$sdir" || @@ -158,9 +153,19 @@ insert_ds() { ${authkeys:+"public-keys=${authkeys}"} > "$sdir/meta-data" || { error "failed to write metadata to $sdir/meta-data"; return 1; } - [ -z "$userdata" ] || - echo "$userdata" > "$sdir/user-data" || - { error "failed to write user-data to $sdir"; return 1; } + if [ -n "$udfile" ]; then + cat "$udfile" > "$sdir/user-data" || + { error "failed to write user-data to $sdir"; return 1; } + else + rm -f "$sdir/user-data" + fi +} + +insert_ds() { + local dstype="$1" root_d="$2" authkey="$3" udfile="$4" + case "$dstype" in + nocloud) insert_ds_nocloud "$root_d" "$authkey" "$udfile" + esac } extract_rootfs() { @@ -187,6 +192,7 @@ download_tarball() { mkdir -p "${outd}" || { error "failed to create ${outd}"; return 1; } + debug 1 "downloading ${baseurl%/}/$dlpath" to "${cached}/$dlpath" wget "${baseurl%/}/$dlpath" -O "$cached/${dlpath}.$$" && mv "$cached/$dlpath.$$" "$cached/$dlpath" || { rm -f "$cached/$dlpath.$$"; @@ -196,93 +202,118 @@ download_tarball() { _RET="$cached/$dlpath" } -short_opts="a:n:p:S:huV" -long_opts="arch:,auth-key:,name:,path:,userdata:,verbose,version:" -getopt_out=$(getopt --name "${0##*/}" \ - --options "${short_opts}" --long "${long_opts}" -- "$@") && - eval set -- "${getopt_out}" || - bad_Usage +create_main() { + local short_opts="a:hn:p:S:uvV" + local long_opts="arch:,auth-key:,name:,path:,tarball:,userdata:,verbose,version:" + local getopt_out="" + getopt_out=$(getopt --name "${0##*/}" \ + --options "${short_opts}" --long "${long_opts}" -- "$@") && + eval set -- "${getopt_out}" || + { bad_Usage; return; } -arch="${DEF_ARCH}" -version="${DEF_VERSION}" -authkey_f="" -authkeys="" -userdata="" -seed=true -path="" + local arch="${DEF_ARCH}" dsource="${DEF_SOURCE}" version="${DEF_VERSION}" + local authkey_f="" authkeys="" userdata_f="" path="" tarball="" + local cur="" next="" -while [ $# -ne 0 ]; do - cur=$1; next=$2; - case "$cur" in - -a|--arch) arch="$next"; shift;; - -h|--help) Usage ; exit 0;; - -n|--name) name="$next"; shift;; - -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));; - -S|--auth-key) authkey_f="$next"; shift;; - -p|--path) path=$next; shift;; - -v|--version) version=$next; shift;; - -u|--userdata) userdata="$next"; shift;; - --) shift; break;; - esac - shift; -done + while [ $# -ne 0 ]; do + cur=$1; next=$2; + case "$cur" in + -a|--arch) arch="$next"; shift;; + -h|--help) Usage ; return 0;; + -n|--name) name="$next"; shift;; + -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));; + -S|--auth-key) authkey_f="$next"; shift;; + -p|--path) path=$next; shift;; + -v|--version) version=$next; shift;; + -u|--userdata) userdata_f="$next"; shift;; + --tarball) tarball="$next"; shift;; + --source) dsource="$next"; shift;; + --) shift; break;; + esac + shift; + done -[ $# -eq 0 ] || bad_Usage "unexpected arguments: $*" -[ -n "$path" ] || fail "'path' parameter is required" + [ $# -eq 0 ] || { bad_Usage "unexpected arguments: $*"; return; } + [ -n "$path" ] || { error "'path' parameter is required"; return 1; } -if [ "$(id -u)" != "0" ]; then - fail "must be run as root" -fi - -case "$arch" in - i?86) arch="i386";; - amd64) arch="x86_65";; -esac - -inargs "$arch" "${ARCHES[@]}" || - fail "bad arch '$arch'. allowed: ${ARCHES[*]}" - -if inargs "$version" "${STREAMS[@]}"; then - out=$(wget -O - -q "${DOWNLOAD_URL%/}/version/$version") || - fail "failed to convert 'version=$version'" - version="$out" -fi - -if [ -n "$authkey_f" ]; then - if [ ! -f "$authkey_f" ]; then - echo "--auth-key=${authkey_f} must reference a file" - exit 1 + if [ "$(id -u)" != "0" ]; then + { error "must be run as root"; return 1; } fi - authkeys=$(cat "$authkey") -fi -trap cleanup EXIT + case "$arch" in + i?86) arch="i386";; + amd64) arch="x86_64";; + esac -download_tarball "$arch" "$version" "${CACHE_D}" "${DOWNLOAD_URL}" || fail -tarball="$_RET" + inargs "$arch" "${ARCHES[@]}" || + { error "bad arch '$arch'. allowed: ${ARCHES[*]}"; return 1; } -rootfs_d="$path/rootfs" -extract_rootfs "${tarball}" "${rootfs_d}" || fail + inargs "$dsource" "${SOURCES[@]}" || + { error "bad source '$dsource'. allowed: ${SOURCES[*]}"; return 1; } -# oopsie - some cirros tarballs ship random as a blockdev, breaking -# dropbear. -if [ -b ${rootfs_d}/dev/random ]; then - rm -f ${rootfs_d}/dev/random - rm -f ${rootfs_d}/dev/urandom - mknod ${rootfs_d}/dev/random c 1 8 - mknod ${rootfs_d}/dev/urandom c 1 9 - chmod 777 ${rootfs_d}/dev/random ${rootfs_d}/dev/urandom -fi + if [ "$dsource" = "none" ] && [ -n "$userdata_f" -o -n "$authkey_f" ]; then + error "userdata and authkey are incompatible with --source=none"; + return 1; + fi -if false; then - insert_ds "$path/rootfs" "nocloud" "$authkeys" "$userdata" || - fail "failed to insert userdata to $path/rootfs" -else - # disable ec2, because its annoying - sed -i 's,ec2,,' "$path/rootfs/etc/cirros-init/config" -fi + if [ -n "$authkey_f" ]; then + if [ ! -f "$authkey_f" ]; then + error "--auth-key=${authkey_f} must reference a file" + return 1 + fi + authkeys=$(cat "$authkey_f") || + { error "failed to read ${authkey_f}"; return 1; } + fi -copy_configuration "$path" "$path/rootfs" "$name" "$arch" "$release" + if [ -n "$userdata_f" -a ! -f "${userdata_f}" ]; then + error "${userdata_f}: --userdata arg not a file" + return 1 + fi + if [ -z "$tarball" ]; then + if inargs "$version" "${STREAMS[@]}"; then + out=$(wget -O - -q "${DOWNLOAD_URL%/}/version/$version") || + { error "failed to convert 'version=$version'"; return 1; } + version="$out" + fi + download_tarball "$arch" "$version" "${CACHE_D}" "${DOWNLOAD_URL}" || + return + tarball="$_RET" + fi + + local rootfs_d="$path/rootfs" + extract_rootfs "${tarball}" "${rootfs_d}" || return + + # cirros 0.3.1 was broken for /dev/random and /dev/urandom + if [ -b "$rootfs_d/dev/random" ]; then + rm -f "$rootfs_d/dev/random" && + mknod --mode=666 "$rootfs_d/dev/random" c 1 8 || + { error "failed to fix /dev/random"; return 1; } + fi + if [ -b "$rootfs_d/dev/urandom" ]; then + rm -f "$rootfs_d/dev/urandom" && + mknod --mode=666 "$rootfs_d/dev/urandom" c 1 9 || + { error "failed to fix /dev/urandom"; return 1; } + fi + + if [ "$version" = "0.3.2~pre1" ]; then + debug 1 "fixing console for lxc and '$version'" + sed -i 's,^\(#console.* 115200 \)# /dev/console,\1 console,g' \ + "$rootfs_d/etc/inittab" || + { error "failed to fix console entry for $version"; return 1; } + fi + + if [ "$dsource" != "none" ]; then + insert_ds "$dsource" "$path/rootfs" "$authkeys" "$userdata_f" || { + error "failed to insert userdata to $path/rootfs" + return 1 + } + fi + + copy_configuration "$path" "$path/rootfs" "$name" "$arch" "$release" + return +} + +create_main "$@" # vi: ts=4 expandtab From 794fb287b3bd7a6c07f99ec1565c517922287065 Mon Sep 17 00:00:00 2001 From: Bogdan Purcareata Date: Wed, 15 May 2013 12:08:14 +0300 Subject: [PATCH 113/325] lxc-busybox: check when bind-mounting host libdirs The patch removes the behavior of automatically mounting /lib and /usr/lib, since this is duplicated a few lines below. It will also remove the risk of failing when one of these entries are not present on the host - e.g. on a 64bit machine. Signed-off-by: Bogdan Purcareata Signed-off-by: Serge Hallyn --- templates/lxc-busybox.in | 7 ------- 1 file changed, 7 deletions(-) diff --git a/templates/lxc-busybox.in b/templates/lxc-busybox.in index 81e9566c5..db39b0e62 100644 --- a/templates/lxc-busybox.in +++ b/templates/lxc-busybox.in @@ -267,13 +267,6 @@ lxc.cap.drop = sys_module mac_admin mac_override sys_time #lxc.aa_profile = unconfined 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 - libdirs="\ lib \ usr/lib \ From 627fe3b4c3a65535eb53c3d63794705d8f6322d4 Mon Sep 17 00:00:00 2001 From: "Michael H. Warfield" Date: Tue, 14 May 2013 17:45:12 -0400 Subject: [PATCH 114/325] lxc-fedora-template: autodev, hostname, ARM archs, Raspberry Pi fixes This took a lot longer for me to get around to it... Sorry. Patch to the lxc-fedora template. I didn't get any further comments from my earlier proposal, weeks ago, and did get one addition based on comments about properly setting the hostname in /etc/hostname, which I've added. I could have broken them into separate patches but most are pretty small and minor. Changes: * Map armv6l and armv7l architectures to "arm" for yum and repos to function properly. * Detect Fedora Remix distros with no "/etc/fedora-release" file (Raspberry Pi) and find proper release versions when "remix" part of the file context. * Change default Fedora container on non-Fedora hosts to Fedora 17. * Added code for autodev for Fedora systemd containers. * Added code to set /etc/hostname for Fedora > 14 (systemd). * Fix a few typos. Regards, Mike -- Michael H. Warfield (AI4NB) | (770) 985-6132 | mhw@WittsEnd.com /\/\|=mhw=|\/\/ | (678) 463-0932 | http://www.wittsend.com/mhw/ NIC whois: MHW9 | An optimist believes we live in the best of all PGP Key: 0x674627FF | possible worlds. A pessimist is sure of it! -- Signed-off-by: Michael H. Warfield Signed-off-by: Serge Hallyn --- templates/lxc-fedora.in | 52 +++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/templates/lxc-fedora.in b/templates/lxc-fedora.in index 59f453be9..710039c5b 100644 --- a/templates/lxc-fedora.in +++ b/templates/lxc-fedora.in @@ -32,12 +32,28 @@ default_path=@LXCPATH@ root_password=root # is this fedora? -[ -f /etc/fedora-release ] && is_fedora=true - -if [ "$arch" = "i686" ]; then - arch=i386 +# Alow for weird remixes like the Raspberry Pi +if [ -e /etc/redhat-release ] +then + fedora_host_ver=$( sed -e '/^Fedora /!d' -e 's/Fedora.*\srelease\s*\([0-9][0-9]*\)\s.*/\1/' < /etc/redhat-release ) + if [ "$fedora_host_ver" != "" ] + then + is_fedora=true + fi fi +# Map a few architectures to their generic Fedora repository archs. +# The two ARM archs are a bit of a guesstimate for the v5 and v6 +# archs. V6 should have hardware floating point (Rasberry Pi). +# The "arm" arch is safer (no hardware floating point). So +# there may be cases where we "get it wrong" for some v6 other +# than RPi. +case "$arch" in +i686) arch=i386 ;; +armv3l|armv4l|armv5l) arch=arm ;; +armv6l|armv7l|armv8l) arch=armhfp ;; +esac + configure_fedora() { @@ -62,9 +78,15 @@ NETWORKING=yes HOSTNAME=${name} EOF + # set hostname on systemd Fedora systems + if [ $release -gt 14 ]; then + echo "${name}" > ${rootfs_path}/etc/hostname + fi + # set minimal hosts cat < $rootfs_path/etc/hosts 127.0.0.1 localhost $name +::1 localhost6.localdomain6 localhost6 EOF dev_path="${rootfs_path}/dev" @@ -254,6 +276,8 @@ lxc.pts = 1024 lxc.mount = $config_path/fstab lxc.cap.drop = sys_module mac_admin mac_override sys_time +lxc.autodev = $auto_dev + # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined @@ -321,7 +345,7 @@ Mandatory args: Optional args: -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. + -R,--release Fedora release for the new container. if the host is Fedora, then it will default to the host's release. -A,--arch NOT USED YET. Define what arch the container will be [i686,x86_64] -h,--help print this help EOF @@ -366,7 +390,7 @@ fi if [ -n "$needed_pkgs" ]; then echo "Missing commands: $needed_pkgs" - echo "Please install these using \"sudo apt-get install $needed_pkgs\"" + echo "Please install these using \"sudo yum install $needed_pkgs\"" exit 1 fi @@ -375,14 +399,22 @@ if [ -z "$path" ]; then fi if [ -z "$release" ]; then - if [ "$is_fedora" ]; then - release=$(cat /etc/fedora-release |awk '/^Fedora/ {print $3}') + if [ "$is_fedora" -a "$fedora_host_ver" ]; then + release=$fedora_host_ver else - echo "This is not a fedora host and release missing, defaulting to 14. use -R|--release to specify release" - release=14 + echo "This is not a fedora host and release missing, defaulting to 17. use -R|--release to specify release" + release=17 fi fi +# Fedora 15 and above run systemd. We need autodev enabled to keep +# systemd from causing problems. +if [ $release -gt 14 ]; then + auto_dev="1" +else + auto_dev="0" +fi + if [ "$(id -u)" != "0" ]; then echo "This script should be run as 'root'" exit 1 From d9e80daf54e15b89b0b08d475b29893be9830be0 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 15 May 2013 13:23:12 -0400 Subject: [PATCH 115/325] doc/lxc.conf minor clarifications Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- doc/lxc.conf.sgml.in | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in index 0a5a52a41..509ca2df0 100644 --- a/doc/lxc.conf.sgml.in +++ b/doc/lxc.conf.sgml.in @@ -65,8 +65,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA The configuration file defines the different system resources to be assigned for the container. At present, the utsname, the - network, the mount points, the root file system and the control - groups are supported. + network, the mount points, the root file system, the user namespace, + and the control groups are supported. @@ -80,7 +80,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Allows to set the architecture for the container. For example, set a 32bits architecture for a container running 32bits - binaries on a 64bits host. That fix the container scripts + binaries on a 64bits host. This fixes the container scripts which rely on the architecture to do some work like downloading the packages. @@ -258,7 +258,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA specify an action to do for the network. - + activates the interface. @@ -448,7 +448,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA If the container is configured with a root filesystem and the inittab file is setup to use the console, you may want to specify - where goes the output of this console. + where the output of this console goes. @@ -471,14 +471,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Console through the ttys - If the container is configured with a root filesystem and the - inittab file is setup to launch a getty on the ttys. This - option will specify the number of ttys to be available for the - container. The number of getty in the inittab file of the - container should not be greater than the number of ttys - specified in this configuration file, otherwise the excess - getty sessions will die and respawn indefinitly giving - annoying messages on the console. + This option is useful if the container is configured with a root + filesystem and the inittab file is setup to launch a getty on the + ttys. The option specifies the number of ttys to be available for + the container. The number of gettys in the inittab file of the + container should not be greater than the number of ttys specified + in this option, otherwise the excess getty sessions will die and + respawn indefinitely giving annoying messages on the console or in + /var/log/messages. @@ -590,13 +590,20 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA specify a file location in the fstab format, containing the - mount informations. If the rootfs is an image file or a - device block and the fstab is used to mount a point + mount information. If the rootfs is an image file or a + block device and the fstab is used to mount a point somewhere in this rootfs, the path of the rootfs mount point should be prefixed with the @LXCROOTFSMOUNT@ default path or the value of if - specified. + specified. Note that when mounting a filesystem from an + image file or block device the third field (fs_vfstype) + cannot be auto as with + + mount + 8 + + but must be explicitly specified. From 6031a6e5f939bda07d98768d34dafae677a7dfeb Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 15 May 2013 12:27:34 -0400 Subject: [PATCH 116/325] set non device cgroup items before the cgroup is entered This allows some special cgroup items such as memory.kmem.limit_in_bytes to be successfully set, since they must be set before any task is put into the cgroup. The devices cgroup is setup later giving the container a chance to mount file systems before the device it might want to mount from becomes unavailable. Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/conf.c | 25 +++++++++++++++++++------ src/lxc/conf.h | 3 +-- src/lxc/start.c | 10 +++++++--- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 746a2dbd7..9877cc362 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1401,7 +1401,8 @@ static int setup_kmsg(const struct lxc_rootfs *rootfs, return 0; } -int setup_cgroup(const char *cgpath, struct lxc_list *cgroups) +static int _setup_cgroup(const char *cgpath, struct lxc_list *cgroups, + int devices) { struct lxc_list *iterator; struct lxc_cgroup *cg; @@ -1411,13 +1412,15 @@ int setup_cgroup(const char *cgpath, struct lxc_list *cgroups) return 0; lxc_list_for_each(iterator, cgroups) { - cg = iterator->elem; - if (lxc_cgroup_set_bypath(cgpath, cg->subsystem, cg->value)) { - ERROR("Error setting %s to %s for %s\n", cg->subsystem, - cg->value, cgpath); - goto out; + if (devices == !strncmp("devices", cg->subsystem, 7)) { + if (lxc_cgroup_set_bypath(cgpath, cg->subsystem, + cg->value)) { + ERROR("Error setting %s to %s for %s\n", + cg->subsystem, cg->value, cgpath); + goto out; + } } DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value); @@ -1429,6 +1432,16 @@ out: return ret; } +int setup_cgroup_devices(const char *cgpath, struct lxc_list *cgroups) +{ + return _setup_cgroup(cgpath, cgroups, 1); +} + +int setup_cgroup(const char *cgpath, struct lxc_list *cgroups) +{ + return _setup_cgroup(cgpath, cgroups, 0); +} + static void parse_mntopt(char *opt, unsigned long *flags, char **data) { struct mount_opt *mo; diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 465b1ece4..8180e1870 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -287,6 +287,7 @@ struct lxc_conf { int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf); extern int setup_cgroup(const char *cgpath, struct lxc_list *cgroups); +extern int setup_cgroup_devices(const char *cgpath, struct lxc_list *cgroups); extern int detect_shared_rootfs(void); /* @@ -313,8 +314,6 @@ extern int lxc_clear_cgroups(struct lxc_conf *c, const char *key); extern int lxc_clear_mount_entries(struct lxc_conf *c); extern int lxc_clear_hooks(struct lxc_conf *c, const char *key); -extern int setup_cgroup(const char *name, struct lxc_list *cgroups); - extern int uid_shift_ttys(int pid, struct lxc_conf *conf); /* diff --git a/src/lxc/start.c b/src/lxc/start.c index fd96d4f60..cf5f9bd12 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -809,6 +809,11 @@ int lxc_spawn(struct lxc_handler *handler) if ((handler->cgroup = lxc_cgroup_path_create(NULL, name)) == NULL) goto out_delete_net; + if (setup_cgroup(handler->cgroup, &handler->conf->cgroup)) { + ERROR("failed to setup the cgroups for '%s'", name); + goto out_delete_net; + } + if (lxc_cgroup_enter(handler->cgroup, handler->pid) < 0) goto out_delete_net; @@ -839,12 +844,11 @@ int lxc_spawn(struct lxc_handler *handler) if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_CONFIGURE)) goto out_delete_net; - if (setup_cgroup(handler->cgroup, &handler->conf->cgroup)) { - ERROR("failed to setup the cgroups for '%s'", name); + if (setup_cgroup_devices(handler->cgroup, &handler->conf->cgroup)) { + ERROR("failed to setup the devices cgroup for '%s'", name); goto out_delete_net; } - /* Tell the child to complete its initialization and wait for * it to exec or return an error. (the child will never * return LXC_SYNC_POST_CGROUP+1. It will either close the From 9a93d99213da44b5ddf2f5295f6ef3a59d4f1fba Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Wed, 15 May 2013 15:21:24 -0500 Subject: [PATCH 117/325] cgroup: prevent DOS when a hierachy is mounted multiple times When starting a container, we walk through all cgroup mounts looking for a unique directory name we can use for this container. If the name we are trying is in use, we try another name. If it is not in use in the first mount we check, we need to check other hierarchies as it may exist there. But we weren't checking whether we have already checked a subsystem - so that if freezer was mounted twice, we would create it in the first mount, see it exists in the second, so start over trying in the second mount. To fix this, keep track of which subsystems we have already checked, and do not re-check. (See http://pad.lv/1176287 for a bug report) Note we still need to add, at the next: label, the removal of the directories we've already created. I'm keeping that for later as it's far lower priority than this fix, and I don't want to risk introducing a regression for that. Signed-off-by: Serge Hallyn --- src/lxc/cgroup.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c index 0c93703d6..a8ae8c1a5 100644 --- a/src/lxc/cgroup.c +++ b/src/lxc/cgroup.c @@ -504,6 +504,103 @@ static void set_clone_children(const char *mntdir) fclose(fout); } +static char *get_all_cgroups(void) +{ + FILE *f; + char *line = NULL, *ret = NULL; + size_t len; + int first = 1; + + /* read the list of subsystems from the kernel */ + f = fopen("/proc/cgroups", "r"); + if (!f) + return NULL; + + while (getline(&line, &len, f) != -1) { + char *c; + int oldlen, newlen, inc; + + /* skip the first line */ + if (first) { + first=0; + continue; + } + + c = strchr(line, '\t'); + if (!c) + continue; + *c = '\0'; + + oldlen = ret ? strlen(ret) : 0; + newlen = oldlen + strlen(line) + 2; + ret = realloc(ret, newlen); + if (!ret) + goto out; + inc = snprintf(ret + oldlen, newlen, ",%s", line); + if (inc < 0 || inc >= newlen) { + free(ret); + ret = NULL; + goto out; + } + } + +out: + fclose(f); + return ret; +} + +static int in_cgroup_list(char *s, char *list) +{ + char *token, *str, *saveptr; + + if (!list || !s) + return 0; + + for (str = strdupa(list); (token = strtok_r(str, ",", &saveptr)); str = NULL) { + if (strcmp(s, token) == 0) + return 1; + } + + return 0; +} + +static int have_visited(char *opts, char *visited, char *allcgroups) +{ + char *str, *s, *token; + + for (str = strdupa(opts); (token = strtok_r(str, ",", &s)); str = NULL) { + if (!in_cgroup_list(token, allcgroups)) + continue; + if (visited && in_cgroup_list(token, visited)) + return 1; + } + + return 0; +} + +static int record_visited(char *opts, char **visitedp, char *allcgroups) +{ + char *s, *token, *str; + int oldlen, newlen, ret; + + for (str = strdupa(opts); (token = strtok_r(str, ",", &s)); str = NULL) { + if (!in_cgroup_list(token, allcgroups)) + continue; + if (*visitedp && in_cgroup_list(token, *visitedp)) + continue; + oldlen = (*visitedp) ? strlen(*visitedp) : 0; + newlen = oldlen + strlen(token) + 2; + (*visitedp) = realloc(*visitedp, newlen); + if (!(*visitedp)) + return -1; + ret = snprintf((*visitedp)+oldlen, newlen, ",%s", token); + if (ret < 0 || ret >= newlen) + return -1; + } + + return 0; +} + /* * Make sure the 'cgroup group' exists, so that we don't have to worry about * that later. @@ -592,16 +689,29 @@ char *lxc_cgroup_path_create(const char *lxcgroup, const char *name) char tail[12]; FILE *file = NULL; struct mntent mntent_r; + char *allcgroups = get_all_cgroups(); + char *visited = NULL; char buf[LARGE_MAXPATHLEN] = {0}; if (create_lxcgroups(lxcgroup) < 0) return NULL; + if (!allcgroups) + return NULL; + again: + if (visited) { + /* we're checking for a new name, so start over with all cgroup + * mounts */ + free(visited); + visited = NULL; + } file = setmntent(MTAB, "r"); if (!file) { SYSERROR("failed to open %s", MTAB); + if (allcgroups) + free(allcgroups); return NULL; } @@ -617,6 +727,12 @@ again: if (!mount_has_subsystem(&mntent_r)) continue; + /* make sure we haven't checked this subsystem already */ + if (have_visited(mntent_r.mnt_opts, visited, allcgroups)) + continue; + if (record_visited(mntent_r.mnt_opts, &visited, allcgroups) < 0) + goto fail; + /* find unused mnt_dir + lxcgroup + name + -$i */ ret = snprintf(path, MAXPATHLEN, "%s/%s/%s%s", mntent_r.mnt_dir, lxcgroup ? lxcgroup : "lxc", name, tail); @@ -641,6 +757,9 @@ again: goto fail; retpath = strdup(path); + free(allcgroups); + if (visited) + free(visited); return retpath; @@ -651,6 +770,9 @@ next: fail: endmntent(file); + free(allcgroups); + if (visited) + free(visited); return NULL; } From 148e91f56799f03c868deca8dcad473983a1a2bf Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 14 May 2013 16:10:37 -0500 Subject: [PATCH 118/325] lxc: add clone hook. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a clone hook called from api_clone. Pass arguments to it from lxc_clone.c. The clone update hook is called while the container's bdev is mounted. Information about the container is passed in through environment variables LXC_ROOTFS_PATH, LXC_NAME, The LXC_ROOTFS_MOUNT, and LXC_CONFIG_FILE. LXC_ROOTFS_MOUNT=/usr/lib/x86_64-linux-gnu/lxc LXC_CONFIG_FILE=/var/lib/lxc/demo3/config LXC_ROOTFS_PATH=/var/lib/lxc/demo3/rootfs LXC_NAME=demo3 So from the hook, updates to the container should be made under $LXC_ROOTFS_MOUNT/ . The hook also receives command line arguments as follows: First argument is container name, second is always 'lxc', third is the hook name (always clone), then come the arguments which were passed to lxc-clone. I.e. when I did: sudo lxc-clone demo2 demo3 -- hey there dude the arguments passed in were "demo3 lxc clone hey there dude" I personally would like to drop the first two arguments. The name is available as $LXC_NAME, and the section argument ('lxc') is meaningless. However, doing so risks invalidating existing hooks. Soon analogous create and destroy hooks will be added as well. Signed-off-by: Serge Hallyn Acked-by: Stéphane Graber --- src/lxc/conf.c | 63 ++++++++++++++++++++++++++++++++++++++---- src/lxc/conf.h | 4 +-- src/lxc/confile.c | 3 ++ src/lxc/lxc_clone.c | 16 +++++------ src/lxc/lxccontainer.c | 55 +++++++++++++++++++++++++----------- src/lxc/lxccontainer.h | 2 +- src/lxc/start.c | 6 ++-- 7 files changed, 113 insertions(+), 36 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 9877cc362..a38015df9 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -173,7 +173,7 @@ return -1; #endif char *lxchook_names[NUM_LXC_HOOKS] = { - "pre-start", "pre-mount", "mount", "autodev", "start", "post-stop" }; + "pre-start", "pre-mount", "mount", "autodev", "start", "post-stop", "clone" }; typedef int (*instanciate_cb)(struct lxc_handler *, struct lxc_netdev *); @@ -336,6 +336,55 @@ static int run_buffer(char *buffer) return 0; } +static int run_script_argv(const char *name, const char *section, + const char *script, const char *hook, char **argsin) +{ + int ret, i; + char *buffer; + size_t size = 0; + + INFO("Executing script '%s' for container '%s', config section '%s'", + script, name, section); + + for (i=0; argsin && argsin[i]; i++) + size += strlen(argsin[i]) + 1; + + size += strlen(hook) + 1; + + size += strlen(script); + size += strlen(name); + size += strlen(section); + size += 3; + + if (size > INT_MAX) + return -1; + + buffer = alloca(size); + if (!buffer) { + ERROR("failed to allocate memory"); + return -1; + } + + ret = snprintf(buffer, size, "%s %s %s %s", script, name, section, hook); + if (ret < 0 || ret >= size) { + ERROR("Script name too long"); + return -1; + } + + for (i=0; argsin && argsin[i]; i++) { + int len = size-ret; + int rc; + rc = snprintf(buffer + ret, len, " %s", argsin[i]); + if (rc < 0 || rc >= len) { + ERROR("Script args too long"); + return -1; + } + ret += rc; + } + + return run_buffer(buffer); +} + static int run_script(const char *name, const char *section, const char *script, ...) { @@ -2765,7 +2814,7 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf) return -1; } - if (run_lxc_hooks(name, "pre-mount", lxc_conf)) { + if (run_lxc_hooks(name, "pre-mount", lxc_conf, NULL)) { ERROR("failed to run pre-mount hooks for container '%s'.", name); return -1; } @@ -2792,13 +2841,13 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf) return -1; } - if (run_lxc_hooks(name, "mount", lxc_conf)) { + if (run_lxc_hooks(name, "mount", lxc_conf, NULL)) { ERROR("failed to run mount hooks for container '%s'.", name); return -1; } if (lxc_conf->autodev) { - if (run_lxc_hooks(name, "autodev", lxc_conf)) { + if (run_lxc_hooks(name, "autodev", lxc_conf, NULL)) { ERROR("failed to run autodev hooks for container '%s'.", name); return -1; } @@ -2865,7 +2914,7 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf) return 0; } -int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf) +int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf, char *argv[]) { int which = -1; struct lxc_list *it; @@ -2882,12 +2931,14 @@ int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf) which = LXCHOOK_START; else if (strcmp(hook, "post-stop") == 0) which = LXCHOOK_POSTSTOP; + else if (strcmp(hook, "clone") == 0) + which = LXCHOOK_CLONE; else return -1; lxc_list_for_each(it, &conf->hooks[which]) { int ret; char *hookname = it->elem; - ret = run_script(name, "lxc", hookname, hook, NULL); + ret = run_script_argv(name, "lxc", hookname, hook, argv); if (ret) return ret; } diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 8180e1870..a2efa7c2b 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -237,7 +237,7 @@ struct lxc_rootfs { */ enum lxchooks { LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_AUTODEV, - LXCHOOK_START, LXCHOOK_POSTSTOP, NUM_LXC_HOOKS}; + LXCHOOK_START, LXCHOOK_POSTSTOP, LXCHOOK_CLONE, NUM_LXC_HOOKS}; extern char *lxchook_names[NUM_LXC_HOOKS]; struct saved_nic { @@ -284,7 +284,7 @@ struct lxc_conf { char *rcfile; // Copy of the top level rcfile we read }; -int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf); +int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf, char *argv[]); extern int setup_cgroup(const char *cgpath, struct lxc_list *cgroups); extern int setup_cgroup_devices(const char *cgpath, struct lxc_list *cgroups); diff --git a/src/lxc/confile.c b/src/lxc/confile.c index aec817769..fb851c98b 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -117,6 +117,7 @@ static struct lxc_config_t config[] = { { "lxc.hook.autodev", config_hook }, { "lxc.hook.start", config_hook }, { "lxc.hook.post-stop", config_hook }, + { "lxc.hook.clone", config_hook }, { "lxc.network.type", config_network_type }, { "lxc.network.flags", config_network_flags }, { "lxc.network.link", config_network_link }, @@ -894,6 +895,8 @@ static int config_hook(const char *key, const char *value, return add_hook(lxc_conf, LXCHOOK_START, copy); else if (strcmp(key, "lxc.hook.post-stop") == 0) return add_hook(lxc_conf, LXCHOOK_POSTSTOP, copy); + else if (strcmp(key, "lxc.hook.clone") == 0) + return add_hook(lxc_conf, LXCHOOK_CLONE, copy); SYSERROR("Unknown key: %s", key); free(copy); return -1; diff --git a/src/lxc/lxc_clone.c b/src/lxc/lxc_clone.c index b9c7a6eb7..2b0ee435c 100644 --- a/src/lxc/lxc_clone.c +++ b/src/lxc/lxc_clone.c @@ -16,7 +16,7 @@ lxc_log_define(lxc_clone, lxc); -void usage(char *me) +void usage(const char *me) { printf("Usage: %s [-s] [-B backingstore] [-L size] [-K] [-M] [-H]\n", me); printf(" [-p lxcpath] [-P newlxcpath] orig new\n"); @@ -60,6 +60,7 @@ int main(int argc, char *argv[]) long newsize = 0; char *bdevtype = NULL, *lxcpath = NULL, *newpath = NULL, *fstype = NULL; char *orig = NULL, *new = NULL, *vgname = NULL; + char **args = NULL; char c; if (argc < 3) @@ -86,14 +87,13 @@ int main(int argc, char *argv[]) default: break; } } - if (optind == argc-2 && !orig) + if (optind < argc && !orig) orig = argv[optind++]; - if (optind == argc-1 && !new) + if (optind < argc && !new) new = argv[optind++]; - if (optind < argc) { - printf("%d extraneous arguments\n", argc-optind); - usage(argv[0]); - } + if (optind < argc) + /* arguments for the clone hook */ + args = &argv[optind]; if (!new || !orig) { printf("Error: you must provide orig and new names\n"); usage(argv[0]); @@ -124,7 +124,7 @@ int main(int argc, char *argv[]) lxc_container_put(c1); exit(1); } - c2 = c1->clone(c1, new, newpath, flags, bdevtype, NULL, newsize); + c2 = c1->clone(c1, new, newpath, flags, bdevtype, NULL, newsize, args); if (c2 == NULL) { lxc_container_put(c1); fprintf(stderr, "clone failed\n"); diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index bac94c234..23db6f203 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -1364,16 +1364,14 @@ static int copy_storage(struct lxc_container *c0, struct lxc_container *c, return 0; } -static int clone_update_rootfs(struct lxc_container *c, int flags) +static int clone_update_rootfs(struct lxc_container *c, int flags, char **hookargs) { int ret = -1; char path[MAXPATHLEN]; struct bdev *bdev; FILE *fout; pid_t pid; - - if (flags & LXC_CLONE_KEEPNAME) - return 0; + struct lxc_conf *conf = c->lxc_conf; /* update hostname in rootfs */ /* we're going to mount, so run in a clean namespace to simplify cleanup */ @@ -1393,17 +1391,41 @@ static int clone_update_rootfs(struct lxc_container *c, int flags) exit(1); if (bdev->ops->mount(bdev) < 0) exit(1); - ret = snprintf(path, MAXPATHLEN, "%s/etc/hostname", bdev->dest); - if (ret < 0 || ret >= MAXPATHLEN) - exit(1); - if (!(fout = fopen(path, "w"))) { - SYSERROR("unable to open %s: ignoring\n", path); - exit(0); + + if (!lxc_list_empty(&conf->hooks[LXCHOOK_CLONE])) { + /* Start of environment variable setup for hooks */ + if (setenv("LXC_NAME", c->name, 1)) { + SYSERROR("failed to set environment variable for container name"); + } + if (setenv("LXC_CONFIG_FILE", conf->rcfile, 1)) { + SYSERROR("failed to set environment variable for config path"); + } + if (setenv("LXC_ROOTFS_MOUNT", conf->rootfs.mount, 1)) { + SYSERROR("failed to set environment variable for rootfs mount"); + } + if (setenv("LXC_ROOTFS_PATH", conf->rootfs.path, 1)) { + SYSERROR("failed to set environment variable for rootfs mount"); + } + + if (run_lxc_hooks(c->name, "clone", conf, hookargs)) { + ERROR("Error executing clone hook for %s", c->name); + exit(1); + } + } + + if (!(flags & LXC_CLONE_KEEPNAME)) { + ret = snprintf(path, MAXPATHLEN, "%s/etc/hostname", bdev->dest); + if (ret < 0 || ret >= MAXPATHLEN) + exit(1); + if (!(fout = fopen(path, "w"))) { + SYSERROR("unable to open %s: ignoring\n", path); + exit(0); + } + if (fprintf(fout, "%s", c->name) < 0) + exit(1); + if (fclose(fout) < 0) + exit(1); } - if (fprintf(fout, "%s", c->name) < 0) - exit(1); - if (fclose(fout) < 0) - exit(1); exit(0); } @@ -1436,7 +1458,8 @@ static int create_file_dirname(char *path) struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname, const char *lxcpath, int flags, - const char *bdevtype, const char *bdevdata, unsigned long newsize) + const char *bdevtype, const char *bdevdata, unsigned long newsize, + char **hookargs) { struct lxc_container *c2 = NULL; char newpath[MAXPATHLEN]; @@ -1525,7 +1548,7 @@ struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname, if (!c2->save_config(c2, NULL)) goto out; - if (clone_update_rootfs(c2, flags) < 0) + if (clone_update_rootfs(c2, flags, hookargs) < 0) goto out; // TODO: update c's lxc.snapshot = count diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index b6bd97c2e..67fbed412 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -105,7 +105,7 @@ struct lxc_container { */ struct lxc_container *(*clone)(struct lxc_container *c, const char *newname, const char *lxcpath, int flags, const char *bdevtype, - const char *bdevdata, unsigned long newsize); + const char *bdevdata, unsigned long newsize, char **hookargs); #if 0 bool (*commit_cgroups)(struct lxc_container *c); diff --git a/src/lxc/start.c b/src/lxc/start.c index cf5f9bd12..692de4459 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -461,7 +461,7 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char } /* End of environment variable setup for hooks */ - if (run_lxc_hooks(name, "pre-start", conf)) { + if (run_lxc_hooks(name, "pre-start", conf, NULL)) { ERROR("failed to run pre-start hooks for container '%s'.", name); goto out_aborting; } @@ -513,7 +513,7 @@ static void lxc_fini(const char *name, struct lxc_handler *handler) lxc_set_state(name, handler, STOPPING); lxc_set_state(name, handler, STOPPED); - if (run_lxc_hooks(name, "post-stop", handler->conf)) + if (run_lxc_hooks(name, "post-stop", handler->conf, NULL)) ERROR("failed to run post-stop hooks for container '%s'.", name); /* reset mask set by setup_signal_fd */ @@ -676,7 +676,7 @@ static int do_start(void *data) if (lxc_seccomp_load(handler->conf) != 0) goto out_warn_father; - if (run_lxc_hooks(handler->name, "start", handler->conf)) { + if (run_lxc_hooks(handler->name, "start", handler->conf, NULL)) { ERROR("failed to run start hooks for container '%s'.", handler->name); goto out_warn_father; } From dc92f6c7eec81dc104b3f7873ffd74ec56a1dae1 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Thu, 16 May 2013 08:22:41 -0500 Subject: [PATCH 119/325] document clone hooks Signed-off-by: Serge Hallyn --- doc/lxc-clone.sgml.in | 18 ++++++++++++++++++ doc/lxc.conf.sgml.in | 20 +++++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/doc/lxc-clone.sgml.in b/doc/lxc-clone.sgml.in index 3ad769880..2d2fda328 100644 --- a/doc/lxc-clone.sgml.in +++ b/doc/lxc-clone.sgml.in @@ -60,6 +60,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -P newlxcpath -o orig -n new + -- hook arguments lxc-clone @@ -73,6 +74,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -P newlxcpath orig new + -- hook arguments @@ -242,6 +244,22 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Clone hook + + If the container being cloned has one or more lxc.hook.clone + specified, then the specified hooks will be called for the new container. The + first 3 arguments passed to the clone hook will be the container name, a section + ('lxc'), and the hook type ('clone'). Extra arguments passed + lxc-clone will be passed to the hook program starting at + argument 4. The LXC_ROOTFS_MOUNT environment variable gives + the path under which the container's root filesystem is mounted. The + configuration file pathname is stored in LXC_CONFIG_FILE, the + container name in LXC_NAME, and the path or device on which + the rootfs is located is in LXC_ROOTFS_PATH. + + + &seealso; diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in index 509ca2df0..af8663add 100644 --- a/doc/lxc.conf.sgml.in +++ b/doc/lxc.conf.sgml.in @@ -770,9 +770,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Startup hooks + Container hooks - Startup hooks are programs or scripts which can be executed + Container hooks are programs or scripts which can be executed at various times in a container's lifetime. @@ -866,10 +866,24 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + + + + + A hook to be run when the container is cloned to a new one. + See lxc-clone + 1 for more information. + + + + - Startup hooks Environment Variables + Container hooks Environment Variables A number of environment variables are made available to the startup hooks to provide configuration information and assist in the From de09eccbeda214a1ef5a9b7144870defa97e88c4 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 17 May 2013 11:25:15 -0500 Subject: [PATCH 120/325] lxc-create: zfs: consistently use zfsroot, not zfs_root 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 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in index d85450157..439ce51ef 100644 --- a/src/lxc/lxc-create.in +++ b/src/lxc/lxc-create.in @@ -32,7 +32,7 @@ verify_btrfs() { } verify_zfs() { - if which zfs >/dev/null 2>&1 && zfs get all "$zfs_root" >/dev/null 2>&1; then + if which zfs >/dev/null 2>&1 && zfs get all "$zfsroot" >/dev/null 2>&1; then echo zfs else echo no @@ -198,7 +198,7 @@ while [ $# -gt 0 ]; do ;; --zfsroot) optarg_check $opt "$1" - zfs_root=$1 + zfsroot=$1 shift ;; --) @@ -248,8 +248,8 @@ if [ -z "$lvname" ]; then lvname="$lxc_name" fi -if [ -z "$zfs_root" ]; then - zfs_root="tank/lxc" +if [ -z "$zfsroot" ]; then + zfsroot="tank/lxc" fi if [ "$(id -u)" != "0" ]; then @@ -271,7 +271,7 @@ case "$backingstore" in ;; zfs) if [ `verify_zfs` != 'zfs' ]; then - echo "missing 'zfs' command or $zfs_root is not zfs" >&2 + echo "missing 'zfs' command or $zfsroot is not zfs" >&2 exit 1 fi ;; @@ -313,7 +313,7 @@ cleanup() { elif [ "$backingstore" = "btrfs" ]; then btrfs subvolume delete "$rootfs" || true elif [ "$backingstore" = "zfs" ]; then - zfs destroy "$zfs_root/$lxc_name" || true + zfs destroy "$zfsroot/$lxc_name" || true fi ${bindir}/lxc-destroy -n $lxc_name -P "$lxc_path" @@ -325,7 +325,7 @@ trap cleanup HUP INT TERM # set up container dir per backing store if [ "$backingstore" = "zfs" ]; then - zfs create -omountpoint=$lxc_path/$lxc_name/rootfs "$zfs_root/$lxc_name" + zfs create -omountpoint=$lxc_path/$lxc_name/rootfs "$zfsroot/$lxc_name" elif [ "$backingstore" = "btrfs" ]; then mkdir "$lxc_path/$lxc_name" if ! out=$(btrfs subvolume create "$rootfs" 2>&1); then From 40650ea6817286a9587a84bf3ce5d25d10620303 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Fri, 17 May 2013 17:40:12 -0400 Subject: [PATCH 121/325] quiet gcc 4.4.7 warning about saveptr use before initialization Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/cgroup.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c index a8ae8c1a5..c2f0a2da4 100644 --- a/src/lxc/cgroup.c +++ b/src/lxc/cgroup.c @@ -551,7 +551,7 @@ out: static int in_cgroup_list(char *s, char *list) { - char *token, *str, *saveptr; + char *token, *str, *saveptr = NULL; if (!list || !s) return 0; @@ -566,7 +566,7 @@ static int in_cgroup_list(char *s, char *list) static int have_visited(char *opts, char *visited, char *allcgroups) { - char *str, *s, *token; + char *str, *s = NULL, *token; for (str = strdupa(opts); (token = strtok_r(str, ",", &s)); str = NULL) { if (!in_cgroup_list(token, allcgroups)) @@ -580,7 +580,7 @@ static int have_visited(char *opts, char *visited, char *allcgroups) static int record_visited(char *opts, char **visitedp, char *allcgroups) { - char *s, *token, *str; + char *s = NULL, *token, *str; int oldlen, newlen, ret; for (str = strdupa(opts); (token = strtok_r(str, ",", &s)); str = NULL) { From 11029c023a12dbe3f3569fcc22f25667686e417f Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Fri, 17 May 2013 18:28:12 -0400 Subject: [PATCH 122/325] return lxc generated name for veth pair Doing a get_config_item for lxc.network.0.veth.pair only returns the pair name if explicitly given, but it can be useful to know the name even if it is the one that lxc autogenerated. Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/confile.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lxc/confile.c b/src/lxc/confile.c index fb851c98b..a7db1178a 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -1705,8 +1705,12 @@ static int lxc_get_item_nic(struct lxc_conf *c, char *retv, int inlen, 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); + if (netdev->type == LXC_NET_VETH) { + strprint(retv, inlen, "%s", + netdev->priv.veth_attr.pair ? + netdev->priv.veth_attr.pair : + netdev->priv.veth_attr.veth1); + } } else if (strcmp(p1, "vlan") == 0) { if (netdev->type == LXC_NET_VLAN) { strprint(retv, inlen, "%d", netdev->priv.vlan_attr.vid); From d1240f0335e0c469b850da467661dfbb8f262727 Mon Sep 17 00:00:00 2001 From: "Michael H. Warfield" Date: Mon, 20 May 2013 12:04:38 -0400 Subject: [PATCH 123/325] lxc-fedora template - systemd console gettys Hey all... Patch to the lxc-fedora template to setup gettys on the ttys that are enabled in the configuration. The area of the code already had some modifications to that service that didn't seem to do anything and would get wiped out by an update. I commented that out but subsumed the change it was attempting into my command in case it does something on another rev somewhere. This is very similar to the logic in the OpenSuse template but doesn't seem to appear in other templates, such as arch, which have to deal with systemd. This isn't unique to Fedora. The templates for Fedora, ArchLinux, and OpenSuse are the only three that seem to have any reference to systemd at all. Attached below the jump. Regards, Mike -- Michael H. Warfield (AI4NB) | (770) 985-6132 | mhw@WittsEnd.com /\/\|=mhw=|\/\/ | (678) 463-0932 | http://www.wittsend.com/mhw/ NIC whois: MHW9 | An optimist believes we live in the best of all PGP Key: 0x674627FF | possible worlds. A pessimist is sure of it! -- Signed-off-by: Michael H. Warfield Signed-off-by: Serge Hallyn --- templates/lxc-fedora.in | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/templates/lxc-fedora.in b/templates/lxc-fedora.in index 710039c5b..481f7187b 100644 --- a/templates/lxc-fedora.in +++ b/templates/lxc-fedora.in @@ -143,7 +143,24 @@ configure_fedora_systemd() chroot ${rootfs_path} ln -s /dev/null //etc/systemd/system/udev.service chroot ${rootfs_path} ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target #dependency on a device unit fails it specially that we disabled udev - sed -i 's/After=dev-%i.device/After=/' ${rootfs_path}/lib/systemd/system/getty\@.service + # sed -i 's/After=dev-%i.device/After=/' ${rootfs_path}/lib/systemd/system/getty\@.service + # + # Actually, the After=dev-%i.device line does not appear in the + # Fedora 17 or Fedora 18 systemd getty\@.service file. It may be left + # over from an earlier version and it's not doing any harm. We do need + # to disable the "ConditionalPathExists=/dev/tty0" line or no gettys are + # started on the ttys in the container. Lets do it in an override copy of + # the service so it can still pass rpm verifies and not be automatically + # updated by a new systemd version. -- mhw /\/\|=mhw=|\/\/ + + sed -e 's/^ConditionPathExists=/# ConditionPathExists=/' \ + -e 's/After=dev-%i.device/After=/' \ + < ${rootfs_path}/lib/systemd/system/getty\@.service \ + > ${rootfs_path}/etc/systemd/system/getty\@.service + # Setup getty service on the 4 ttys we are going to allow in the + # default config. Number should match lxc.tty + ( cd ${rootfs_path}/etc/systemd/system/getty.target.wants + for i in 1 2 3 4 ; do ln -sf ../getty\@.service getty@tty${i}.service; done ) } download_fedora() From 71b9b8ed262e2d826181bfb79e5d5075ff1a3ff0 Mon Sep 17 00:00:00 2001 From: Christian Seiler Date: Mon, 20 May 2013 17:54:20 +0200 Subject: [PATCH 124/325] wait_for_pid: Fix EINTR check Signed-off-by: Christian Seiler Signed-off-by: Serge Hallyn --- src/lxc/utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/utils.c b/src/lxc/utils.c index be1ce8860..cf42c385c 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -255,7 +255,7 @@ int wait_for_pid(pid_t pid) again: ret = waitpid(pid, &status, 0); if (ret == -1) { - if (errno == -EINTR) + if (errno == EINTR) goto again; return -1; } From c797a220d51d2796355fd60eca50523ffd6fb45e Mon Sep 17 00:00:00 2001 From: Christian Seiler Date: Mon, 20 May 2013 17:54:21 +0200 Subject: [PATCH 125/325] utils.c: Add lxc_wait_for_pid_status routine that returns exit code Signed-off-by: Christian Seiler Signed-off-by: Serge Hallyn --- src/lxc/utils.c | 16 ++++++++++++++++ src/lxc/utils.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/src/lxc/utils.c b/src/lxc/utils.c index cf42c385c..66bd19d09 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -265,3 +265,19 @@ again: return -1; return 0; } + +int lxc_wait_for_pid_status(pid_t pid) +{ + int status, ret; + +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == EINTR) + goto again; + return -1; + } + if (ret != pid) + goto again; + return status; +} diff --git a/src/lxc/utils.h b/src/lxc/utils.h index 09af34ac3..be1a8a8c3 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -23,6 +23,8 @@ #ifndef _utils_h #define _utils_h +#include + extern int lxc_setup_fs(void); extern int get_u16(unsigned short *val, const char *arg, int base); extern int mkdir_p(const char *dir, mode_t mode); @@ -64,5 +66,6 @@ extern int __build_bug_on_failed; * wait on a child we forked */ extern int wait_for_pid(pid_t pid); +extern int lxc_wait_for_pid_status(pid_t pid); #endif From 65fbbb0a0f7bad119aa5f2ac6f3ee041970889fc Mon Sep 17 00:00:00 2001 From: Christian Seiler Date: Mon, 20 May 2013 17:54:22 +0200 Subject: [PATCH 126/325] Move declarations of some constants to where they are needed. Signed-off-by: Christian Seiler Signed-off-by: Serge Hallyn --- src/lxc/bdev.h | 26 ++++++++++++++++++++++++++ src/lxc/conf.c | 24 ------------------------ 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/lxc/bdev.h b/src/lxc/bdev.h index cc0359250..d69efd8a7 100644 --- a/src/lxc/bdev.h +++ b/src/lxc/bdev.h @@ -54,4 +54,30 @@ struct bdev *bdev_copy(const char *src, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, const char *bdevtype, int snap, const char *bdevdata, unsigned long newsize); void bdev_put(struct bdev *bdev); + +/* define constants if the kernel/glibc headers don't define them */ +#ifndef MS_DIRSYNC +#define MS_DIRSYNC 128 +#endif + +#ifndef MS_REC +#define MS_REC 16384 +#endif + +#ifndef MNT_DETACH +#define MNT_DETACH 2 +#endif + +#ifndef MS_SLAVE +#define MS_SLAVE (1<<19) +#endif + +#ifndef MS_RELATIME +#define MS_RELATIME (1 << 21) +#endif + +#ifndef MS_STRICTATIME +#define MS_STRICTATIME (1 << 24) +#endif + #endif diff --git a/src/lxc/conf.c b/src/lxc/conf.c index a38015df9..a1aee141f 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -93,30 +93,6 @@ lxc_log_define(lxc_conf, lxc); #define MAXMTULEN 16 #define MAXLINELEN 128 -#ifndef MS_DIRSYNC -#define MS_DIRSYNC 128 -#endif - -#ifndef MS_REC -#define MS_REC 16384 -#endif - -#ifndef MNT_DETACH -#define MNT_DETACH 2 -#endif - -#ifndef MS_SLAVE -#define MS_SLAVE (1<<19) -#endif - -#ifndef MS_RELATIME -#define MS_RELATIME (1 << 21) -#endif - -#ifndef MS_STRICTATIME -#define MS_STRICTATIME (1 << 24) -#endif - #if HAVE_SYS_CAPABILITY_H #ifndef CAP_SETFCAP #define CAP_SETFCAP 31 From 92f023dccced28a55ce323253f298e9825fe7da7 Mon Sep 17 00:00:00 2001 From: Christian Seiler Date: Mon, 20 May 2013 17:54:23 +0200 Subject: [PATCH 127/325] Implement simple utility functions for reading and writing to fds Signed-off-by: Christian Seiler Signed-off-by: Serge Hallyn --- src/lxc/utils.c | 35 +++++++++++++++++++++++++++++++++++ src/lxc/utils.h | 5 +++++ 2 files changed, 40 insertions(+) diff --git a/src/lxc/utils.c b/src/lxc/utils.c index 66bd19d09..cd35e0006 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -281,3 +281,38 @@ again: goto again; return status; } + +int lxc_write_nointr(int fd, const void* buf, size_t count) +{ + int ret; +again: + ret = write(fd, buf, count); + if (ret < 0 && errno == EINTR) + goto again; + return ret; +} + +int lxc_read_nointr(int fd, void* buf, size_t count) +{ + int ret; +again: + ret = read(fd, buf, count); + if (ret < 0 && errno == EINTR) + goto again; + return ret; +} + +int lxc_read_nointr_expect(int fd, void* buf, size_t count, const void* expected_buf) +{ + int ret; + ret = lxc_read_nointr(fd, buf, count); + if (ret <= 0) + return ret; + if (ret != count) + return -1; + if (expected_buf && memcmp(buf, expected_buf, count) != 0) { + errno = EINVAL; + return -1; + } + return ret; +} diff --git a/src/lxc/utils.h b/src/lxc/utils.h index be1a8a8c3..d1242b1f5 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -68,4 +68,9 @@ extern int __build_bug_on_failed; extern int wait_for_pid(pid_t pid); extern int lxc_wait_for_pid_status(pid_t pid); +/* send and receive buffers completely */ +extern int lxc_write_nointr(int fd, const void* buf, size_t count); +extern int lxc_read_nointr(int fd, void* buf, size_t count); +extern int lxc_read_nointr_expect(int fd, void* buf, size_t count, const void* expected_buf); + #endif From 9c83a661397456e1455d739bcadfa38f05ce2fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Fri, 26 Apr 2013 16:01:58 +0200 Subject: [PATCH 128/325] lxcapi: Add new get_ips() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a new get_ips call which takes a family (inet, inet6 or NULL), a network interface (or NULL for all) and a scope (0 for global) and returns a char** of all the IPs in the container. This also adds a matching python3 binding (function result is a tuple) and deprecates the previous pure-python get_ips() implementation. WARNING: The python get_ips() call is quite different from the previous implementation. The timeout argument has been removed, the family names are slightly different (inet/inet6 vs ipv4/ipv6) and an extra scope parameter has been added. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- src/lxc/lxccontainer.c | 116 ++++++++++++++++++++++++++++ src/lxc/lxccontainer.h | 1 + src/python-lxc/examples/api_test.py | 9 ++- src/python-lxc/lxc.c | 61 +++++++++++++++ src/python-lxc/lxc/__init__.py | 61 --------------- 5 files changed, 186 insertions(+), 62 deletions(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 23db6f203..4a6f774b2 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -37,6 +37,10 @@ #include "bdev.h" #include #include +#include +#include +#include +#include static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -768,6 +772,117 @@ static bool lxcapi_clear_config_item(struct lxc_container *c, const char *key) return ret == 0; } +char** lxcapi_get_ips(struct lxc_container *c, char* interface, char* family, int scope) +{ + int count = 0; + struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; + char addressOutputBuffer[INET6_ADDRSTRLEN]; + void *tempAddrPtr = NULL; + char **addresses = NULL, **temp; + char *address = NULL; + char new_netns_path[MAXPATHLEN]; + int old_netns = -1, new_netns = -1, ret = 0; + + if (!c->is_running(c)) + goto out; + + /* Save reference to old netns */ + old_netns = open("/proc/self/ns/net", O_RDONLY); + if (old_netns < 0) { + SYSERROR("failed to open /proc/self/ns/net"); + goto out; + } + + /* Switch to new netns */ + ret = snprintf(new_netns_path, MAXPATHLEN, "/proc/%d/ns/net", c->init_pid(c)); + if (ret < 0 || ret >= MAXPATHLEN) + goto out; + + new_netns = open(new_netns_path, O_RDONLY); + if (new_netns < 0) { + SYSERROR("failed to open %s", new_netns_path); + goto out; + } + + if (setns(new_netns, CLONE_NEWNET)) { + SYSERROR("failed to setns"); + goto out; + } + + /* Grab the list of interfaces */ + if (getifaddrs(&interfaceArray)) { + SYSERROR("failed to get interfaces list"); + goto out; + } + + /* Iterate through the interfaces */ + for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) { + if(tempIfAddr->ifa_addr->sa_family == AF_INET) { + if (family && strcmp(family, "inet")) + continue; + tempAddrPtr = &((struct sockaddr_in *)tempIfAddr->ifa_addr)->sin_addr; + } + else { + if (family && strcmp(family, "inet6")) + continue; + + if (((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_scope_id != scope) + continue; + + tempAddrPtr = &((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_addr; + } + + if (interface && strcmp(interface, tempIfAddr->ifa_name)) + continue; + else if (!interface && strcmp("lo", tempIfAddr->ifa_name) == 0) + continue; + + address = (char *)inet_ntop(tempIfAddr->ifa_addr->sa_family, + tempAddrPtr, + addressOutputBuffer, + sizeof(addressOutputBuffer)); + if (!address) + continue; + + count += 1; + temp = realloc(addresses, count * sizeof(*addresses)); + if (!temp) { + count--; + goto out; + } + addresses = temp; + addresses[count - 1] = strdup(address); + } + +out: + if(interfaceArray) + freeifaddrs(interfaceArray); + + /* Switch back to original netns */ + if (old_netns >= 0 && setns(old_netns, CLONE_NEWNET)) + SYSERROR("failed to setns"); + if (new_netns >= 0) + close(new_netns); + if (old_netns >= 0) + close(old_netns); + + /* Append NULL to the array */ + if (count) { + count++; + temp = realloc(addresses, count * sizeof(*addresses)); + if (!temp) { + for (int i = 0; i < count-1; i++) + free(addresses[i]); + free(addresses); + return NULL; + } + addresses = temp; + addresses[count - 1] = NULL; + } + + return addresses; +} + static int lxcapi_get_config_item(struct lxc_container *c, const char *key, char *retv, int inlen) { int ret; @@ -1642,6 +1757,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath c->get_config_path = lxcapi_get_config_path; c->set_config_path = lxcapi_set_config_path; c->clone = lxcapi_clone; + c->get_ips = lxcapi_get_ips; /* we'll allow the caller to update these later */ if (lxc_log_init(NULL, "none", NULL, "lxc_container", 0, c->config_path)) { diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index 67fbed412..bf3168cb9 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -58,6 +58,7 @@ 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); + char** (*get_ips)(struct lxc_container *c, char* interface, char* family, int scope); /* * 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 diff --git a/src/python-lxc/examples/api_test.py b/src/python-lxc/examples/api_test.py index 367bb7ab9..45a115071 100644 --- a/src/python-lxc/examples/api_test.py +++ b/src/python-lxc/examples/api_test.py @@ -28,6 +28,7 @@ warnings.filterwarnings("ignore", "The python-lxc API isn't yet stable") import lxc import uuid import sys +import time # Some constants LXC_TEMPLATE = "ubuntu" @@ -90,7 +91,13 @@ assert(container.state == "RUNNING") ## Checking IP address print("Getting the IP addresses") -ips = container.get_ips(timeout=10) + +count = 0 +ips = [] +while not ips or count == 10: + ips = container.get_ips() + time.sleep(1) + count += 1 container.attach("NETWORK|UTSNAME", "/sbin/ifconfig", "eth0") # A few basic checks of the current state diff --git a/src/python-lxc/lxc.c b/src/python-lxc/lxc.c index 4e9fde7ed..cdc1e8b83 100644 --- a/src/python-lxc/lxc.c +++ b/src/python-lxc/lxc.c @@ -399,6 +399,61 @@ Container_get_keys(Container *self, PyObject *args, PyObject *kwds) return ret; } +static PyObject * +Container_get_ips(Container *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"interface", "family", "scope", NULL}; + char* interface = NULL; + char* family = NULL; + int scope = 0; + + int i = 0; + char** ips = NULL; + + PyObject* ret; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "|ssi", kwlist, + &interface, &family, &scope)) + return NULL; + + /* Get the IPs */ + ips = self->container->get_ips(self->container, interface, family, scope); + if (!ips) + return PyTuple_New(0); + + /* Count the entries */ + while (ips[i]) + i++; + + /* Create the new tuple */ + ret = PyTuple_New(i); + if (!ret) + return NULL; + + /* Add the entries to the tuple and free the memory */ + i = 0; + while (ips[i]) { + PyObject *unicode = PyUnicode_FromString(ips[i]); + if (!unicode) { + Py_DECREF(ret); + ret = NULL; + break; + } + PyTuple_SET_ITEM(ret, i, unicode); + i++; + } + + /* Free the list of IPs */ + i = 0; + while (ips[i]) { + free(ips[i]); + i++; + } + free(ips); + + return ret; +} + static PyObject * Container_load_config(Container *self, PyObject *args, PyObject *kwds) { @@ -678,6 +733,12 @@ static PyMethodDef Container_methods[] = { "\n" "Get a list of valid sub-keys for a key." }, + {"get_ips", (PyCFunction)Container_get_ips, + METH_VARARGS|METH_KEYWORDS, + "get_ips(interface, family, scope) -> tuple\n" + "\n" + "Get a tuple of IPs for the container." + }, {"load_config", (PyCFunction)Container_load_config, METH_VARARGS|METH_KEYWORDS, "load_config(path = DEFAULT) -> boolean\n" diff --git a/src/python-lxc/lxc/__init__.py b/src/python-lxc/lxc/__init__.py index c235d1826..2fc907c3d 100644 --- a/src/python-lxc/lxc/__init__.py +++ b/src/python-lxc/lxc/__init__.py @@ -26,7 +26,6 @@ import glob import os import subprocess import stat -import time import warnings warnings.warn("The python-lxc API isn't yet stable " @@ -332,66 +331,6 @@ class Container(_lxc.Container): 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.running: - return False - - ips = [] - - count = 0 - while count < timeout: - if count != 0: - time.sleep(1) - - base_cmd = ["lxc-attach", "-s", "NETWORK", - "-P", self.get_config_path(), "-n", self.name, "--", - "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 - - return ips - def get_keys(self, key=None): """ Returns a list of valid sub-keys. From ef6e34eec8d5a9f1447462d6080facb674b3ccdb Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Fri, 17 May 2013 18:29:12 -0400 Subject: [PATCH 129/325] extend command processor to handle generic data Motivation for this change is to have the ability to get the run-time configuration items from a container, which may differ from its current on disk configuration, or might not be available any other way (for example lxc.network.0.veth.pair). In adding this ability it seemed there was room for refactoring improvements. Genericize the command infrastructure so that both command requests and responses can have arbitrary data. Consolidate all commands into command.c and name them consistently. This allows all the callback routines to be made static, reducing exposure. Return the actual allocated tty for the console command. Don't print the init pid in lxc_info if the container isn't actually running. Command processing was made more thread safe by removing the static buffer from receive_answer(). Refactored command response code to a common routine. Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/Makefile.am | 1 - src/lxc/cgroup.c | 70 +---- src/lxc/cgroup.h | 1 - src/lxc/commands.c | 676 ++++++++++++++++++++++++++++++++++------- src/lxc/commands.h | 77 +++-- src/lxc/console.c | 84 ----- src/lxc/lxc.h | 8 - src/lxc/lxc_attach.c | 4 +- src/lxc/lxc_console.c | 8 +- src/lxc/lxc_info.c | 10 +- src/lxc/lxc_kill.c | 2 +- src/lxc/lxc_stop.c | 3 +- src/lxc/lxccontainer.c | 4 +- src/lxc/start.c | 88 +----- src/lxc/state.c | 63 +--- src/lxc/stop.c | 115 ------- src/tests/cgpath.c | 5 +- 17 files changed, 648 insertions(+), 571 deletions(-) delete mode 100644 src/lxc/stop.c diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 4a1906101..f8eb41b8d 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -40,7 +40,6 @@ liblxc_so_SOURCES = \ bdev.c bdev.h \ commands.c commands.h \ start.c start.h \ - stop.c \ execute.c \ monitor.c monitor.h \ console.c \ diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c index c2f0a2da4..44266de62 100644 --- a/src/lxc/cgroup.c +++ b/src/lxc/cgroup.c @@ -120,7 +120,7 @@ static int get_cgroup_mount(const char *subsystem, char *mnt) while ((getmntent_r(file, &mntent_r, buf, sizeof(buf)))) { if (strcmp(mntent_r.mnt_type, "cgroup") != 0) continue; - + if (subsystem) { if (!hasmntopt(&mntent_r, subsystem)) continue; @@ -214,56 +214,10 @@ fail: return -1; } -/* - * Calculate a container's cgroup path for a particular subsystem. This - * is the cgroup path relative to the root of the cgroup filesystem. - * @path: A char ** into which we copy the char* containing the answer - * @subsystem: the cgroup subsystem of interest (i.e. freezer) - * @name: container name - * @lxcpath: the lxcpath in which the container is running. - * - * Returns 0 on success, -1 on error. - * - * Note that the char* copied into *path is a static char[MAXPATHLEN] in - * commands.c:receive_answer(). It should not be freed. - */ -extern int lxc_get_cgpath(const char **path, const char *subsystem, const char *name, const char *lxcpath) -{ - struct lxc_command command = { - .request = { .type = LXC_COMMAND_CGROUP }, - }; - - int ret, stopped = 0; - - ret = lxc_command(name, &command, &stopped, lxcpath); - if (ret < 0) { - if (!stopped) - ERROR("failed to send command"); - return -1; - } - - if (!ret) { - WARN("'%s' has stopped before sending its state", name); - return -1; - } - - if (command.answer.ret < 0 || command.answer.pathlen < 0) { - ERROR("failed to get state for '%s': %s", - name, strerror(-command.answer.ret)); - return -1; - } - - *path = command.answer.path; - - return 0; -} - /* * lxc_cgroup_path_get: determine full pathname for a cgroup * file for a specific container. - * @path: char ** used to return the answer. The char * will point - * into the static char* retuf from cgroup_path_get() (so no need - * to free it). + * @path: char ** used to return the answer. * @subsystem: cgroup subsystem (i.e. "freezer") for which to * return an answer. If NULL, then the first cgroup entry in * mtab will be used. @@ -275,12 +229,16 @@ extern int lxc_get_cgpath(const char **path, const char *subsystem, const char * */ int lxc_cgroup_path_get(char **path, const char *subsystem, const char *name, const char *lxcpath) { - const char *cgpath; + int ret; + char *cgpath; - if (lxc_get_cgpath(&cgpath, subsystem, name, lxcpath) < 0) + cgpath = lxc_cmd_get_cgroup_path(subsystem, name, lxcpath); + if (!cgpath) return -1; - return cgroup_path_get(path, subsystem, cgpath); + ret = cgroup_path_get(path, subsystem, cgpath); + free(cgpath); + return ret; } /* @@ -917,13 +875,17 @@ int lxc_cgroup_destroy(const char *cgpath) int lxc_cgroup_attach(pid_t pid, const char *name, const char *lxcpath) { - const char *dirpath; + int ret; + char *dirpath; - if (lxc_get_cgpath(&dirpath, NULL, name, lxcpath) < 0) { + dirpath = lxc_cmd_get_cgroup_path(NULL, name, lxcpath); + if (!dirpath) { ERROR("Error getting cgroup for container %s: %s", lxcpath, name); return -1; } INFO("joining pid %d to cgroup %s", pid, dirpath); - return lxc_cgroup_enter(dirpath, pid); + ret = lxc_cgroup_enter(dirpath, pid); + free(dirpath); + return ret; } diff --git a/src/lxc/cgroup.h b/src/lxc/cgroup.h index 937b9c92f..971311ece 100644 --- a/src/lxc/cgroup.h +++ b/src/lxc/cgroup.h @@ -34,5 +34,4 @@ extern char *lxc_cgroup_path_create(const char *lxcgroup, const char *name); extern int lxc_cgroup_enter(const char *cgpath, pid_t pid); extern int lxc_cgroup_attach(pid_t pid, const char *name, const char *lxcpath); extern int cgroup_path_get(char **path, const char *subsystem, const char *cgpath); -extern int lxc_get_cgpath(const char **path, const char *subsystem, const char *name, const char *lxcpath); #endif diff --git a/src/lxc/commands.c b/src/lxc/commands.c index d45ae2112..3f2148858 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -34,27 +34,30 @@ #include #include +#include #include #include /* for struct lxc_handler */ #include #include "commands.h" +#include "confile.h" #include "mainloop.h" #include "af_unix.h" #include "config.h" /* - * This file provides the different functions to have the client - * and the server to communicate + * This file provides the different functions for clients to + * query/command the server. The client is typically some lxc + * tool and the server is typically the container (ie. lxc-start). * - * Each command is transactional, the client send a request to - * the server and the server answer the request with a message + * Each command is transactional, the clients send a request to + * the server and the server answers the request with a message * giving the request's status (zero or a negative errno value). + * Both the request and response may contain addtional data. * * Each command is wrapped in a ancillary message in order to pass * a credential making possible to the server to check if the client * is allowed to ask for this command or not. - * */ lxc_log_define(lxc_commands, lxc); @@ -81,66 +84,183 @@ static int fill_sock_name(char *path, int len, const char *name, return 0; } -static int receive_answer(int sock, struct lxc_answer *answer) +static const char *lxc_cmd_str(lxc_cmd_t cmd) { - int ret; - static char answerpath[MAXPATHLEN]; + static const char *cmdname[LXC_CMD_MAX] = { + [LXC_CMD_CONSOLE] = "console", + [LXC_CMD_STOP] = "stop", + [LXC_CMD_GET_STATE] = "get_state", + [LXC_CMD_GET_INIT_PID] = "get_init_pid", + [LXC_CMD_GET_CLONE_FLAGS] = "get_clone_flags", + [LXC_CMD_GET_CGROUP] = "get_cgroup", + [LXC_CMD_GET_CONFIG_ITEM] = "get_config_item", + }; - ret = lxc_af_unix_recv_fd(sock, &answer->fd, answer, sizeof(*answer)); - if (ret < 0) - ERROR("failed to receive answer for the command"); - if (answer->pathlen == 0) - return ret; - if (answer->pathlen >= MAXPATHLEN) { - ERROR("cgroup path was too long"); + if (cmd < 0 || cmd >= LXC_CMD_MAX) + return "Unknown cmd"; + return cmdname[cmd]; +} + +/* + * lxc_cmd_rsp_recv: Receive a response to a command + * + * @sock : the socket connected to the container + * @cmd : command to put response in + * + * Returns the size of the response message or < 0 on failure + * + * Note that if the command response datalen > 0, then data is + * a malloc()ed buffer and should be free()ed by the caller. If + * the response data is <= a void * worth of data, it will be + * stored directly in data and datalen will be 0. + * + * As a special case, the response for LXC_CMD_CONSOLE is created + * here as it contains an fd passed through the unix socket. + */ +static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd) +{ + int ret,rspfd; + struct lxc_cmd_rsp *rsp = &cmd->rsp; + + ret = lxc_af_unix_recv_fd(sock, &rspfd, rsp, sizeof(*rsp)); + if (ret < 0) { + ERROR("command %s failed to receive response", + lxc_cmd_str(cmd->req.cmd)); return -1; } - ret = recv(sock, answerpath, answer->pathlen, 0); - if (ret != answer->pathlen) { - ERROR("failed to receive answer for the command"); - ret = 0; - } else - answer->path = answerpath; + + if (cmd->req.cmd == LXC_CMD_CONSOLE) { + struct lxc_cmd_console_rsp_data *rspdata; + + rspdata = malloc(sizeof(*rspdata)); + if (!rspdata) { + ERROR("command %s couldn't allocate response buffer", + lxc_cmd_str(cmd->req.cmd)); + return -1; + } + rspdata->fd = rspfd; + rspdata->ttynum = PTR_TO_INT(rsp->data); + rsp->data = rspdata; + } + + if (rsp->datalen == 0) + return ret; + if (rsp->datalen > LXC_CMD_DATA_MAX) { + ERROR("command %s response data %d too long", + lxc_cmd_str(cmd->req.cmd), rsp->datalen); + errno = EFBIG; + return -1; + } + + rsp->data = malloc(rsp->datalen); + if (!rsp->data) { + ERROR("command %s unable to allocate response buffer", + lxc_cmd_str(cmd->req.cmd)); + return -1; + } + ret = recv(sock, rsp->data, rsp->datalen, 0); + if (ret != rsp->datalen) { + ERROR("command %s failed to receive response data", + lxc_cmd_str(cmd->req.cmd)); + if (ret >= 0) + ret = -1; + } return ret; } -static int __lxc_command(const char *name, struct lxc_command *command, - int *stopped, int stay_connected, const char *lxcpath) +/* + * lxc_cmd_rsp_send: Send a command response + * + * @fd : file descriptor of socket to send response on + * @rsp : response to send + * + * Returns 0 on success, < 0 on failure + */ +static int lxc_cmd_rsp_send(int fd, struct lxc_cmd_rsp *rsp) +{ + int ret; + + ret = send(fd, rsp, sizeof(*rsp), 0); + if (ret != sizeof(*rsp)) { + ERROR("failed to send command response %d %s", ret, + strerror(errno)); + return -1; + } + + if (rsp->datalen > 0) { + ret = send(fd, rsp->data, rsp->datalen, 0); + if (ret != rsp->datalen) { + WARN("failed to send command response data %d %s", ret, + strerror(errno)); + return -1; + } + } + return 0; +} + +/* + * lxc_cmd: Connect to the specified running container, send it a command + * request and collect the response + * + * @name : name of container to connect to + * @cmd : command with initialized reqest to send + * @stopped : output indicator if the container was not running + * @lxcpath : the lxcpath in which the container is running + * + * Returns the size of the response message on success, < 0 on failure + * + * Note that there is a special case for LXC_CMD_CONSOLE. For this command + * the fd cannot be closed because it is used as a placeholder to indicate + * that a particular tty slot is in use. The fd is also used as a signal to + * the container that when the caller dies or closes the fd, the container + * will notice the fd in its mainloop select and then free the slot with + * lxc_cmd_fd_cleanup(). + */ +static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped, + const char *lxcpath) { int sock, ret = -1; char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 }; char *offset = &path[1]; int len; + int stay_connected = cmd->req.cmd == LXC_CMD_CONSOLE; len = sizeof(path)-1; if (fill_sock_name(offset, len, name, lxcpath)) return -1; sock = lxc_af_unix_connect(path); - if (sock < 0 && errno == ECONNREFUSED) { - *stopped = 1; - return -1; - } - if (sock < 0) { - SYSERROR("failed to connect to '@%s'", offset); + if (errno == ECONNREFUSED) + *stopped = 1; + else + SYSERROR("command %s failed to connect to '@%s'", + lxc_cmd_str(cmd->req.cmd), offset); return -1; } - ret = lxc_af_unix_send_credential(sock, &command->request, - sizeof(command->request)); - if (ret < 0) { - SYSERROR("failed to send request to '@%s'", offset); + ret = lxc_af_unix_send_credential(sock, &cmd->req, sizeof(cmd->req)); + if (ret != sizeof(cmd->req)) { + SYSERROR("command %s failed to send req to '@%s' %d", + lxc_cmd_str(cmd->req.cmd), offset, ret); + if (ret >=0) + ret = -1; goto out; } - if (ret != sizeof(command->request)) { - SYSERROR("message partially sent to '@%s'", offset); - goto out; + if (cmd->req.datalen > 0) { + ret = send(sock, cmd->req.data, cmd->req.datalen, 0); + if (ret != cmd->req.datalen) { + SYSERROR("command %s failed to send request data to '@%s' %d", + lxc_cmd_str(cmd->req.cmd), offset, ret); + if (ret >=0) + ret = -1; + goto out; + } } - ret = receive_answer(sock, &command->answer); + ret = lxc_cmd_rsp_recv(sock, cmd); out: if (!stay_connected || ret < 0) close(sock); @@ -148,114 +268,418 @@ out: return ret; } -extern int lxc_command(const char *name, - struct lxc_command *command, int *stopped, - const char *lxcpath) -{ - return __lxc_command(name, command, stopped, 0, lxcpath); -} +/* Implentations of the commands and their callbacks */ -extern int lxc_command_connected(const char *name, - struct lxc_command *command, int *stopped, - const char *lxcpath) +/* + * lxc_cmd_get_init_pid: Get pid of the container's init process + * + * @name : name of container to connect to + * @lxcpath : the lxcpath in which the container is running + * + * Returns the pid on success, < 0 on failure + */ +pid_t lxc_cmd_get_init_pid(const char *name, const char *lxcpath) { - return __lxc_command(name, command, stopped, 1, lxcpath); -} - - -pid_t get_init_pid(const char *name, const char *lxcpath) -{ - struct lxc_command command = { - .request = { .type = LXC_COMMAND_PID }, + int ret, stopped = 0; + struct lxc_cmd_rr cmd = { + .req = { .cmd = LXC_CMD_GET_INIT_PID }, }; - int ret, stopped = 0; + ret = lxc_cmd(name, &cmd, &stopped, lxcpath); + if (ret < 0) + return ret; - ret = lxc_command(name, &command, &stopped, lxcpath); - if (ret < 0 && stopped) - return -1; - - if (ret < 0) { - ERROR("failed to send command"); - return -1; - } - - if (command.answer.ret) { - ERROR("failed to retrieve the init pid: %s", - strerror(-command.answer.ret)); - return -1; - } - - return command.answer.pid; + return PTR_TO_INT(cmd.rsp.data); } -int lxc_get_clone_flags(const char *name, const char *lxcpath) +static int lxc_cmd_get_init_pid_callback(int fd, struct lxc_cmd_req *req, + struct lxc_handler *handler) { - struct lxc_command command = { - .request = { .type = LXC_COMMAND_CLONE_FLAGS }, + struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->pid) }; + + return lxc_cmd_rsp_send(fd, &rsp); +} + +/* + * lxc_cmd_get_clone_flags: Get clone flags container was spawned with + * + * @name : name of container to connect to + * @lxcpath : the lxcpath in which the container is running + * + * Returns the clone flags on success, < 0 on failure + */ +int lxc_cmd_get_clone_flags(const char *name, const char *lxcpath) +{ + int ret, stopped = 0; + struct lxc_cmd_rr cmd = { + .req = { .cmd = LXC_CMD_GET_CLONE_FLAGS }, }; - int ret, stopped = 0; + ret = lxc_cmd(name, &cmd, &stopped, lxcpath); + if (ret < 0) + return ret; - ret = lxc_command(name, &command, &stopped, lxcpath); + return PTR_TO_INT(cmd.rsp.data); +} + +static int lxc_cmd_get_clone_flags_callback(int fd, struct lxc_cmd_req *req, + struct lxc_handler *handler) +{ + struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->clone_flags) }; + + return lxc_cmd_rsp_send(fd, &rsp); +} + +/* + * lxc_cmd_get_cgroup_path: Calculate a container's cgroup path for a + * particular subsystem. This is the cgroup path relative to the root + * of the cgroup filesystem. + * + * @subsystem : the cgroup subsystem of interest (i.e. freezer) + * @name : name of container to connect to + * @lxcpath : the lxcpath in which the container is running + * + * Returns the path on success, NULL on failure. The caller must free() the + * returned path. + */ +char *lxc_cmd_get_cgroup_path(const char *subsystem, const char *name, + const char *lxcpath) +{ + int ret, stopped = 0; + struct lxc_cmd_rr cmd = { + .req = { .cmd = LXC_CMD_GET_CGROUP }, + }; + + ret = lxc_cmd(name, &cmd, &stopped, lxcpath); + if (ret < 0) + return NULL; + + if (!ret) { + WARN("'%s' has stopped before sending its state", name); + return NULL; + } + + if (cmd.rsp.ret < 0 || cmd.rsp.datalen < 0) { + ERROR("command %s failed for '%s': %s", + lxc_cmd_str(cmd.req.cmd), name, + strerror(-cmd.rsp.ret)); + return NULL; + } + + return cmd.rsp.data; +} + +static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req, + struct lxc_handler *handler) +{ + struct lxc_cmd_rsp rsp = { + .datalen = strlen(handler->cgroup) + 1, + .data = handler->cgroup, + }; + + return lxc_cmd_rsp_send(fd, &rsp); +} + +/* + * lxc_cmd_get_config_item: Get config item the running container + * + * @name : name of container to connect to + * @item : the configuration item to retrieve (ex: lxc.network.0.veth.pair) + * @lxcpath : the lxcpath in which the container is running + * + * Returns the item on success, NULL on failure. The caller must free() the + * returned item. + */ +char *lxc_cmd_get_config_item(const char *name, const char *item, + const char *lxcpath) +{ + int ret, stopped = 0; + struct lxc_cmd_rr cmd = { + .req = { .cmd = LXC_CMD_GET_CONFIG_ITEM, + .data = item, + .datalen = strlen(item)+1, + }, + }; + + ret = lxc_cmd(name, &cmd, &stopped, lxcpath); + if (ret < 0) + return NULL; + + if (cmd.rsp.ret == 0) + return cmd.rsp.data; + return NULL; +} + +static int lxc_cmd_get_config_item_callback(int fd, struct lxc_cmd_req *req, + struct lxc_handler *handler) +{ + int cilen; + struct lxc_cmd_rsp rsp; + char *cidata; + + memset(&rsp, 0, sizeof(rsp)); + cilen = lxc_get_config_item(handler->conf, req->data, NULL, 0); + if (cilen <= 0) + goto err1; + + cidata = alloca(cilen + 1); + if (lxc_get_config_item(handler->conf, req->data, cidata, cilen + 1) != cilen) + goto err1; + cidata[cilen] = '\0'; + rsp.data = cidata; + rsp.datalen = cilen + 1; + rsp.ret = 0; + goto out; + +err1: + rsp.ret = -1; +out: + return lxc_cmd_rsp_send(fd, &rsp); +} + +/* + * lxc_cmd_get_state: Get current state of the container + * + * @name : name of container to connect to + * @lxcpath : the lxcpath in which the container is running + * + * Returns the state on success, < 0 on failure + */ +lxc_state_t lxc_cmd_get_state(const char *name, const char *lxcpath) +{ + int ret, stopped = 0; + struct lxc_cmd_rr cmd = { + .req = { .cmd = LXC_CMD_GET_STATE } + }; + + ret = lxc_cmd(name, &cmd, &stopped, lxcpath); if (ret < 0 && stopped) + return STOPPED; + + if (ret < 0) return -1; - if (ret < 0) { - ERROR("failed to send command"); + if (!ret) { + WARN("'%s' has stopped before sending its state", name); return -1; } - return command.answer.ret; + DEBUG("'%s' is in '%s' state", name, + lxc_state2str(PTR_TO_INT(cmd.rsp.data))); + return PTR_TO_INT(cmd.rsp.data); } -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 *); -extern int lxc_cgroup_callback(int, struct lxc_request *, struct lxc_handler *); +static int lxc_cmd_get_state_callback(int fd, struct lxc_cmd_req *req, + struct lxc_handler *handler) +{ + struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->state) }; -static int trigger_command(int fd, struct lxc_request *request, + return lxc_cmd_rsp_send(fd, &rsp); +} + +/* + * lxc_cmd_stop: Stop the container previously started with lxc_start. All + * the processes running inside this container will be killed. + * + * @name : name of container to connect to + * @lxcpath : the lxcpath in which the container is running + * + * Returns 0 on success, < 0 on failure + */ +int lxc_cmd_stop(const char *name, const char *lxcpath) +{ + int ret, stopped = 0; + struct lxc_cmd_rr cmd = { + .req = { .cmd = LXC_CMD_STOP }, + }; + + ret = lxc_cmd(name, &cmd, &stopped, lxcpath); + if (ret < 0) { + if (stopped) { + INFO("'%s' is already stopped", name); + return 0; + } + return -1; + } + + /* we do not expect any answer, because we wait for the connection to be + * closed + */ + if (ret > 0) { + ERROR("failed to stop '%s': %s", name, strerror(-cmd.rsp.ret)); + return -1; + } + + INFO("'%s' has stopped", name); + return 0; +} + +static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req, + struct lxc_handler *handler) +{ + struct lxc_cmd_rsp rsp; + int ret; + int stopsignal = SIGKILL; + + if (handler->conf->stopsignal) + stopsignal = handler->conf->stopsignal; + memset(&rsp, 0, sizeof(rsp)); + rsp.ret = kill(handler->pid, stopsignal); + if (!rsp.ret) { + ret = lxc_unfreeze_bypath(handler->cgroup); + if (!ret) + return 0; + + ERROR("failed to unfreeze container"); + rsp.ret = ret; + } + + return lxc_cmd_rsp_send(fd, &rsp); +} + +/* + * lxc_cmd_console: Open an fd to a tty in the container + * + * @name : name of container to connect to + * @ttynum : in: the tty to open or -1 for next available + * : out: the tty allocated + * @fd : out: file descriptor for master side of pty + * @lxcpath : the lxcpath in which the container is running + * + * Returns 0 on success, < 0 on failure + */ +int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath) +{ + int ret, stopped = 0; + struct lxc_cmd_console_rsp_data *rspdata; + struct lxc_cmd_rr cmd = { + .req = { .cmd = LXC_CMD_CONSOLE, .data = INT_TO_PTR(*ttynum) }, + }; + + ret = lxc_cmd(name, &cmd, &stopped, lxcpath); + if (ret < 0) + return ret; + if (ret == 0) { + ERROR("console %d invalid or all consoles busy", *ttynum); + ret = -1; + goto out; + } + + ret = -1; + #if 1 /* FIXME: how can this happen? */ + if (cmd.rsp.ret) { + ERROR("console access denied: %s", + strerror(-cmd.rsp.ret)); + goto out; + } + #endif + + rspdata = cmd.rsp.data; + if (rspdata->fd < 0) { + ERROR("unable to allocate fd for tty %d", rspdata->ttynum); + goto out; + } + + ret = 0; + *fd = rspdata->fd; + *ttynum = rspdata->ttynum; + INFO("tty %d allocated", rspdata->ttynum); +out: + free(cmd.rsp.data); + return ret; +} + +static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req, + struct lxc_handler *handler) +{ + int ttynum = PTR_TO_INT(req->data); + struct lxc_tty_info *tty_info = &handler->conf->tty_info; + struct lxc_cmd_rsp rsp; + + if (ttynum > 0) { + if (ttynum > tty_info->nbtty) + goto out_close; + + if (tty_info->pty_info[ttynum - 1].busy) + goto out_close; + + /* the requested tty is available */ + goto out_send; + } + + /* search for next available tty, fixup index tty1 => [0] */ + for (ttynum = 1; + ttynum <= tty_info->nbtty && tty_info->pty_info[ttynum - 1].busy; + ttynum++); + + /* we didn't find any available slot for tty */ + if (ttynum > tty_info->nbtty) + goto out_close; + +out_send: + memset(&rsp, 0, sizeof(rsp)); + rsp.data = INT_TO_PTR(ttynum); + if (lxc_af_unix_send_fd(fd, tty_info->pty_info[ttynum - 1].master, + &rsp, sizeof(rsp)) < 0) { + ERROR("failed to send tty to client"); + goto out_close; + } + + tty_info->pty_info[ttynum - 1].busy = fd; + return 0; + +out_close: + /* special indicator to lxc_cmd_handler() to close the fd and do + * related cleanup + */ + return 1; +} + + + +static int lxc_cmd_process(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { - typedef int (*callback)(int, struct lxc_request *, struct lxc_handler *); + typedef int (*callback)(int, struct lxc_cmd_req *, 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_CLONE_FLAGS] = lxc_clone_flags_callback, - [LXC_COMMAND_CGROUP] = lxc_cgroup_callback, + callback cb[LXC_CMD_MAX] = { + [LXC_CMD_CONSOLE] = lxc_cmd_console_callback, + [LXC_CMD_STOP] = lxc_cmd_stop_callback, + [LXC_CMD_GET_STATE] = lxc_cmd_get_state_callback, + [LXC_CMD_GET_INIT_PID] = lxc_cmd_get_init_pid_callback, + [LXC_CMD_GET_CLONE_FLAGS] = lxc_cmd_get_clone_flags_callback, + [LXC_CMD_GET_CGROUP] = lxc_cmd_get_cgroup_callback, + [LXC_CMD_GET_CONFIG_ITEM] = lxc_cmd_get_config_item_callback, }; - if (request->type < 0 || request->type >= LXC_COMMAND_MAX) + if (req->cmd < 0 || req->cmd >= LXC_CMD_MAX) { + ERROR("bad cmd %d recieved", req->cmd); return -1; - - return cb[request->type](fd, request, handler); + } + return cb[req->cmd](fd, req, handler); } -static void command_fd_cleanup(int fd, struct lxc_handler *handler, +static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler, struct lxc_epoll_descr *descr) { + extern void lxc_console_remove_fd(int, struct lxc_tty_info *); lxc_console_remove_fd(fd, &handler->conf->tty_info); lxc_mainloop_del_handler(descr, fd); close(fd); } -static int command_handler(int fd, void *data, struct lxc_epoll_descr *descr) +static int lxc_cmd_handler(int fd, void *data, struct lxc_epoll_descr *descr) { int ret; - struct lxc_request request; + struct lxc_cmd_req req; struct lxc_handler *handler = data; - ret = lxc_af_unix_rcv_credential(fd, &request, sizeof(request)); + ret = lxc_af_unix_rcv_credential(fd, &req, sizeof(req)); if (ret == -EACCES) { /* we don't care for the peer, just send and close */ - struct lxc_answer answer = { .ret = ret }; - send(fd, &answer, sizeof(answer), 0); + struct lxc_cmd_rsp rsp = { .ret = ret }; + + lxc_cmd_rsp_send(fd, &rsp); goto out_close; } @@ -269,12 +693,32 @@ static int command_handler(int fd, void *data, struct lxc_epoll_descr *descr) goto out_close; } - if (ret != sizeof(request)) { + if (ret != sizeof(req)) { WARN("partial request, ignored"); + ret = -1; goto out_close; } - ret = trigger_command(fd, &request, handler); + if (req.datalen > LXC_CMD_DATA_MAX) { + ERROR("cmd data length %d too large", req.datalen); + ret = -1; + goto out_close; + } + + if (req.datalen > 0) { + void *reqdata; + + reqdata = alloca(req.datalen); + ret = recv(fd, reqdata, req.datalen, 0); + if (ret != req.datalen) { + WARN("partial request, ignored"); + ret = -1; + goto out_close; + } + req.data = reqdata; + } + + ret = lxc_cmd_process(fd, &req, handler); if (ret) { /* this is not an error, but only a request to close fd */ ret = 0; @@ -284,12 +728,11 @@ static int command_handler(int fd, void *data, struct lxc_epoll_descr *descr) out: return ret; out_close: - command_fd_cleanup(fd, handler, descr); + lxc_cmd_fd_cleanup(fd, handler, descr); goto out; } -static int incoming_command_handler(int fd, void *data, - struct lxc_epoll_descr *descr) +static int lxc_cmd_accept(int fd, void *data, struct lxc_epoll_descr *descr) { int opt = 1, ret = -1, connection; @@ -310,7 +753,7 @@ static int incoming_command_handler(int fd, void *data, goto out_close; } - ret = lxc_mainloop_add_handler(descr, connection, command_handler, data); + ret = lxc_mainloop_add_handler(descr, connection, lxc_cmd_handler, data); if (ret) { ERROR("failed to add handler"); goto out_close; @@ -324,8 +767,8 @@ out_close: goto out; } -extern int lxc_command_init(const char *name, struct lxc_handler *handler, - const char *lxcpath) +int lxc_cmd_init(const char *name, struct lxc_handler *handler, + const char *lxcpath) { int fd; char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 }; @@ -357,14 +800,13 @@ extern int lxc_command_init(const char *name, struct lxc_handler *handler, return 0; } -extern int lxc_command_mainloop_add(const char *name, - struct lxc_epoll_descr *descr, - struct lxc_handler *handler) +int lxc_cmd_mainloop_add(const char *name, + struct lxc_epoll_descr *descr, + struct lxc_handler *handler) { int ret, fd = handler->conf->maincmd_fd; - ret = lxc_mainloop_add_handler(descr, fd, incoming_command_handler, - handler); + ret = lxc_mainloop_add_handler(descr, fd, lxc_cmd_accept, handler); if (ret) { ERROR("failed to add handler for command socket"); close(fd); diff --git a/src/lxc/commands.h b/src/lxc/commands.h index 3b725fbbf..b5b478878 100644 --- a/src/lxc/commands.h +++ b/src/lxc/commands.h @@ -20,52 +20,67 @@ * 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 __commands_h #define __commands_h -enum { - LXC_COMMAND_TTY, - LXC_COMMAND_STOP, - LXC_COMMAND_STATE, - LXC_COMMAND_PID, - LXC_COMMAND_CLONE_FLAGS, - LXC_COMMAND_CGROUP, - LXC_COMMAND_MAX, +#include "state.h" + +#define LXC_CMD_DATA_MAX (MAXPATHLEN*2) + +/* https://developer.gnome.org/glib/2.28/glib-Type-Conversion-Macros.html */ +#define INT_TO_PTR(n) ((void *) (long) (n)) +#define PTR_TO_INT(p) ((int) (long) (p)) + +typedef enum { + LXC_CMD_CONSOLE, + LXC_CMD_STOP, + LXC_CMD_GET_STATE, + LXC_CMD_GET_INIT_PID, + LXC_CMD_GET_CLONE_FLAGS, + LXC_CMD_GET_CGROUP, + LXC_CMD_GET_CONFIG_ITEM, + LXC_CMD_MAX, +} lxc_cmd_t; + +struct lxc_cmd_req { + lxc_cmd_t cmd; + int datalen; + const void *data; }; -struct lxc_request { - int type; - int data; -}; - -struct lxc_answer { - int fd; +struct lxc_cmd_rsp { int ret; /* 0 on success, -errno on failure */ - pid_t pid; - int pathlen; - const char *path; + int datalen; + void *data; }; -struct lxc_command { - struct lxc_request request; - struct lxc_answer answer; +struct lxc_cmd_rr { + struct lxc_cmd_req req; + struct lxc_cmd_rsp rsp; }; -extern pid_t get_init_pid(const char *name, const char *lxcpath); -extern int lxc_get_clone_flags(const char *name, const char *lxcpath); +struct lxc_cmd_console_rsp_data { + int fd; + int ttynum; +}; -extern int lxc_command(const char *name, struct lxc_command *command, - int *stopped, const char *lxcpath); - -extern int lxc_command_connected(const char *name, struct lxc_command *command, - int *stopped, const char *lxcpath); +extern int lxc_cmd_console(const char *name, int *ttynum, int *fd, + const char *lxcpath); +extern char *lxc_cmd_get_cgroup_path(const char *subsystem, + const char *name, const char *lxcpath); +extern int lxc_cmd_get_clone_flags(const char *name, const char *lxcpath); +extern char *lxc_cmd_get_config_item(const char *name, const char *item, const char *lxcpath); +extern pid_t lxc_cmd_get_init_pid(const char *name, const char *lxcpath); +extern lxc_state_t lxc_cmd_get_state(const char *name, const char *lxcpath); +extern int lxc_cmd_stop(const char *name, const char *lxcpath); struct lxc_epoll_descr; struct lxc_handler; -extern int lxc_command_init(const char *name, struct lxc_handler *handler, +extern int lxc_cmd_init(const char *name, struct lxc_handler *handler, const char *lxcpath); -extern int lxc_command_mainloop_add(const char *name, struct lxc_epoll_descr *descr, +extern int lxc_cmd_mainloop_add(const char *name, struct lxc_epoll_descr *descr, struct lxc_handler *handler); -#endif +#endif /* __commands_h */ diff --git a/src/lxc/console.c b/src/lxc/console.c index 9ef62c102..93c16b5fa 100644 --- a/src/lxc/console.c +++ b/src/lxc/console.c @@ -46,49 +46,6 @@ lxc_log_define(lxc_console, lxc); -extern int lxc_console(const char *name, int ttynum, int *fd, const char *lxcpath) -{ - int ret, stopped = 0; - struct lxc_command command = { - .request = { .type = LXC_COMMAND_TTY, .data = ttynum }, - }; - - ret = lxc_command_connected(name, &command, &stopped, lxcpath); - if (ret < 0 && stopped) { - ERROR("'%s' is stopped", name); - return -1; - } - - if (ret < 0) { - ERROR("failed to send command"); - return -1; - } - - if (!ret) { - ERROR("console denied by '%s'", name); - return -1; - } - - if (command.answer.ret) { - ERROR("console access denied: %s", - strerror(-command.answer.ret)); - return -1; - } - - *fd = command.answer.fd; - if (*fd <0) { - ERROR("unable to allocate fd for tty %d", ttynum); - return -1; - } - - INFO("tty %d allocated", ttynum); - return 0; -} - -/*---------------------------------------------------------------------------- - * functions used by lxc-start mainloop - * to handle above command request. - *--------------------------------------------------------------------------*/ extern void lxc_console_remove_fd(int fd, struct lxc_tty_info *tty_info) { int i; @@ -104,47 +61,6 @@ extern void lxc_console_remove_fd(int fd, struct lxc_tty_info *tty_info) return; } -extern int lxc_console_callback(int fd, struct lxc_request *request, - struct lxc_handler *handler) -{ - int ttynum = request->data; - struct lxc_tty_info *tty_info = &handler->conf->tty_info; - - if (ttynum > 0) { - if (ttynum > tty_info->nbtty) - goto out_close; - - if (tty_info->pty_info[ttynum - 1].busy) - goto out_close; - - goto out_send; - } - - /* fixup index tty1 => [0] */ - for (ttynum = 1; - ttynum <= tty_info->nbtty && tty_info->pty_info[ttynum - 1].busy; - ttynum++); - - /* we didn't find any available slot for tty */ - if (ttynum > tty_info->nbtty) - goto out_close; - -out_send: - if (lxc_af_unix_send_fd(fd, tty_info->pty_info[ttynum - 1].master, - &ttynum, sizeof(ttynum)) < 0) { - ERROR("failed to send tty to client"); - goto out_close; - } - - tty_info->pty_info[ttynum - 1].busy = fd; - - return 0; - -out_close: - /* the close fd and related cleanup will be done by caller */ - return 1; -} - static int get_default_console(char **console) { int fd; diff --git a/src/lxc/lxc.h b/src/lxc/lxc.h index 9057757c5..8491ff3da 100644 --- a/src/lxc/lxc.h +++ b/src/lxc/lxc.h @@ -51,14 +51,6 @@ struct lxc_arguments; extern int lxc_start(const char *name, char *const argv[], struct lxc_conf *conf, const char *lxcpath); -/* - * Stop the container previously started with lxc_start, all - * the processes running inside this container will be killed. - * @name : the name of the container - * Returns 0 on success, < 0 otherwise - */ -extern int lxc_stop(const char *name, const char *lxcpath); - /* * Start the specified command inside an application container * @name : the name of the container diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c index e0c253b4b..efa7d89da 100644 --- a/src/lxc/lxc_attach.c +++ b/src/lxc/lxc_attach.c @@ -296,7 +296,7 @@ int main(int argc, char *argv[]) if (ret) return ret; - init_pid = get_init_pid(my_args.name, my_args.lxcpath[0]); + init_pid = lxc_cmd_get_init_pid(my_args.name, my_args.lxcpath[0]); if (init_pid < 0) { ERROR("failed to get the init pid"); return -1; @@ -314,7 +314,7 @@ int main(int argc, char *argv[]) * by asking lxc-start */ if (namespace_flags == -1) { - namespace_flags = lxc_get_clone_flags(my_args.name, my_args.lxcpath[0]); + namespace_flags = lxc_cmd_get_clone_flags(my_args.name, my_args.lxcpath[0]); /* call failed */ if (namespace_flags == -1) { ERROR("failed to automatically determine the " diff --git a/src/lxc/lxc_console.c b/src/lxc/lxc_console.c index 795b54c24..820794ab9 100644 --- a/src/lxc/lxc_console.c +++ b/src/lxc/lxc_console.c @@ -43,6 +43,7 @@ #include "log.h" #include "mainloop.h" #include "arguments.h" +#include "commands.h" lxc_log_define(lxc_console_ui, lxc_console); @@ -202,13 +203,14 @@ int main(int argc, char *argv[]) return -1; } - err = lxc_console(my_args.name, my_args.ttynum, &master, my_args.lxcpath[0]); + err = lxc_cmd_console(my_args.name, &my_args.ttynum, &master, my_args.lxcpath[0]); if (err) goto out; fprintf(stderr, "\n\ -Type to exit the console, \ - to enter Ctrl+%1$c itself\n", +Connected to tty %1$d\n\ +Type to exit the console, \ + to enter Ctrl+%2$c itself\n", my_args.ttynum, 'a' + my_args.escape - 1); err = setsid(); diff --git a/src/lxc/lxc_info.c b/src/lxc/lxc_info.c index f815b0f47..f86626d97 100644 --- a/src/lxc/lxc_info.c +++ b/src/lxc/lxc_info.c @@ -22,6 +22,7 @@ */ #include #include +#include #include #include #include @@ -96,8 +97,13 @@ int main(int argc, char *argv[]) printf("state:%10s\n", lxc_state2str(ret)); } - if (pid) - printf("pid:%10d\n", get_init_pid(my_args.name, my_args.lxcpath[0])); + if (pid) { + pid_t initpid; + + initpid = lxc_cmd_get_init_pid(my_args.name, my_args.lxcpath[0]); + if (initpid >= 0) + printf("pid:%10d\n", initpid); + } return 0; } diff --git a/src/lxc/lxc_kill.c b/src/lxc/lxc_kill.c index 9a24209dd..1fedf1d08 100644 --- a/src/lxc/lxc_kill.c +++ b/src/lxc/lxc_kill.c @@ -76,7 +76,7 @@ int main(int argc, char *argv[], char *envp[]) } else sig=SIGKILL; - pid = get_init_pid(my_args.name, my_args.lxcpath[0]); + pid = lxc_cmd_get_init_pid(my_args.name, my_args.lxcpath[0]); if (pid < 0) { ERROR("failed to get the init pid"); return -1; diff --git a/src/lxc/lxc_stop.c b/src/lxc/lxc_stop.c index d7c728373..0ed8ca066 100644 --- a/src/lxc/lxc_stop.c +++ b/src/lxc/lxc_stop.c @@ -29,6 +29,7 @@ #include #include "arguments.h" +#include "commands.h" #include "utils.h" static const struct option my_longopts[] = { @@ -58,5 +59,5 @@ int main(int argc, char *argv[]) my_args.progname, my_args.quiet, my_args.lxcpath[0])) return -1; - return lxc_stop(my_args.name, my_args.lxcpath[0]); + return lxc_cmd_stop(my_args.name, my_args.lxcpath[0]); } diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 4a6f774b2..7ce25c6d8 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -283,7 +283,7 @@ static pid_t lxcapi_init_pid(struct lxc_container *c) if (lxclock(c->slock, 0)) return -1; - ret = get_init_pid(c->name, c->config_path); + ret = lxc_cmd_get_init_pid(c->name, c->config_path); lxcunlock(c->slock); return ret; } @@ -521,7 +521,7 @@ static bool lxcapi_stop(struct lxc_container *c) if (!c) return false; - ret = lxc_stop(c->name, c->config_path); + ret = lxc_cmd_stop(c->name, c->config_path); return ret == 0; } diff --git a/src/lxc/start.c b/src/lxc/start.c index 692de4459..d43d58073 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -262,90 +262,6 @@ static int signal_handler(int fd, void *data, return 1; } -int lxc_pid_callback(int fd, struct lxc_request *request, - struct lxc_handler *handler) -{ - struct lxc_answer answer; - int ret; - - memset(&answer, 0, sizeof(answer)); - answer.pid = handler->pid; - answer.ret = 0; - - 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_cgroup_callback(int fd, struct lxc_request *request, - struct lxc_handler *handler) -{ - struct lxc_answer answer; - int ret; - - memset(&answer, 0, sizeof(answer)); - answer.pathlen = strlen(handler->cgroup) + 1; - answer.path = handler->cgroup; - answer.ret = 0; - - 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; - } - - ret = send(fd, answer.path, answer.pathlen, 0); - if (ret < 0) { - WARN("failed to send answer to the peer"); - return -1; - } - - if (ret != answer.pathlen) { - ERROR("partial answer sent"); - return -1; - } - - return 0; -} - -int lxc_clone_flags_callback(int fd, struct lxc_request *request, - struct lxc_handler *handler) -{ - struct lxc_answer answer; - int ret; - - memset(&answer, 0, sizeof(answer)); - 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; @@ -374,7 +290,7 @@ int lxc_poll(const char *name, struct lxc_handler *handler) goto out_mainloop_open; } - if (lxc_command_mainloop_add(name, &descr, handler)) { + if (lxc_cmd_mainloop_add(name, &descr, handler)) { ERROR("failed to add command handler to mainloop"); goto out_mainloop_open; } @@ -426,7 +342,7 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char goto out_free; } - if (lxc_command_init(name, handler, lxcpath)) + if (lxc_cmd_init(name, handler, lxcpath)) goto out_free_name; if (lxc_read_seccomp_config(conf) != 0) { diff --git a/src/lxc/state.c b/src/lxc/state.c index 68ec00ba0..ed5953511 100644 --- a/src/lxc/state.c +++ b/src/lxc/state.c @@ -103,75 +103,14 @@ fail: return -1; } -static lxc_state_t __lxc_getstate(const char *name, const char *lxcpath) -{ - struct lxc_command command = { - .request = { .type = LXC_COMMAND_STATE }, - }; - - int ret, stopped = 0; - - ret = lxc_command(name, &command, &stopped, lxcpath); - if (ret < 0 && stopped) - return STOPPED; - - if (ret < 0) { - ERROR("failed to send command"); - return -1; - } - - if (!ret) { - WARN("'%s' has stopped before sending its state", name); - return -1; - } - - if (command.answer.ret < 0) { - ERROR("failed to get state for '%s': %s", - name, strerror(-command.answer.ret)); - return -1; - } - - DEBUG("'%s' is in '%s' state", name, lxc_state2str(command.answer.ret)); - - return command.answer.ret; -} - lxc_state_t lxc_getstate(const char *name, const char *lxcpath) { lxc_state_t state = freezer_state(name, lxcpath); if (state != FROZEN && state != FREEZING) - state = __lxc_getstate(name, lxcpath); + state = lxc_cmd_get_state(name, lxcpath); return state; } -/*---------------------------------------------------------------------------- - * functions used by lxc-start mainloop - * to handle above command request. - *--------------------------------------------------------------------------*/ -extern int lxc_state_callback(int fd, struct lxc_request *request, - struct lxc_handler *handler) -{ - struct lxc_answer answer; - int ret; - - memset(&answer, 0, sizeof(answer)); - answer.ret = handler->state; - - ret = send(fd, &answer, sizeof(answer), 0); - if (ret < 0) { - WARN("failed to send answer to the peer"); - goto out; - } - - if (ret != sizeof(answer)) { - ERROR("partial answer sent"); - goto out; - } - -out: - return ret; -} - static int fillwaitedstates(const char *strstates, int *states) { char *token, *saveptr = NULL; diff --git a/src/lxc/stop.c b/src/lxc/stop.c deleted file mode 100644 index 4fb4480f8..000000000 --- a/src/lxc/stop.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * lxc: linux Container library - * - * (C) Copyright IBM Corp. 2007, 2008 - * - * Authors: - * Daniel Lezcano - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "lxc.h" -#include "commands.h" - -lxc_log_define(lxc_stop, lxc); - -int lxc_stop(const char *name, const char *lxcpath) -{ - struct lxc_command command = { - .request = { .type = LXC_COMMAND_STOP }, - }; - - int ret, stopped = 0; - - ret = lxc_command(name, &command,&stopped, lxcpath); - if (ret < 0 && stopped) { - INFO("'%s' is already stopped", name); - return 0; - } - - if (ret < 0) { - ERROR("failed to send command"); - return -1; - } - - /* we do not expect any answer, because we wait for the connection to be - * closed - */ - if (ret > 0) { - ERROR("failed to stop '%s': %s", - name, strerror(-command.answer.ret)); - return -1; - } - - INFO("'%s' has stopped", name); - - return 0; -} - -/*---------------------------------------------------------------------------- - * functions used by lxc-start mainloop - * to handle above command request. - *--------------------------------------------------------------------------*/ -extern int lxc_stop_callback(int fd, struct lxc_request *request, - struct lxc_handler *handler) -{ - struct lxc_answer answer; - int ret; - int stopsignal = SIGKILL; - - if (handler->conf->stopsignal) - stopsignal = handler->conf->stopsignal; - memset(&answer, 0, sizeof(answer)); - answer.ret = kill(handler->pid, stopsignal); - if (!answer.ret) { - ret = lxc_unfreeze_bypath(handler->cgroup); - if (!ret) - return 0; - - ERROR("failed to unfreeze container"); - answer.ret = ret; - } - - ret = send(fd, &answer, sizeof(answer), 0); - if (ret < 0) { - WARN("failed to send answer to the peer"); - goto out; - } - - if (ret != sizeof(answer)) { - ERROR("partial answer sent"); - goto out; - } - -out: - return -1; -} - diff --git a/src/tests/cgpath.c b/src/tests/cgpath.c index d8c36248e..b7f088563 100644 --- a/src/tests/cgpath.c +++ b/src/tests/cgpath.c @@ -137,7 +137,8 @@ int main() } const char *dirpath; - if (lxc_get_cgpath(&dirpath, NULL, c2->name, c2->config_path) < 0) { + dirpath = lxc_cmd_get_cgroup_path(NULL, c2->name, c2->config_path); + if (!dirpath) { TSTERR("getting second container's cgpath"); goto out; } @@ -150,6 +151,8 @@ int main() retv = 0; out: + if (dirpath) + free(dirpath); if (c2) { c2->stop(c2); c2->destroy(c2); From fca3080f6a46f856c54218a8e478a174382b4c15 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 21 May 2013 13:15:53 -0400 Subject: [PATCH 130/325] fix minor gcc 4.7.2 error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lxccontainer.c:874:4: error: ‘for’ loop initial declarations are only allowed in C99 mode Signed-off-by: Dwight Engen Acked-by: Stéphane Graber --- src/lxc/lxccontainer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 7ce25c6d8..2934afa51 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -871,7 +871,8 @@ out: count++; temp = realloc(addresses, count * sizeof(*addresses)); if (!temp) { - for (int i = 0; i < count-1; i++) + int i; + for (i = 0; i < count-1; i++) free(addresses[i]); free(addresses); return NULL; From 20fe4e8febe40f6fc4e4c6f52b91f0af0232e6f5 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 21 May 2013 15:36:32 -0400 Subject: [PATCH 131/325] fix getline(3) memory leaks Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/attach.c | 2 +- src/lxc/cgroup.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lxc/attach.c b/src/lxc/attach.c index a7e907475..d8b4915d5 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -114,6 +114,7 @@ struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) } } + free(line); fclose(proc_file); if (!found) { @@ -145,7 +146,6 @@ struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) out_error: free(info); - free(line); return NULL; } diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c index 44266de62..2ffbb54b5 100644 --- a/src/lxc/cgroup.c +++ b/src/lxc/cgroup.c @@ -503,6 +503,7 @@ static char *get_all_cgroups(void) } out: + free(line); fclose(f); return ret; } From fa9ac567a7f1593c586cca57362f6b542985e5d7 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 21 May 2013 20:31:04 -0500 Subject: [PATCH 132/325] attach: and cgroup.c: be overly cautious Realistically (as Dwight points out) it doesn't seem possible that getline won't return at least one line in this functions, however just to make absolutely sure we don't get a segv on free(NULL), check line != NULL before freeing it on exit. Signed-off-by: Serge Hallyn --- src/lxc/attach.c | 3 ++- src/lxc/cgroup.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lxc/attach.c b/src/lxc/attach.c index d8b4915d5..a33d24f89 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -114,7 +114,8 @@ struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) } } - free(line); + if (line) + free(line); fclose(proc_file); if (!found) { diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c index 2ffbb54b5..bb1268b30 100644 --- a/src/lxc/cgroup.c +++ b/src/lxc/cgroup.c @@ -503,7 +503,8 @@ static char *get_all_cgroups(void) } out: - free(line); + if (line) + free(line); fclose(f); return ret; } From 481624b37b37ffa98b735cf3f94e35d1fbd729e0 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 21 May 2013 13:30:09 -0400 Subject: [PATCH 133/325] fix build with --enable-tests Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/lxccontainer.h | 1 + src/tests/cgpath.c | 3 ++- src/tests/clonetest.c | 10 +++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index bf3168cb9..22ccb0b72 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -103,6 +103,7 @@ struct lxc_container { * then the original backing store's size will be used if possible. Note this * only applies to the rootfs. For any other filesystems, the original size * will be duplicated. + * @hookargs: additional arguments to pass to the clone hook script */ struct lxc_container *(*clone)(struct lxc_container *c, const char *newname, const char *lxcpath, int flags, const char *bdevtype, diff --git a/src/tests/cgpath.c b/src/tests/cgpath.c index b7f088563..2497e9ee2 100644 --- a/src/tests/cgpath.c +++ b/src/tests/cgpath.c @@ -27,6 +27,7 @@ #include #include "../lxc/cgroup.h" #include "../lxc/lxc.h" +#include "../lxc/commands.h" #define MYNAME "lxctest1" #define MYNAME2 "lxctest2" @@ -39,6 +40,7 @@ int main() { struct lxc_container *c = NULL, *c2 = NULL; char *path; + char *dirpath = NULL; int len; int ret, retv = -1; @@ -136,7 +138,6 @@ int main() goto out; } - const char *dirpath; dirpath = lxc_cmd_get_cgroup_path(NULL, c2->name, c2->config_path); if (!dirpath) { TSTERR("getting second container's cgpath"); diff --git a/src/tests/clonetest.c b/src/tests/clonetest.c index 5644405e9..fbeacdf71 100644 --- a/src/tests/clonetest.c +++ b/src/tests/clonetest.c @@ -64,7 +64,7 @@ int main(int argc, char *argv[]) goto out; } - c2 = c->clone(c, MYNAME2, NULL, 0, NULL, NULL, 0); + c2 = c->clone(c, MYNAME2, NULL, 0, NULL, NULL, 0, NULL); if (!c2) { fprintf(stderr, "%d: %s clone returned NULL\n", __LINE__, MYNAME2); goto out; @@ -118,7 +118,7 @@ int main(int argc, char *argv[]) goto out; } - if ((c2 = c->clone(c, "clonetestlvm2", NULL, 0, NULL, NULL, 0)) == NULL) { + if ((c2 = c->clone(c, "clonetestlvm2", NULL, 0, NULL, NULL, 0, NULL)) == NULL) { fprintf(stderr, "lvm clone failed\n"); goto out; } @@ -134,7 +134,7 @@ int main(int argc, char *argv[]) c2 = NULL; } - if ((c2 = c->clone(c, "clonetestlvm3", NULL, LXC_CLONE_SNAPSHOT, NULL, NULL, 0)) == NULL) { + if ((c2 = c->clone(c, "clonetestlvm3", NULL, LXC_CLONE_SNAPSHOT, NULL, NULL, 0, NULL)) == NULL) { fprintf(stderr, "lvm clone failed\n"); goto out; } @@ -148,13 +148,13 @@ int main(int argc, char *argv[]) } // Now create an overlayfs clone of a dir-backed container - if ((c2 = c->clone(c, "clonetest-o1", NULL, LXC_CLONE_SNAPSHOT, "overlayfs", NULL, 0)) == NULL) { + if ((c2 = c->clone(c, "clonetest-o1", NULL, LXC_CLONE_SNAPSHOT, "overlayfs", NULL, 0, NULL)) == NULL) { fprintf(stderr, "overlayfs clone of dir failed\n"); goto out; } // Now create an overlayfs clone of the overlayfs clone - if ((c3 = c2->clone(c2, "clonetest-o2", NULL, LXC_CLONE_SNAPSHOT, "overlayfs", NULL, 0)) == NULL) { + if ((c3 = c2->clone(c2, "clonetest-o2", NULL, LXC_CLONE_SNAPSHOT, "overlayfs", NULL, 0, NULL)) == NULL) { fprintf(stderr, "overlayfs clone of overlayfs failed\n"); goto out; } From 65be441e0892b45000b9b3863d407539e56e47a4 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 21 May 2013 11:34:45 -0400 Subject: [PATCH 134/325] oracle template: mount /dev/shm as tmpfs sem_open(3) checks that /dev/shm is SHMFS_SUPER_MAGIC. Normally /dev/shm is mounted in the initramfs created by dracut, but that won't be run for a container so make sure that rc.sysinit mounts /dev/shm. Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- templates/lxc-oracle.in | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in index 946956d1c..70ef63245 100644 --- a/templates/lxc-oracle.in +++ b/templates/lxc-oracle.in @@ -142,6 +142,17 @@ EOF sed -i 's|action $"Setting network parameters|# LXC action $"Setting network parameters|' $container_rootfs/etc/init.d/NetworkManager 2>/dev/null fi + # sem_open(3) checks that /dev/shm is SHMFS_SUPER_MAGIC, so make sure to mount /dev/shm (normally done by dracut initrd) as tmpfs + if [ $container_release_major = "4" -o $container_release_major = "5" ]; then + echo "mount -t tmpfs tmpfs /dev/shm" >>$container_rootfs/etc/rc.sysinit + echo "mount -t tmpfs tmpfs /dev/shm" >>$container_rootfs/etc/rc.d/rc.sysinit + fi + + if [ $container_release_major = "6" ]; then + sed -i 's|mount -n -o remount /dev/shm >/dev/null 2>&1$|mount -t tmpfs tmpfs /dev/shm # LXC|' $container_rootfs/etc/rc.sysinit + sed -i 's|mount -n -o remount /dev/shm >/dev/null 2>&1$|mount -t tmpfs tmpfs /dev/shm # LXC|' $container_rootfs/etc/rc.d/rc.sysinit + fi + # no need to attempt to mount / sed -i 's|mount -f /$|# LXC mount -f /|' $container_rootfs/etc/rc.sysinit sed -i 's|mount -f /$|# LXC mount -f /|' $container_rootfs/etc/rc.d/rc.sysinit From 5bb4a226ebec9f3fb678a282a2b2833748d6707b Mon Sep 17 00:00:00 2001 From: "Michael H. Warfield" Date: Tue, 21 May 2013 14:17:25 -0400 Subject: [PATCH 135/325] lxc-fedora template. Cleanup for rootfs. This is just some minor changes in the way the Fedora template is synthesizing the target rootfs_path. Currently, the template uses a path with the container in it twice like this: /var/lib/lxc/rasputin/rasputin/rootfs This happens because the container name is already contained in the "path" and the template appends it a second time. This changes the logic to be congruent with other templates such as lxc-arch. The new behavior will be to create the rootfs like this: /var/lib/lxc/rasputin/rootfs Attached below the jump. Regards, Mike -- Michael H. Warfield (AI4NB) | (770) 985-6132 | mhw@WittsEnd.com /\/\|=mhw=|\/\/ | (678) 463-0932 | http://www.wittsend.com/mhw/ NIC whois: MHW9 | An optimist believes we live in the best of all PGP Key: 0x674627FF | possible worlds. A pessimist is sure of it! -- Signed-off-by: Michael H. Warfield Signed-off-by: Serge Hallyn --- templates/lxc-fedora.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/lxc-fedora.in b/templates/lxc-fedora.in index 481f7187b..871ae7a71 100644 --- a/templates/lxc-fedora.in +++ b/templates/lxc-fedora.in @@ -140,7 +140,7 @@ configure_fedora_systemd() { unlink ${rootfs_path}/etc/systemd/system/default.target touch ${rootfs_path}/etc/fstab - chroot ${rootfs_path} ln -s /dev/null //etc/systemd/system/udev.service + chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/udev.service chroot ${rootfs_path} ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target #dependency on a device unit fails it specially that we disabled udev # sed -i 's/After=dev-%i.device/After=/' ${rootfs_path}/lib/systemd/system/getty\@.service @@ -412,7 +412,7 @@ if [ -n "$needed_pkgs" ]; then fi if [ -z "$path" ]; then - path=$default_path + path=$default_path/$name fi if [ -z "$release" ]; then @@ -438,7 +438,7 @@ if [ "$(id -u)" != "0" ]; then fi -rootfs_path=$path/$name/rootfs +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 }'` From ad5f15151580201b79fc140f664227b494639e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 22 May 2013 22:28:43 -0400 Subject: [PATCH 136/325] python: Fix lxc-ls's usage of get_ips() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The recent port of get_ips() from pure python to the C API came with a couple of API changes for that function call (as were highlighted in the commit message). I somehow didn't notice that lxc-ls was still calling with the old API and so was crashing whenever it was asked to show the ipv4 or ipv6 address. Signed-off-by: Stéphane Graber --- src/lxc/lxc-ls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lxc/lxc-ls b/src/lxc/lxc-ls index d4e369b6f..491e95bee 100644 --- a/src/lxc/lxc-ls +++ b/src/lxc/lxc-ls @@ -202,10 +202,10 @@ for container_name in lxc.list_containers(config_path=lxcpath): entry['pid'] = str(container.init_pid) # Get the IPs - for protocol in ('ipv4', 'ipv6'): + for family, protocol in {'inet': 'ipv4', 'inet6': 'ipv6'}.items(): if protocol in args.fancy_format or args.nesting: entry[protocol] = "-" - ips = container.get_ips(protocol=protocol, timeout=1) + ips = container.get_ips(family=family) if ips: entry[protocol] = ", ".join(ips) From 6a44839f5973f41553349f1b5e77d8db809e60eb Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Thu, 23 May 2013 15:39:03 -0400 Subject: [PATCH 137/325] consolidate missing C library functions into utils.h This fixes the build of lxccontainer.c on systems that have __NR_setns but not HAVE_SETNS. Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/attach.c | 34 +--------------------------------- src/lxc/bdev.c | 15 +-------------- src/lxc/lxccontainer.c | 17 +---------------- src/lxc/parse.c | 8 +------- src/lxc/utils.h | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 41 insertions(+), 70 deletions(-) diff --git a/src/lxc/attach.c b/src/lxc/attach.c index a33d24f89..5061b93ab 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -46,42 +46,10 @@ #include "caps.h" #include "config.h" #include "apparmor.h" +#include "utils.h" lxc_log_define(lxc_attach, lxc); -/* Define setns() if missing from the C library */ -#ifndef HAVE_SETNS -static int setns(int fd, int nstype) -{ -#ifdef __NR_setns -return syscall(__NR_setns, fd, nstype); -#else -errno = ENOSYS; -return -1; -#endif -} -#endif - -/* Define unshare() if missing from the C library */ -#ifndef HAVE_UNSHARE -static int unshare(int flags) -{ -#ifdef __NR_unshare -return syscall(__NR_unshare, flags); -#else -errno = ENOSYS; -return -1; -#endif -} -#endif - -/* Define getline() if missing from the C library */ -#ifndef HAVE_GETLINE -#ifdef HAVE_FGETLN -#include <../include/getline.h> -#endif -#endif - struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) { struct lxc_proc_context_info *info = calloc(1, sizeof(*info)); diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index fcde16b51..a0ce5f522 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -44,23 +44,10 @@ #include "utils.h" #include "namespace.h" #include "parse.h" +#include "utils.h" lxc_log_define(bdev, lxc); -/* Define unshare() if missing from the C library */ -/* this is also in attach.c and lxccontainer.c: commonize it in utils.c */ -#ifndef HAVE_UNSHARE -static int unshare(int flags) -{ -#ifdef __NR_unshare -return syscall(__NR_unshare, flags); -#else -errno = ENOSYS; -return -1; -#endif -} -#endif - static int do_rsync(const char *src, const char *dest) { // call out to rsync diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 2934afa51..60b77b92d 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -35,6 +35,7 @@ #include "version.h" #include "log.h" #include "bdev.h" +#include "utils.h" #include #include #include @@ -44,22 +45,6 @@ static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; -/* Define unshare() if missing from the C library */ -/* this is also in attach.c and lxccontainer.c: commonize it in utils.c */ -#ifndef HAVE_UNSHARE -static int unshare(int flags) -{ -#ifdef __NR_unshare - return syscall(__NR_unshare, flags); -#else - errno = ENOSYS; - return -1; -#endif -} -#else -int unshare(int); -#endif - lxc_log_define(lxc_container, lxc); /* LOCKING diff --git a/src/lxc/parse.c b/src/lxc/parse.c index 4504ec292..6c2ed5a6c 100644 --- a/src/lxc/parse.c +++ b/src/lxc/parse.c @@ -30,15 +30,9 @@ #include "parse.h" #include "config.h" +#include "utils.h" #include -/* Define getline() if missing from the C library */ -#ifndef HAVE_GETLINE -#ifdef HAVE_FGETLN -#include <../include/getline.h> -#endif -#endif - /* Workaround for the broken signature of alphasort() in bionic. This was fixed upstream in 40e467ec668b59be25491bd44bf348a884d6a68d so the workaround can probably be dropped with the next version of the Android NDK. diff --git a/src/lxc/utils.h b/src/lxc/utils.h index d1242b1f5..fbfe5d3a0 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -23,7 +23,9 @@ #ifndef _utils_h #define _utils_h +#include #include +#include "config.h" extern int lxc_setup_fs(void); extern int get_u16(unsigned short *val, const char *arg, int base); @@ -36,6 +38,41 @@ extern const char *default_lxc_path(void); extern const char *default_zfs_root(void); extern const char *default_lvm_vg(void); +/* Define getline() if missing from the C library */ +#ifndef HAVE_GETLINE +#ifdef HAVE_FGETLN +#include <../include/getline.h> +#endif +#endif + +/* Define setns() if missing from the C library */ +#ifndef HAVE_SETNS +static inline int setns(int fd, int nstype) +{ +#ifdef __NR_setns + return syscall(__NR_setns, fd, nstype); +#else + errno = ENOSYS; + return -1; +#endif +} +#endif + +/* Define unshare() if missing from the C library */ +#ifndef HAVE_UNSHARE +static inline int unshare(int flags) +{ +#ifdef __NR_unshare + return syscall(__NR_unshare, flags); +#else + errno = ENOSYS; + return -1; +#endif +} +#else +int unshare(int); +#endif + /** * BUILD_BUG_ON - break compile if a condition is true. * @condition: the condition which the compiler should know is false. From 2acf77955239ec0046451fa16812d2884e6bd19b Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Thu, 23 May 2013 15:44:39 -0400 Subject: [PATCH 138/325] fix memory leaks in cgroup functions There were several memory leaks in the cgroup functions, notably in the success cases. The cgpath test program was refactored and additional tests added to it. It was used in various modes under valgrind to test that the leaks were fixed. Simplify lxc_cgroup_path_get() and cgroup_path_get by having them return a char * instead of an int and an output char * argument. The only return values ever used were -1 and 0, which are now handled with NULL and non-NULL returns respectively. Use consistent variable names of cgabspath when refering to an absolute path to a cgroup subsystem or file, and cgrelpath when refering to a container "group/name" within the cgroup heirarchy. Remove unused subsystem argument to lxc_cmd_get_cgroup_path(). Remove unused #define MAXPRIOLEN Make template arg to lxcapi_create() const Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/cgroup.c | 270 ++++++++++++++-------------- src/lxc/cgroup.h | 6 +- src/lxc/commands.c | 4 +- src/lxc/commands.h | 3 +- src/lxc/freezer.c | 36 ++-- src/lxc/lxccontainer.c | 6 +- src/lxc/lxccontainer.h | 4 +- src/lxc/state.c | 46 ++--- src/tests/cgpath.c | 400 ++++++++++++++++++++++++++++++----------- 9 files changed, 481 insertions(+), 294 deletions(-) diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c index bb1268b30..f04d59a38 100644 --- a/src/lxc/cgroup.c +++ b/src/lxc/cgroup.c @@ -147,102 +147,113 @@ out: } /* - * cgroup_path_get: Calculate the full path for a particular subsystem, plus - * a passed-in (to be appended) relative cgpath for a container. - * @path: a char** into which a pointer to the answer is copied - * @subsystem: subsystem of interest (i.e. freezer). - * @cgpath: a container's (relative) cgroup path, i.e. "/lxc/c1". + * cgroup_path_get: Get the absolute path to a particular subsystem, + * plus a passed-in (to be appended) relative cgpath for a container. * - * Returns 0 on success, -1 on error. + * @subsystem : subsystem of interest (e.g. "freezer") + * @cgrelpath : a container's relative cgroup path (e.g. "lxc/c1") * + * Returns absolute path on success, NULL on error. The caller must free() + * the returned path. + * + * Note that @subsystem may be the name of an item (e.g. "freezer.state") + * in which case the subsystem will be determined by taking the string up + * to the first '.' */ -extern int cgroup_path_get(char **path, const char *subsystem, const char *cgpath) +char *cgroup_path_get(const char *subsystem, const char *cgrelpath) { int rc; char *buf = NULL; - char *retbuf = NULL; + char *cgabspath = NULL; buf = malloc(MAXPATHLEN * sizeof(char)); if (!buf) { ERROR("malloc failed"); - goto fail; + goto out1; } - retbuf = malloc(MAXPATHLEN * sizeof(char)); - if (!retbuf) { + cgabspath = malloc(MAXPATHLEN * sizeof(char)); + if (!cgabspath) { ERROR("malloc failed"); - goto fail; + goto out2; } /* lxc_cgroup_set passes a state object for the subsystem, * so trim it to just the subsystem part */ if (subsystem) { - rc = snprintf(retbuf, MAXPATHLEN, "%s", subsystem); + rc = snprintf(cgabspath, MAXPATHLEN, "%s", subsystem); if (rc < 0 || rc >= MAXPATHLEN) { ERROR("subsystem name too long"); - goto fail; + goto err3; } - char *s = index(retbuf, '.'); + char *s = index(cgabspath, '.'); if (s) *s = '\0'; - DEBUG("%s: called for subsys %s name %s\n", __func__, retbuf, cgpath); + DEBUG("%s: called for subsys %s name %s\n", __func__, + subsystem, cgrelpath); } - if (get_cgroup_mount(subsystem ? retbuf : NULL, buf)) { + if (get_cgroup_mount(subsystem ? cgabspath : NULL, buf)) { ERROR("cgroup is not mounted"); - goto fail; + goto err3; } - rc = snprintf(retbuf, MAXPATHLEN, "%s/%s", buf, cgpath); + rc = snprintf(cgabspath, MAXPATHLEN, "%s/%s", buf, cgrelpath); if (rc < 0 || rc >= MAXPATHLEN) { ERROR("name too long"); - goto fail; + goto err3; } - DEBUG("%s: returning %s for subsystem %s", __func__, retbuf, subsystem); + DEBUG("%s: returning %s for subsystem %s relpath %s", __func__, + cgabspath, subsystem, cgrelpath); + goto out2; - if(buf) - free(buf); - - *path = retbuf; - return 0; -fail: - if (buf) - free(buf); - if (retbuf) - free(retbuf); - return -1; +err3: + free(cgabspath); + cgabspath = NULL; +out2: + free(buf); +out1: + return cgabspath; } /* - * lxc_cgroup_path_get: determine full pathname for a cgroup - * file for a specific container. - * @path: char ** used to return the answer. - * @subsystem: cgroup subsystem (i.e. "freezer") for which to - * return an answer. If NULL, then the first cgroup entry in - * mtab will be used. + * lxc_cgroup_path_get: Get the absolute pathname for a cgroup + * file for a running container. + * + * @subsystem : subsystem of interest (e.g. "freezer"). If NULL, then + * the first cgroup entry in mtab will be used. + * @name : name of container to connect to + * @lxcpath : the lxcpath in which the container is running * * This is the exported function, which determines cgpath from the - * monitor running in lxcpath. + * lxc-start of the @name container running in @lxcpath. * - * Returns 0 on success, < 0 on error. + * Returns path on success, NULL on error. The caller must free() + * the returned path. */ -int lxc_cgroup_path_get(char **path, const char *subsystem, const char *name, const char *lxcpath) +char *lxc_cgroup_path_get(const char *subsystem, const char *name, + const char *lxcpath) { - int ret; - char *cgpath; + char *cgabspath; + char *cgrelpath; - cgpath = lxc_cmd_get_cgroup_path(subsystem, name, lxcpath); - if (!cgpath) - return -1; + cgrelpath = lxc_cmd_get_cgroup_path(name, lxcpath); + if (!cgrelpath) + return NULL; - ret = cgroup_path_get(path, subsystem, cgpath); - free(cgpath); - return ret; + cgabspath = cgroup_path_get(subsystem, cgrelpath); + free(cgrelpath); + return cgabspath; } /* - * small helper which simply write a value into a (cgroup) file + * do_cgroup_set: Write a value into a cgroup file + * + * @path : absolute path to cgroup file + * @value : value to write into file + * + * Returns 0 on success, < 0 on error. */ static int do_cgroup_set(const char *path, const char *value) { @@ -267,87 +278,86 @@ static int do_cgroup_set(const char *path, const char *value) } /* - * small helper to write a value into a file in a particular directory. - * @cgpath: the directory in which to find the file - * @filename: the file (under cgpath) to which to write - * @value: what to write + * lxc_cgroup_set_bypath: Write a value into a cgroup file + * + * @cgrelpath : a container's relative cgroup path (e.g. "lxc/c1") + * @filename : the cgroup file to write (e.g. "freezer.state") + * @value : value to write into file * * Returns 0 on success, < 0 on error. */ -int lxc_cgroup_set_bypath(const char *cgpath, const char *filename, const char *value) +int lxc_cgroup_set_bypath(const char *cgrelpath, const char *filename, const char *value) { int ret; - char *dirpath = NULL; + char *cgabspath; char path[MAXPATHLEN]; - ret = cgroup_path_get(&dirpath, filename, cgpath); - if (ret) - goto fail; + cgabspath = cgroup_path_get(filename, cgrelpath); + if (!cgabspath) + return -1; - ret = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename); + ret = snprintf(path, MAXPATHLEN, "%s/%s", cgabspath, filename); if (ret < 0 || ret >= MAXPATHLEN) { ERROR("pathname too long"); - goto fail; + ret = -1; + goto out; } - return do_cgroup_set(path, value); + ret = do_cgroup_set(path, value); -fail: - if(dirpath) - free(dirpath); - return -1; +out: + free(cgabspath); + return ret; } /* - * set a cgroup value for a container + * lxc_cgroup_set: Write a value into a cgroup file * - * @name: name of the container - * @filename: the cgroup file (i.e. freezer.state) whose value to change - * @value: the value to write to the file - * @lxcpath: the lxcpath under which the container is running. + * @name : name of container to connect to + * @filename : the cgroup file to write (e.g. "freezer.state") + * @value : value to write into file + * @lxcpath : the lxcpath in which the container is running * * Returns 0 on success, < 0 on error. */ - int lxc_cgroup_set(const char *name, const char *filename, const char *value, const char *lxcpath) { int ret; - char *dirpath = NULL; + char *cgabspath; char path[MAXPATHLEN]; - ret = lxc_cgroup_path_get(&dirpath, filename, name, lxcpath); - if (ret) - goto fail; + cgabspath = lxc_cgroup_path_get(filename, name, lxcpath); + if (!cgabspath) + return -1; - ret = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename); + ret = snprintf(path, MAXPATHLEN, "%s/%s", cgabspath, filename); if (ret < 0 || ret >= MAXPATHLEN) { ERROR("pathname too long"); - goto fail; + ret = -1; + goto out; } - return do_cgroup_set(path, value); + ret = do_cgroup_set(path, value); -fail: - if(dirpath) - free(dirpath); - return -1; +out: + free(cgabspath); + return ret; } /* - * Get value of a cgroup setting for a container. + * lxc_cgroup_get: Read value from a cgroup file * - * @name: name of the container - * @filename: the cgroup file to read (i.e. 'freezer.state') - * @value: a preallocated char* into which to copy the answer - * @len: the length of pre-allocated @value - * @lxcpath: the lxcpath in which the container is running (i.e. - * /var/lib/lxc) + * @name : name of container to connect to + * @filename : the cgroup file to read (e.g. "freezer.state") + * @value : a pre-allocated buffer to copy the answer into + * @len : the length of pre-allocated @value + * @lxcpath : the lxcpath in which the container is running * - * Returns < 0 on error, or the number of bytes read. + * Returns the number of bytes read on success, < 0 on error * - * If you pass in NULL value or 0 len, then you are asking for the size of the - * file. + * If you pass in NULL value or 0 len, the return value will be the size of + * the file, and @value will not contain the contents. * * 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 @@ -357,25 +367,26 @@ fail: int lxc_cgroup_get(const char *name, const char *filename, char *value, size_t len, const char *lxcpath) { - int fd, ret = -1; - char *dirpath = NULL; + int fd, ret; + char *cgabspath; char path[MAXPATHLEN]; - int rc; - ret = lxc_cgroup_path_get(&dirpath, filename, name, lxcpath); - if (ret) - goto fail; + cgabspath = lxc_cgroup_path_get(filename, name, lxcpath); + if (!cgabspath) + return -1; - rc = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename); - if (rc < 0 || rc >= MAXPATHLEN) { + ret = snprintf(path, MAXPATHLEN, "%s/%s", cgabspath, filename); + if (ret < 0 || ret >= MAXPATHLEN) { ERROR("pathname too long"); - goto fail; + ret = -1; + goto out; } fd = open(path, O_RDONLY); if (fd < 0) { ERROR("open %s : %s", path, strerror(errno)); - goto fail; + ret = -1; + goto out; } if (!len || !value) { @@ -394,47 +405,45 @@ int lxc_cgroup_get(const char *name, const char *filename, char *value, ERROR("read %s : %s", path, strerror(errno)); close(fd); +out: + free(cgabspath); return ret; -fail: - if(dirpath) - free(dirpath); - return -1; } -int lxc_cgroup_nrtasks(const char *cgpath) +int lxc_cgroup_nrtasks(const char *cgrelpath) { - char *dirpath = NULL; + char *cgabspath = NULL; char path[MAXPATHLEN]; - int pid, ret, count = 0; + int pid, ret; FILE *file; - int rc; - ret = cgroup_path_get(&dirpath, NULL, cgpath); - if (ret) - goto fail; + cgabspath = cgroup_path_get(NULL, cgrelpath); + if (!cgabspath) + return -1; - rc = snprintf(path, MAXPATHLEN, "%s/tasks", dirpath); - if (rc < 0 || rc >= MAXPATHLEN) { + ret = snprintf(path, MAXPATHLEN, "%s/tasks", cgabspath); + if (ret < 0 || ret >= MAXPATHLEN) { ERROR("pathname too long"); - goto fail; + ret = -1; + goto out; } file = fopen(path, "r"); if (!file) { SYSERROR("fopen '%s' failed", path); - return -1; + ret = -1; + goto out; } + ret = 0; while (fscanf(file, "%d", &pid) != EOF) - count++; + ret++; fclose(file); - return count; -fail: - if(dirpath) - free(dirpath); - return -1; +out: + free(cgabspath); + return ret; } /* @@ -654,12 +663,12 @@ char *lxc_cgroup_path_create(const char *lxcgroup, const char *name) char buf[LARGE_MAXPATHLEN] = {0}; - if (create_lxcgroups(lxcgroup) < 0) - return NULL; - if (!allcgroups) return NULL; + if (create_lxcgroups(lxcgroup) < 0) + goto err1; + again: if (visited) { /* we're checking for a new name, so start over with all cgroup @@ -670,9 +679,7 @@ again: file = setmntent(MTAB, "r"); if (!file) { SYSERROR("failed to open %s", MTAB); - if (allcgroups) - free(allcgroups); - return NULL; + goto err1; } if (i) @@ -730,6 +737,7 @@ next: fail: endmntent(file); +err1: free(allcgroups); if (visited) free(visited); @@ -880,7 +888,7 @@ int lxc_cgroup_attach(pid_t pid, const char *name, const char *lxcpath) int ret; char *dirpath; - dirpath = lxc_cmd_get_cgroup_path(NULL, name, lxcpath); + dirpath = lxc_cmd_get_cgroup_path(name, lxcpath); if (!dirpath) { ERROR("Error getting cgroup for container %s: %s", lxcpath, name); return -1; diff --git a/src/lxc/cgroup.h b/src/lxc/cgroup.h index 971311ece..747ff5c0d 100644 --- a/src/lxc/cgroup.h +++ b/src/lxc/cgroup.h @@ -23,15 +23,13 @@ #ifndef _cgroup_h #define _cgroup_h -#define MAXPRIOLEN 24 - struct lxc_handler; extern int lxc_cgroup_destroy(const char *cgpath); -extern int lxc_cgroup_path_get(char **path, const char *subsystem, const char *name, +extern char *lxc_cgroup_path_get(const char *subsystem, const char *name, const char *lxcpath); extern int lxc_cgroup_nrtasks(const char *cgpath); extern char *lxc_cgroup_path_create(const char *lxcgroup, const char *name); extern int lxc_cgroup_enter(const char *cgpath, pid_t pid); extern int lxc_cgroup_attach(pid_t pid, const char *name, const char *lxcpath); -extern int cgroup_path_get(char **path, const char *subsystem, const char *cgpath); +extern char *cgroup_path_get(const char *subsystem, const char *cgpath); #endif diff --git a/src/lxc/commands.c b/src/lxc/commands.c index 3f2148858..169914e18 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -335,15 +335,13 @@ static int lxc_cmd_get_clone_flags_callback(int fd, struct lxc_cmd_req *req, * particular subsystem. This is the cgroup path relative to the root * of the cgroup filesystem. * - * @subsystem : the cgroup subsystem of interest (i.e. freezer) * @name : name of container to connect to * @lxcpath : the lxcpath in which the container is running * * Returns the path on success, NULL on failure. The caller must free() the * returned path. */ -char *lxc_cmd_get_cgroup_path(const char *subsystem, const char *name, - const char *lxcpath) +char *lxc_cmd_get_cgroup_path(const char *name, const char *lxcpath) { int ret, stopped = 0; struct lxc_cmd_rr cmd = { diff --git a/src/lxc/commands.h b/src/lxc/commands.h index b5b478878..08bde9cb6 100644 --- a/src/lxc/commands.h +++ b/src/lxc/commands.h @@ -67,8 +67,7 @@ struct lxc_cmd_console_rsp_data { extern int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath); -extern char *lxc_cmd_get_cgroup_path(const char *subsystem, - const char *name, const char *lxcpath); +extern char *lxc_cmd_get_cgroup_path(const char *name, const char *lxcpath); extern int lxc_cmd_get_clone_flags(const char *name, const char *lxcpath); extern char *lxc_cmd_get_config_item(const char *name, const char *item, const char *lxcpath); extern pid_t lxc_cmd_get_init_pid(const char *name, const char *lxcpath); diff --git a/src/lxc/freezer.c b/src/lxc/freezer.c index 35bf3a764..37a07fd40 100644 --- a/src/lxc/freezer.c +++ b/src/lxc/freezer.c @@ -120,19 +120,16 @@ out: static int freeze_unfreeze(const char *name, int freeze, const char *lxcpath) { - char *nsgroup = NULL; + char *cgabspath; int ret; - - ret = lxc_cgroup_path_get(&nsgroup, "freezer", name, lxcpath); - if (ret) - goto fail; - return do_unfreeze(nsgroup, freeze, name, lxcpath); + cgabspath = lxc_cgroup_path_get("freezer", name, lxcpath); + if (!cgabspath) + return -1; -fail: - if (nsgroup) - free(nsgroup); - return -1; + ret = do_unfreeze(cgabspath, freeze, name, lxcpath); + free(cgabspath); + return ret; } int lxc_freeze(const char *name, const char *lxcpath) @@ -146,19 +143,16 @@ int lxc_unfreeze(const char *name, const char *lxcpath) return freeze_unfreeze(name, 0, lxcpath); } -int lxc_unfreeze_bypath(const char *cgpath) +int lxc_unfreeze_bypath(const char *cgrelpath) { - char *nsgroup = NULL; + char *cgabspath; int ret; - - ret = cgroup_path_get(&nsgroup, "freezer", cgpath); - if (ret) - goto fail; - return do_unfreeze(nsgroup, 0, NULL, NULL); + cgabspath = cgroup_path_get("freezer", cgrelpath); + if (!cgabspath) + return -1; -fail: - if (nsgroup) - free(nsgroup); - return -1; + ret = do_unfreeze(cgabspath, 0, NULL, NULL); + free(cgabspath); + return ret; } diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 60b77b92d..1c0b7b298 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -555,7 +555,7 @@ static bool create_container_dir(struct lxc_container *c) * 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 *const argv[]) +static bool lxcapi_create(struct lxc_container *c, const char *t, char *const argv[]) { bool bret = false; pid_t pid; @@ -621,7 +621,7 @@ static bool lxcapi_create(struct lxc_container *c, char *t, char *const argv[]) newargv = malloc(nargs * sizeof(*newargv)); if (!newargv) exit(1); - newargv[0] = t; + newargv[0] = (char *)t; len = strlen(c->config_path) + strlen(c->name) + strlen("--path=") + 2; patharg = malloc(len); @@ -702,7 +702,7 @@ static bool lxcapi_shutdown(struct lxc_container *c, int timeout) return retv; } -static bool lxcapi_createl(struct lxc_container *c, char *t, ...) +static bool lxcapi_createl(struct lxc_container *c, const char *t, ...) { bool bret = false; char **args = NULL, **temp; diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index 22ccb0b72..1c464fe20 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -48,8 +48,8 @@ struct lxc_container { bool (*set_config_item)(struct lxc_container *c, const char *key, const char *value); bool (*destroy)(struct lxc_container *c); bool (*save_config)(struct lxc_container *c, const char *alt_file); - bool (*create)(struct lxc_container *c, char *t, char *const argv[]); - bool (*createl)(struct lxc_container *c, char *t, ...); + bool (*create)(struct lxc_container *c, const char *t, char *const argv[]); + bool (*createl)(struct lxc_container *c, const 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 */ diff --git a/src/lxc/state.c b/src/lxc/state.c index ed5953511..347477cf1 100644 --- a/src/lxc/state.c +++ b/src/lxc/state.c @@ -69,38 +69,40 @@ lxc_state_t lxc_str2state(const char *state) static lxc_state_t freezer_state(const char *name, const char *lxcpath) { - char *nsgroup = NULL; + char *cgabspath = NULL; char freezer[MAXPATHLEN]; char status[MAXPATHLEN]; FILE *file; - int err; + int ret; - err = lxc_cgroup_path_get(&nsgroup, "freezer", name, lxcpath); - if (err) - goto fail; + cgabspath = lxc_cgroup_path_get("freezer", name, lxcpath); + if (!cgabspath) + return -1; - err = snprintf(freezer, MAXPATHLEN, "%s/freezer.state", nsgroup); - if (err < 0 || err >= MAXPATHLEN) - goto fail; + ret = snprintf(freezer, MAXPATHLEN, "%s/freezer.state", cgabspath); + if (ret < 0 || ret >= MAXPATHLEN) + goto out; file = fopen(freezer, "r"); - if (!file) - goto fail; - - err = fscanf(file, "%s", status); - fclose(file); - - if (err == EOF) { - SYSERROR("failed to read %s", freezer); - goto fail; + if (!file) { + ret = -1; + goto out; } - return lxc_str2state(status); + ret = fscanf(file, "%s", status); + fclose(file); -fail: - if (nsgroup) - free(nsgroup); - return -1; + if (ret == EOF) { + SYSERROR("failed to read %s", freezer); + ret = -1; + goto out; + } + + ret = lxc_str2state(status); + +out: + free(cgabspath); + return ret; } lxc_state_t lxc_getstate(const char *name, const char *lxcpath) diff --git a/src/tests/cgpath.c b/src/tests/cgpath.c index 2497e9ee2..5fb1d319d 100644 --- a/src/tests/cgpath.c +++ b/src/tests/cgpath.c @@ -18,6 +18,7 @@ */ #include "../lxc/lxccontainer.h" +#include #include #include #include @@ -30,19 +31,279 @@ #include "../lxc/commands.h" #define MYNAME "lxctest1" -#define MYNAME2 "lxctest2" -#define TSTERR(x) do { \ - fprintf(stderr, "%d: %s\n", __LINE__, x); \ +#define TSTERR(fmt, ...) do { \ + fprintf(stderr, "%s:%d " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ } while (0) +/* + * test_same_name: Create the same named container within a group + * + * @group : name of the container group or NULL for default "lxc" + * @name : name of the container + * + * Note, lxc will append a - to duplicate container names. This is what + * is tested here. + * + * Returns 0 on success, < 0 on failure + */ +static int test_same_name(const char *group, const char *name) +{ + int ret = -1; + char *cgrelpath1; + char *cgrelpath2; + char relpath[PATH_MAX+1]; + + sprintf(relpath, "%s/%s-1", group ? group : "lxc", name); + + cgrelpath1 = lxc_cgroup_path_create(group, name); + if (!cgrelpath1) { + TSTERR("lxc_cgroup_path_create returned NULL"); + goto err1; + } + cgrelpath2 = lxc_cgroup_path_create(group, name); + if (!cgrelpath2) { + TSTERR("lxc_cgroup_path_create returned NULL"); + goto err2; + } + if (strcmp(cgrelpath2, relpath)) { + TSTERR("unexpected name for duplicate %s", cgrelpath2); + goto err3; + } + + ret = 0; +err3: + lxc_cgroup_destroy(cgrelpath2); + free(cgrelpath2); +err2: + lxc_cgroup_destroy(cgrelpath1); + free(cgrelpath1); +err1: + return ret; +} + +/* + * test_basic: Test cgroup functions that don't require a running container + * + * @group : name of the container group or NULL for default "lxc" + * @name : name of the container + * + * Returns 0 on success, < 0 on failure + */ +static int test_basic(const char *group, const char *name) +{ + int ret = -1; + char *cgabspath; + char *cgrelpath; + char relpath[PATH_MAX+1]; + + sprintf(relpath, "%s/%s", group ? group : "lxc", name); + + cgrelpath = lxc_cgroup_path_create(group, name); + if (!cgrelpath) { + TSTERR("lxc_cgroup_path_create returned NULL"); + goto err1; + } + if (!strstr(cgrelpath, relpath)) { + TSTERR("lxc_cgroup_path_create %s not in %s", relpath, cgrelpath); + goto err2; + } + + cgabspath = cgroup_path_get("freezer", cgrelpath); + if (!cgabspath) { + TSTERR("cgroup_path_get returned NULL"); + goto err2; + } + if (!strstr(cgabspath, relpath)) { + TSTERR("cgroup_path_get %s not in %s", relpath, cgabspath); + goto err3; + } + + ret = lxc_cgroup_destroy(cgrelpath); + if (ret < 0) { + TSTERR("lxc_cgroup_destroy failed"); + goto err3; + } +err3: + free(cgabspath); +err2: + free(cgrelpath); +err1: + return ret; +} + +/* + * test_running_container: test cgroup functions against a running container + * + * @group : name of the container group or NULL for default "lxc" + * @name : name of the container + */ +static int test_running_container(const char *lxcpath, + const char *group, const char *name) +{ + int nrtasks, ret = -1; + struct lxc_container *c = NULL; + char *cgrelpath; + char *cgabspath; + char relpath[PATH_MAX+1]; + char abspath[PATH_MAX+1]; + char value[NAME_MAX], value_save[NAME_MAX]; + + sprintf(relpath, "%s/%s", group ? group : "lxc", name); + + if ((c = lxc_container_new(name, lxcpath)) == NULL) { + TSTERR("container %s couldn't instantiate", name); + goto err1; + } + if (!c->is_defined(c)) { + TSTERR("container %s does not exist", name); + goto err2; + } + + cgrelpath = lxc_cmd_get_cgroup_path(c->name, c->config_path); + if (!cgrelpath) { + TSTERR("lxc_cmd_get_cgroup_path returned NULL"); + goto err2; + } + if (!strstr(cgrelpath, relpath)) { + TSTERR("lxc_cmd_get_cgroup_path %s not in %s", relpath, cgrelpath); + goto err3; + } + + /* test get/set value using memory.swappiness file */ + ret = lxc_cgroup_get(c->name, "memory.swappiness", value, + sizeof(value), c->config_path); + if (ret < 0) { + TSTERR("lxc_cgroup_get failed"); + goto err3; + } + strcpy(value_save, value); + + ret = lxc_cgroup_set_bypath(cgrelpath, "memory.swappiness", "100"); + if (ret < 0) { + TSTERR("lxc_cgroup_set_bypath failed"); + goto err3; + } + ret = lxc_cgroup_get(c->name, "memory.swappiness", value, + sizeof(value), c->config_path); + if (ret < 0) { + TSTERR("lxc_cgroup_get failed"); + goto err3; + } + if (strcmp(value, "100\n")) { + TSTERR("lxc_cgroup_set_bypath failed to set value >%s<", value); + goto err3; + } + + /* restore original value */ + ret = lxc_cgroup_set(c->name, "memory.swappiness", value_save, + c->config_path); + if (ret < 0) { + TSTERR("lxc_cgroup_set failed"); + goto err3; + } + ret = lxc_cgroup_get(c->name, "memory.swappiness", value, + sizeof(value), c->config_path); + if (ret < 0) { + TSTERR("lxc_cgroup_get failed"); + goto err3; + } + if (strcmp(value, value_save)) { + TSTERR("lxc_cgroup_set failed to set value >%s<", value); + goto err3; + } + + nrtasks = lxc_cgroup_nrtasks(cgrelpath); + if (nrtasks < 1) { + TSTERR("failed getting nrtasks"); + goto err3; + } + + cgabspath = lxc_cgroup_path_get("freezer", c->name, c->config_path); + if (!cgabspath) { + TSTERR("lxc_cgroup_path_get returned NULL"); + goto err3; + } + sprintf(abspath, "%s/%s/%s", "freezer", group ? group : "lxc", c->name); + if (!strstr(cgabspath, abspath)) { + TSTERR("lxc_cgroup_path_get %s not in %s", abspath, cgabspath); + goto err4; + } + + free(cgabspath); + cgabspath = lxc_cgroup_path_get("freezer.state", c->name, c->config_path); + if (!cgabspath) { + TSTERR("lxc_cgroup_path_get returned NULL"); + goto err3; + } + sprintf(abspath, "%s/%s/%s", "freezer", group ? group : "lxc", c->name); + if (!strstr(cgabspath, abspath)) { + TSTERR("lxc_cgroup_path_get %s not in %s", abspath, cgabspath); + goto err4; + } + + ret = 0; +err4: + free(cgabspath); +err3: + free(cgrelpath); +err2: + lxc_container_put(c); +err1: + return ret; +} + +static int test_container(const char *lxcpath, + const char *group, const char *name, + const char *template) +{ + int ret; + struct lxc_container *c = NULL; + + if (lxcpath) { + ret = mkdir(lxcpath, 0755); + if (ret < 0 && errno != EEXIST) { + TSTERR("failed to mkdir %s %s", lxcpath, strerror(errno)); + goto out1; + } + } + ret = -1; + + if ((c = lxc_container_new(name, lxcpath)) == NULL) { + TSTERR("instantiating container %s", name); + goto out1; + } + if (c->is_defined(c)) { + c->stop(c); + c->destroy(c); + c = lxc_container_new(name, lxcpath); + } + c->set_config_item(c, "lxc.network.type", "empty"); + if (!c->createl(c, template, NULL)) { + TSTERR("creating container %s", name); + goto out2; + } + c->load_config(c, NULL); + c->want_daemonize(c); + if (!c->startl(c, 0, NULL)) { + TSTERR("starting container %s", name); + goto out3; + } + + ret = test_running_container(lxcpath, group, name); + + c->stop(c); +out3: + c->destroy(c); +out2: + lxc_container_put(c); +out1: + return ret; +} + int main() { - struct lxc_container *c = NULL, *c2 = NULL; - char *path; - char *dirpath = NULL; - int len; - int ret, retv = -1; + int ret = EXIT_FAILURE; /* won't require privilege necessarily once users are classified by * pam_cgroup */ @@ -51,116 +312,43 @@ int main() exit(0); } - printf("Basic cgroup path tests...\n"); - path = lxc_cgroup_path_create(NULL, MYNAME); - if (!path || !(len = strlen(path))) { - TSTERR("zero result from lxc_cgroup_path_create"); - exit(1); - } - if (!strstr(path, "lxc/" MYNAME)) { - TSTERR("lxc_cgroup_path_create NULL lxctest1"); - exit(1); - } - free(path); - - path = lxc_cgroup_path_create("ab", MYNAME); - if (!path || !(len = strlen(path))) { - TSTERR("zero result from lxc_cgroup_path_create"); - exit(1); - } - if (!strstr(path, "ab/" MYNAME)) { - TSTERR("lxc_cgroup_path_create ab lxctest1"); - exit(1); - } - free(path); - printf("... passed\n"); - - printf("Container creation tests...\n"); - - if ((c = lxc_container_new(MYNAME, NULL)) == NULL) { - TSTERR("instantiating first container"); - exit(1); - } - if (c->is_defined(c)) { - c->stop(c); - c->destroy(c); - c = lxc_container_new(MYNAME, NULL); - } - c->set_config_item(c, "lxc.network.type", "empty"); - if (!c->createl(c, "ubuntu", NULL)) { - TSTERR("creating first container"); + if (test_basic(NULL, MYNAME) < 0) goto out; - } - c->load_config(c, NULL); - c->want_daemonize(c); - if (!c->startl(c, 0, NULL)) { - TSTERR("starting first container"); + if (test_basic("ab", MYNAME) < 0) goto out; - } - printf("first container passed. Now two containers...\n"); + printf("Basic cgroup path tests...Passed\n"); - char *nsgroup; -#define ALTBASE "/var/lib/lxctest2" - ret = mkdir(ALTBASE, 0755); - - ret = lxc_cgroup_path_get(&nsgroup, "freezer", MYNAME, c->get_config_path(c)); - if (ret < 0 || !strstr(nsgroup, "lxc/" MYNAME)) { - TSTERR("getting first cgroup path from lxc_command"); + if (test_same_name(NULL, MYNAME) < 0) goto out; - } - - /* start second container */ - if ((c2 = lxc_container_new(MYNAME2, ALTBASE)) == NULL) { - TSTERR("instantiating second container"); + if (test_same_name("ab", MYNAME) < 0) goto out; - } - if (c2->is_defined(c2)) { - c2->stop(c2); - c2->destroy(c2); - c2 = lxc_container_new(MYNAME2, ALTBASE); - } - c2->set_config_item(c2, "lxc.network.type", "empty"); - if (!c2->createl(c2, "ubuntu", NULL)) { - TSTERR("creating second container"); - goto out; - } + printf("Same name tests...Passed\n"); - c2->load_config(c2, NULL); - c2->want_daemonize(c2); - if (!c2->startl(c2, 0, NULL)) { - TSTERR("starting second container"); - goto out; - } + #if TEST_ALREADY_RUNNING_CT - ret = lxc_cgroup_path_get(&nsgroup, "freezer", MYNAME2, c2->get_config_path(c2)); - if (ret < 0 || !strstr(nsgroup, "lxc/" MYNAME2)) { - TSTERR("getting second cgroup path from lxc_command"); + /* + * This is useful for running with valgrind to test for memory + * leaks. The container should already be running, we can't start + * the container ourselves because valgrind gets confused by lxc's + * internal calls to clone. + */ + if (test_running_container(NULL, NULL, "bb01") < 0) goto out; - } + printf("Running container cgroup tests...Passed\n"); - dirpath = lxc_cmd_get_cgroup_path(NULL, c2->name, c2->config_path); - if (!dirpath) { - TSTERR("getting second container's cgpath"); + #else + + if (test_container(NULL, NULL, MYNAME, "busybox") < 0) goto out; - } + printf("Container creation tests...Passed\n"); - if (lxc_cgroup_nrtasks(dirpath) < 1) { - TSTERR("getting nrtasks"); + if (test_container("/var/lib/lxctest2", NULL, MYNAME, "busybox") < 0) goto out; - } - printf("...passed\n"); + printf("Container creation with LXCPATH tests...Passed\n"); - retv = 0; + #endif + + ret = EXIT_SUCCESS; out: - if (dirpath) - free(dirpath); - if (c2) { - c2->stop(c2); - c2->destroy(c2); - } - if (c) { - c->stop(c); - c->destroy(c); - } - return retv; + return ret; } From df271a59cbfcfbe98fa4bd7af3ae595633539a12 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Wed, 22 May 2013 16:24:00 -0500 Subject: [PATCH 139/325] lxclock: Replace named sempahore with flock The problem: if a task is killed while holding a posix semaphore, there appears to be no way to have the semaphore be reliably autmoatically released. The only trick which seemed promising is to store the pid of the lock holder in some file and have later lock seekers check whether that task has died. Instead of going down that route, this patch switches from a named posix semaphore to flock. The advantage is that when the task is killed, its fds are closed and locks are automatically released. The disadvantage of flock is that we can't rely on it to exclude threads. Therefore c->slock must now always be wrapped inside c->privlock. This patch survived basic testing with the lxcapi_create patchset, where now killing lxc-create while it was holding the lock did not lock up future api commands. Signed-off-by: Serge Hallyn --- src/lxc/lxccontainer.c | 63 +++++++--- src/lxc/lxccontainer.h | 4 +- src/lxc/lxclock.c | 210 ++++++++++++++++++++++++++------- src/lxc/lxclock.h | 58 +++++++--- src/tests/locktests.c | 255 +++++++++++++---------------------------- 5 files changed, 332 insertions(+), 258 deletions(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 1c0b7b298..0ec23e8f3 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -43,14 +43,16 @@ #include #include -static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; +extern pthread_mutex_t thread_mutex; 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 - * thread_mutex protects process data (ex: fd table) from multiple threads + * 1. c->privlock protects the struct lxc_container from multiple threads. + * 2. c->slock protects the on-disk container data + * 3. thread_mutex protects process data (ex: fd table) from multiple threads + * slock is an flock, which does not exclude threads. Therefore slock should + * always be wrapped inside privlock. * 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 @@ -81,14 +83,12 @@ static void lxc_container_free(struct lxc_container *c) c->error_string = NULL; } if (c->slock) { - sem_close(c->slock); + lxc_putlock(c->slock); c->slock = NULL; } if (c->privlock) { - sem_t *l = c->privlock; + lxc_putlock(c->privlock); c->privlock = NULL; - sem_destroy(l); - free(l); } if (c->name) { free(c->name); @@ -202,11 +202,15 @@ static const char *lxcapi_state(struct lxc_container *c) if (!c) return NULL; - if (lxclock(c->slock, 0)) + if (lxclock(c->privlock, 0)) return NULL; + if (lxclock(c->slock, 0)) + goto bad; s = lxc_getstate(c->name, c->config_path); ret = lxc_state2str(s); lxcunlock(c->slock); +bad: + lxcunlock(c->privlock); return ret; } @@ -236,10 +240,15 @@ static bool lxcapi_freeze(struct lxc_container *c) if (!c) return false; - if (lxclock(c->slock, 0)) + if (lxclock(c->privlock, 0)) return false; + if (lxclock(c->slock, 0)) { + lxcunlock(c->privlock); + return false; + } ret = lxc_freeze(c->name, c->config_path); lxcunlock(c->slock); + lxcunlock(c->privlock); if (ret) return false; return true; @@ -251,10 +260,15 @@ static bool lxcapi_unfreeze(struct lxc_container *c) if (!c) return false; - if (lxclock(c->slock, 0)) + if (lxclock(c->privlock, 0)) return false; + if (lxclock(c->slock, 0)) { + lxcunlock(c->privlock); + return false; + } ret = lxc_unfreeze(c->name, c->config_path); lxcunlock(c->slock); + lxcunlock(c->privlock); if (ret) return false; return true; @@ -266,10 +280,15 @@ static pid_t lxcapi_init_pid(struct lxc_container *c) if (!c) return -1; - if (lxclock(c->slock, 0)) + if (lxclock(c->privlock, 0)) return -1; + if (lxclock(c->slock, 0)) { + lxcunlock(c->privlock); + return -1; + } ret = lxc_cmd_get_init_pid(c->name, c->config_path); lxcunlock(c->slock); + lxcunlock(c->privlock); return ret; } @@ -294,10 +313,15 @@ static bool lxcapi_load_config(struct lxc_container *c, const char *alt_file) fname = alt_file; if (!fname) return false; - if (lxclock(c->slock, 0)) + if (lxclock(c->privlock, 0)) return false; + if (lxclock(c->slock, 0)) { + lxcunlock(c->privlock); + return false; + } ret = load_config_locked(c, fname); lxcunlock(c->slock); + lxcunlock(c->privlock); return ret; } @@ -589,9 +613,12 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, char *const ar /* we're going to fork. but since we'll wait for our child, we * don't need to lxc_container_get */ + if (lxclock(c->privlock, 0)) + goto out; if (lxclock(c->slock, 0)) { ERROR("failed to grab global container lock for %s\n", c->name); - goto out; + lxcunlock(c->privlock); + goto out_unlock1; } pid = fork(); @@ -672,6 +699,8 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, char *const ar out_unlock: lxcunlock(c->slock); +out_unlock1: + lxcunlock(c->privlock); out: if (tpath) free(tpath); @@ -1695,14 +1724,12 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath strcpy(c->name, name); c->numthreads = 1; - c->slock = lxc_newlock(name); - if (!c->slock) { + if (!(c->slock = lxc_newlock(c->config_path, name))) { fprintf(stderr, "failed to create lock\n"); goto err; } - c->privlock = lxc_newlock(NULL); - if (!c->privlock) { + if (!(c->privlock = lxc_newlock(NULL, NULL))) { fprintf(stderr, "failed to alloc privlock\n"); goto err; } diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index 1c464fe20..1ca751926 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -16,8 +16,8 @@ struct lxc_container { // private fields char *name; char *configfile; - sem_t *slock; - sem_t *privlock; + struct lxc_lock *slock; + struct lxc_lock *privlock; int numthreads; /* protected by privlock. */ struct lxc_conf *lxc_conf; // maybe we'll just want the whole lxc_handler? diff --git a/src/lxc/lxclock.c b/src/lxc/lxclock.c index 0cbe843a1..bddd1e9e8 100644 --- a/src/lxc/lxclock.c +++ b/src/lxc/lxclock.c @@ -17,25 +17,43 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include "lxclock.h" #include #include +#include +#include +#include +#include #define OFLAG (O_CREAT | O_RDWR) #define SEMMODE 0660 #define SEMVALUE 1 #define SEMVALUE_LOCKED 0 -#define LXCLOCK_PREFIX "/lxcapi." +lxc_log_define(lxc_lock, lxc); -static char *lxclock_name(const char *container) +pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; + +static char *lxclock_name(const char *p, const char *n) { int ret; - int len = strlen(container) + strlen(LXCLOCK_PREFIX) + 1; + // $lxcpath/locks/$lxcname + '\0' + int len = strlen(p) + strlen(n) + strlen("/locks/") + 1; char *dest = malloc(len); if (!dest) return NULL; - ret = snprintf(dest, len, "%s%s", LXCLOCK_PREFIX, container); + ret = snprintf(dest, len, "%s/locks", p); + if (ret < 0 || ret >= len) { + free(dest); + return NULL; + } + if (mkdir_p(dest, 0755) < 0) { + free(dest); + return NULL; + } + + ret = snprintf(dest, len, "%s/locks/%s", p, n); if (ret < 0 || ret >= len) { free(dest); return NULL; @@ -43,12 +61,6 @@ static char *lxclock_name(const char *container) return dest; } -static void lxcfree_name(char *name) -{ - if (name) - free(name); -} - static sem_t *lxc_new_unnamed_sem(void) { sem_t *s; @@ -65,44 +77,158 @@ static sem_t *lxc_new_unnamed_sem(void) return s; } -sem_t *lxc_newlock(const char *name) +struct lxc_lock *lxc_newlock(const char *lxcpath, const char *name) { - char *lname; - sem_t *lock; - - if (!name) - return lxc_new_unnamed_sem(); - - lname = lxclock_name(name); - if (!lname) + struct lxc_lock *l; + int ret = pthread_mutex_lock(&thread_mutex); + if (ret != 0) { + ERROR("pthread_mutex_lock returned:%d %s", ret, strerror(ret)); 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); } + l = malloc(sizeof(*l)); + if (!l) + goto out; + + if (!name) { + l->type = LXC_LOCK_ANON_SEM; + l->u.sem = lxc_new_unnamed_sem(); + goto out; + } + + l->type = LXC_LOCK_FLOCK; + l->u.f.fname = lxclock_name(lxcpath, name); + if (!l->u.f.fname) { + free(l); + l = NULL; + goto out; + } + l->u.f.fd = -1; + +out: + pthread_mutex_unlock(&thread_mutex); + return l; +} + +int lxclock(struct lxc_lock *l, int timeout) +{ + int saved_errno = errno; + int ret = pthread_mutex_lock(&thread_mutex); + if (ret != 0) { + ERROR("pthread_mutex_lock returned:%d %s", ret, strerror(ret)); + return ret; + } + + switch(l->type) { + case LXC_LOCK_ANON_SEM: + if (!timeout) { + ret = sem_wait(l->u.sem); + if (ret == -1) + saved_errno = errno; + } else { + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { + ret = -2; + goto out; + } + ts.tv_sec += timeout; + ret = sem_timedwait(l->u.sem, &ts); + if (ret == -1) + saved_errno = errno; + } + break; + case LXC_LOCK_FLOCK: + ret = -2; + if (timeout) { + ERROR("Error: timeout not supported with flock"); + ret = -2; + goto out; + } + if (!l->u.f.fname) { + ERROR("Error: filename not set for flock"); + ret = -2; + goto out; + } + if (l->u.f.fd == -1) { + l->u.f.fd = open(l->u.f.fname, O_RDWR|O_CREAT, + S_IWUSR | S_IRUSR); + if (l->u.f.fd == -1) { + ERROR("Error opening %s", l->u.f.fname); + goto out; + } + } + ret = flock(l->u.f.fd, LOCK_EX); + if (ret == -1) + saved_errno = errno; + break; + } + +out: + pthread_mutex_unlock(&thread_mutex); + errno = saved_errno; return ret; } -int lxcunlock(sem_t *sem) +int lxcunlock(struct lxc_lock *l) { - if (!sem) - return -2; - return sem_post(sem); + int saved_errno = errno; + int ret = pthread_mutex_lock(&thread_mutex); + + if (ret != 0) { + ERROR("pthread_mutex_lock returned:%d %s", ret, strerror(ret)); + return ret; + } + + switch(l->type) { + case LXC_LOCK_ANON_SEM: + if (!l->u.sem) + ret = -2; + else + ret = sem_post(l->u.sem); + saved_errno = errno; + break; + case LXC_LOCK_FLOCK: + if (l->u.f.fd != -1) { + if ((ret = flock(l->u.f.fd, LOCK_UN)) < 0) + saved_errno = errno; + close(l->u.f.fd); + l->u.f.fd = -1; + } else + ret = -2; + break; + } + + pthread_mutex_unlock(&thread_mutex); + errno = saved_errno; + return ret; +} + +void lxc_putlock(struct lxc_lock *l) +{ + int ret = pthread_mutex_lock(&thread_mutex); + if (ret != 0) { + ERROR("pthread_mutex_lock returned:%d %s", ret, strerror(ret)); + return; + } + + if (!l) + goto out; + switch(l->type) { + case LXC_LOCK_ANON_SEM: + if (l->u.sem) + sem_close(l->u.sem); + break; + case LXC_LOCK_FLOCK: + if (l->u.f.fd != -1) { + close(l->u.f.fd); + l->u.f.fd = -1; + } + if (l->u.f.fname) { + free(l->u.f.fname); + l->u.f.fname = NULL; + } + break; + } +out: + pthread_mutex_unlock(&thread_mutex); } diff --git a/src/lxc/lxclock.h b/src/lxc/lxclock.h index 1226c22f0..61e7bbb9b 100644 --- a/src/lxc/lxclock.h +++ b/src/lxc/lxclock.h @@ -19,43 +19,65 @@ #include /* For O_* constants */ #include /* For mode constants */ +#include #include #include #include +#define LXC_LOCK_ANON_SEM 1 +#define LXC_LOCK_FLOCK 2 +struct lxc_lock { + short type; + union { + sem_t *sem; // an anonymous semaphore + struct { + int fd; // fd on which a lock is held (if not -1) + char *fname; + } f; + } u; +}; + /* - * lxc_newlock: + * lxc_newlock: Create a new (unlocked) lock. + * * 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 + * A sem_t * which can be passed to lxclock() and lxcunlock() + * will be placed in l->u.sem * - * return NULL on failure, else a sem_t * which can be passed to - * lxclock() and lxcunlock(). + * If lxcpath and name are given (both must be given if either is + * given) then a lockfile is created, $lxcpath/$lxcname/locks/$name. + * We use that to protect the containers as represented on disk. + * lxc_newlock() for the named lock only allocates the pathname in + * memory so we can quickly open+lock it at lxclock. + * l->u.f.fname will container the malloc'ed name (which must be + * freed when the container is freed), and u.f.fd = -1. + * + * return lxclock on success, NULL on failure. */ -extern sem_t *lxc_newlock(const char *name); +extern struct lxc_lock *lxc_newlock(const char *lxcpath, const 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()). + * + * Note that timeout is (currently?) only supported for privlock, not + * for slock. Since currently there is not a single use of the timeout + * (except in the test case) I may remove the support for it in sem as + * well. */ -extern int lxclock(sem_t *sem, int timeout); +extern int lxclock(struct lxc_lock *lock, int timeout); /* - * lxcunlock: unlock given sem. Return 0 on success. Otherwise returns - * -1 and sem_post will leave errno set. + * lxcunlock: unlock given sem. Return 0 on success, or -2 if we did not + * have the lock. Otherwise returns -1 with errno saved from flock + * or sem_post function. */ -extern int lxcunlock(sem_t *lock); +extern int lxcunlock(struct lxc_lock *lock); + +extern void lxc_putlock(struct lxc_lock *l); diff --git a/src/tests/locktests.c b/src/tests/locktests.c index f08555805..96df9468b 100644 --- a/src/tests/locktests.c +++ b/src/tests/locktests.c @@ -27,213 +27,112 @@ #define mycontainername "lxctest.sem" #define TIMEOUT_SECS 3 -int timedout; -int pid_to_kill; - -void timeouthandler(int sig) +void test_two_locks(void) { - // timeout received - timedout = 1; - kill(pid_to_kill, SIGTERM); -} + struct lxc_lock *l; + pid_t pid; + int ret, status; + int p[2]; + char c; -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); + if (pipe(p) < 0) + exit(1); + if ((pid = fork()) < 0) + exit(1); + if (pid == 0) { + if (read(p[0], &c, 1) < 0) { + perror("read"); + exit(1); } - fprintf(stderr, "%d: child, was not able to get lock\n", __LINE__); + l = lxc_newlock("/tmp", "lxctest-sem"); + if (!l) { + fprintf(stderr, "%d: child: failed to create lock\n", __LINE__); + exit(1); + } + if (lxclock(l, 0) < 0) { + fprintf(stderr, "%d: child: failed to grab lock\n", __LINE__); + exit(1); + } + fprintf(stderr, "%d: child: grabbed lock\n", __LINE__); + exit(0); + } + l = lxc_newlock("/tmp", "lxctest-sem"); + if (!l) { + fprintf(stderr, "%d: failed to create 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); - } + if (lxclock(l, 0) < 0) { + fprintf(stderr, "%d; failed to get lock\n", __LINE__); 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 (write(p[1], &c, 1) < 0) { + perror("write"); + exit(1); } - if (WEXITSTATUS(status) == 0) { - fprintf(stderr, "%d: child was able to get lock, should have failed with timeout\n", __LINE__); - ret = 1; + sleep(3); + ret = waitpid(pid, &status, WNOHANG); + if (ret == pid) { // task exited + if (WIFEXITED(status)) { + printf("%d exited normally with exit code %d\n", pid, + WEXITSTATUS(status)); + if (WEXITSTATUS(status) == 0) + exit(1); + } else + printf("%d did not exit normally\n", pid); + return; + } else if (ret < 0) { + perror("waitpid"); + exit(1); } - lxcunlock(lock); - return ret; + kill(pid, SIGKILL); + wait(&status); + close(p[1]); + close(p[0]); + lxcunlock(l); + lxc_putlock(l); } int main(int argc, char *argv[]) { - int ret, sval, r; - sem_t *lock; + int ret; + struct lxc_lock *lock; - lock = lxc_newlock(NULL); - if (!lock) { + lock = lxc_newlock(NULL, NULL); + if (!lock) { fprintf(stderr, "%d: failed to get unnamed lock\n", __LINE__); exit(1); - } - ret = lxclock(lock, 0); - if (ret) { + } + 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) { + ret = lxcunlock(lock); + if (ret) { fprintf(stderr, "%d: failed to put unnamed lock (%d)\n", __LINE__, ret); exit(1); - } + } + lxc_putlock(lock); - sem_destroy(lock); - free(lock); - - lock = lxc_newlock(mycontainername); + lock = lxc_newlock("/var/lib/lxc", 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__); + struct stat sb; + char *pathname = "/var/lib/lxc/locks/" mycontainername; + ret = stat(pathname, &sb); + if (ret != 0) { + fprintf(stderr, "%d: filename %s not created\n", __LINE__, + pathname); + exit(1); } + lxc_putlock(lock); - 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__); - } + test_two_locks(); - 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__); - } + fprintf(stderr, "all tests passed\n"); - 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); } From 5cee8c5040661f9875bf41cfffd641c87afae8af Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 24 May 2013 16:03:22 -0500 Subject: [PATCH 140/325] locking: update per Dwight's comment Create three pairs of functions: int process_lock(void); void process_unlock(void); int container_mem_lock(struct lxc_container *c) void container_mem_unlock(struct lxc_container *c) int container_disk_lock(struct lxc_container *c); void container_disk_unlock(struct lxc_container *c); and use those in lxccontainer.c process_lock() is to protect the process state among multiple threads. container_mem_lock() is to protect a struct container among multiple threads. container_disk_lock is to protect a container on disk. Also remove the lock in lxcapi_init_pid() as Dwight suggested. Fix a typo (s/container/contain) spotted by Dwight. More locking fixes are needed, but let's first the the fundamentals right. How close does this get us? Changelog: v2: fix lxclock compile Signed-off-by: Serge Hallyn Acked-by: Dwight Engen --- src/lxc/lxccontainer.c | 129 ++++++++++++++--------------------------- src/lxc/lxclock.c | 94 ++++++++++++++++++++---------- src/lxc/lxclock.h | 13 ++++- 3 files changed, 119 insertions(+), 117 deletions(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 0ec23e8f3..2a45c1fe0 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -43,8 +43,6 @@ #include #include -extern pthread_mutex_t thread_mutex; - lxc_log_define(lxc_container, lxc); /* LOCKING @@ -138,7 +136,7 @@ int lxc_container_get(struct lxc_container *c) if (c->numthreads < 1) return 0; - if (lxclock(c->privlock, 0)) + if (container_mem_lock(c)) return 0; if (c->numthreads < 1) { // bail without trying to unlock, bc the privlock is now probably @@ -146,7 +144,7 @@ int lxc_container_get(struct lxc_container *c) return 0; } c->numthreads++; - lxcunlock(c->privlock); + container_mem_unlock(c); return 1; } @@ -154,14 +152,14 @@ int lxc_container_put(struct lxc_container *c) { if (!c) return -1; - if (lxclock(c->privlock, 0)) + if (container_mem_lock(c)) return -1; if (--c->numthreads < 1) { - lxcunlock(c->privlock); + container_mem_unlock(c); lxc_container_free(c); return 1; } - lxcunlock(c->privlock); + container_mem_unlock(c); return 0; } @@ -181,7 +179,7 @@ static bool lxcapi_is_defined(struct lxc_container *c) if (!c) return false; - if (lxclock(c->privlock, 0)) + if (container_mem_lock(c)) return false; if (!c->configfile) goto out; @@ -191,7 +189,7 @@ static bool lxcapi_is_defined(struct lxc_container *c) ret = true; out: - lxcunlock(c->privlock); + container_mem_unlock(c); return ret; } @@ -202,15 +200,11 @@ static const char *lxcapi_state(struct lxc_container *c) if (!c) return NULL; - if (lxclock(c->privlock, 0)) + if (container_disk_lock(c)) return NULL; - if (lxclock(c->slock, 0)) - goto bad; s = lxc_getstate(c->name, c->config_path); ret = lxc_state2str(s); - lxcunlock(c->slock); -bad: - lxcunlock(c->privlock); + container_disk_unlock(c); return ret; } @@ -240,15 +234,10 @@ static bool lxcapi_freeze(struct lxc_container *c) if (!c) return false; - if (lxclock(c->privlock, 0)) + if (container_disk_lock(c)) return false; - if (lxclock(c->slock, 0)) { - lxcunlock(c->privlock); - return false; - } ret = lxc_freeze(c->name, c->config_path); - lxcunlock(c->slock); - lxcunlock(c->privlock); + container_disk_unlock(c); if (ret) return false; return true; @@ -260,15 +249,10 @@ static bool lxcapi_unfreeze(struct lxc_container *c) if (!c) return false; - if (lxclock(c->privlock, 0)) + if (container_disk_lock(c)) return false; - if (lxclock(c->slock, 0)) { - lxcunlock(c->privlock); - return false; - } ret = lxc_unfreeze(c->name, c->config_path); - lxcunlock(c->slock); - lxcunlock(c->privlock); + container_disk_unlock(c); if (ret) return false; return true; @@ -276,20 +260,10 @@ static bool lxcapi_unfreeze(struct lxc_container *c) static pid_t lxcapi_init_pid(struct lxc_container *c) { - pid_t ret; if (!c) return -1; - if (lxclock(c->privlock, 0)) - return -1; - if (lxclock(c->slock, 0)) { - lxcunlock(c->privlock); - return -1; - } - ret = lxc_cmd_get_init_pid(c->name, c->config_path); - lxcunlock(c->slock); - lxcunlock(c->privlock); - return ret; + return lxc_cmd_get_init_pid(c->name, c->config_path); } static bool load_config_locked(struct lxc_container *c, const char *fname) @@ -313,15 +287,10 @@ static bool lxcapi_load_config(struct lxc_container *c, const char *alt_file) fname = alt_file; if (!fname) return false; - if (lxclock(c->privlock, 0)) + if (container_disk_lock(c)) return false; - if (lxclock(c->slock, 0)) { - lxcunlock(c->privlock); - return false; - } ret = load_config_locked(c, fname); - lxcunlock(c->slock); - lxcunlock(c->privlock); + container_disk_unlock(c); return ret; } @@ -384,11 +353,11 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv if (useinit && !argv) return false; - if (lxclock(c->privlock, 0)) + if (container_mem_lock(c)) return false; conf = c->lxc_conf; daemonize = c->daemonize; - lxcunlock(c->privlock); + container_mem_unlock(c); if (useinit) { ret = lxc_execute(c->name, argv, 1, conf, c->config_path); @@ -409,23 +378,20 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv return false; lxc_monitord_spawn(c->config_path); - ret = pthread_mutex_lock(&thread_mutex); - if (ret != 0) { - ERROR("pthread_mutex_lock returned:%d %s", ret, strerror(ret)); + if (process_lock()) return false; - } pid_t pid = fork(); if (pid < 0) { lxc_container_put(c); - pthread_mutex_unlock(&thread_mutex); + process_unlock(); return false; } if (pid != 0) { ret = wait_on_daemonized_start(c); - pthread_mutex_unlock(&thread_mutex); + process_unlock(); return ret; } - pthread_mutex_unlock(&thread_mutex); + process_unlock(); /* second fork to be reparented by init */ pid = fork(); if (pid < 0) { @@ -613,13 +579,8 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, char *const ar /* we're going to fork. but since we'll wait for our child, we * don't need to lxc_container_get */ - if (lxclock(c->privlock, 0)) + if (container_disk_lock(c)) goto out; - if (lxclock(c->slock, 0)) { - ERROR("failed to grab global container lock for %s\n", c->name); - lxcunlock(c->privlock); - goto out_unlock1; - } pid = fork(); if (pid < 0) { @@ -698,9 +659,7 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, char *const ar bret = load_config_locked(c, c->configfile); out_unlock: - lxcunlock(c->slock); -out_unlock1: - lxcunlock(c->privlock); + container_disk_unlock(c); out: if (tpath) free(tpath); @@ -778,11 +737,10 @@ static bool lxcapi_clear_config_item(struct lxc_container *c, const char *key) if (!c || !c->lxc_conf) return false; - if (lxclock(c->privlock, 0)) { + if (container_mem_lock(c)) return false; - } ret = lxc_clear_config_item(c->lxc_conf, key); - lxcunlock(c->privlock); + container_mem_unlock(c); return ret == 0; } @@ -904,11 +862,10 @@ static int lxcapi_get_config_item(struct lxc_container *c, const char *key, char if (!c || !c->lxc_conf) return -1; - if (lxclock(c->privlock, 0)) { + if (container_mem_lock(c)) return -1; - } ret = lxc_get_config_item(c->lxc_conf, key, retv, inlen); - lxcunlock(c->privlock); + container_mem_unlock(c); return ret; } @@ -923,12 +880,12 @@ static int lxcapi_get_keys(struct lxc_container *c, const char *key, char *retv, */ if (!c || !c->lxc_conf) return -1; - if (lxclock(c->privlock, 0)) + if (container_mem_lock(c)) 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); + container_mem_unlock(c); return ret; } @@ -953,13 +910,13 @@ static bool lxcapi_save_config(struct lxc_container *c, const char *alt_file) FILE *fout = fopen(alt_file, "w"); if (!fout) return false; - if (lxclock(c->privlock, 0)) { + if (container_mem_lock(c)) { fclose(fout); return false; } write_config(fout, c->lxc_conf); fclose(fout); - lxcunlock(c->privlock); + container_mem_unlock(c); return true; } @@ -1000,7 +957,7 @@ static bool lxcapi_set_config_item(struct lxc_container *c, const char *key, con if (!c) return false; - if (lxclock(c->privlock, 0)) + if (container_mem_lock(c)) return false; if (!c->lxc_conf) @@ -1015,7 +972,7 @@ static bool lxcapi_set_config_item(struct lxc_container *c, const char *key, con b = true; err: - lxcunlock(c->privlock); + container_mem_unlock(c); return b; } @@ -1076,7 +1033,7 @@ static bool lxcapi_set_config_path(struct lxc_container *c, const char *path) if (!c) return b; - if (lxclock(c->privlock, 0)) + if (container_mem_lock(c)) return b; p = strdup(path); @@ -1102,7 +1059,7 @@ static bool lxcapi_set_config_path(struct lxc_container *c, const char *path) err: if (oldpath) free(oldpath); - lxcunlock(c->privlock); + container_mem_unlock(c); return b; } @@ -1115,7 +1072,7 @@ static bool lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys, if (!c) return false; - if (lxclock(c->privlock, 0)) + if (container_mem_lock(c)) return false; if (is_stopped_nolock(c)) @@ -1125,7 +1082,7 @@ static bool lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys, if (!ret) b = true; err: - lxcunlock(c->privlock); + container_mem_unlock(c); return b; } @@ -1136,7 +1093,7 @@ static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, c if (!c || !c->lxc_conf) return -1; - if (lxclock(c->privlock, 0)) + if (container_mem_lock(c)) return -1; if (is_stopped_nolock(c)) @@ -1145,7 +1102,7 @@ static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, c ret = lxc_cgroup_get(c->name, subsys, retv, inlen, c->config_path); out: - lxcunlock(c->privlock); + container_mem_unlock(c); return ret; } @@ -1600,7 +1557,7 @@ struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname, if (!c || !c->is_defined(c)) return NULL; - if (lxclock(c->privlock, 0)) + if (container_mem_lock(c)) return NULL; if (c->is_running(c)) { @@ -1682,11 +1639,11 @@ struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname, goto out; // TODO: update c's lxc.snapshot = count - lxcunlock(c->privlock); + container_mem_unlock(c); return c2; out: - lxcunlock(c->privlock); + container_mem_unlock(c); if (c2) { c2->destroy(c2); lxc_container_put(c2); diff --git a/src/lxc/lxclock.c b/src/lxc/lxclock.c index bddd1e9e8..4bbe873be 100644 --- a/src/lxc/lxclock.c +++ b/src/lxc/lxclock.c @@ -25,6 +25,7 @@ #include #include #include +#include #define OFLAG (O_CREAT | O_RDWR) #define SEMMODE 0660 @@ -48,7 +49,10 @@ static char *lxclock_name(const char *p, const char *n) free(dest); return NULL; } - if (mkdir_p(dest, 0755) < 0) { + process_lock(); + ret = mkdir_p(dest, 0755); + process_unlock(); + if (ret < 0) { free(dest); return NULL; } @@ -80,11 +84,6 @@ static sem_t *lxc_new_unnamed_sem(void) struct lxc_lock *lxc_newlock(const char *lxcpath, const char *name) { struct lxc_lock *l; - int ret = pthread_mutex_lock(&thread_mutex); - if (ret != 0) { - ERROR("pthread_mutex_lock returned:%d %s", ret, strerror(ret)); - return NULL; - } l = malloc(sizeof(*l)); if (!l) @@ -106,18 +105,12 @@ struct lxc_lock *lxc_newlock(const char *lxcpath, const char *name) l->u.f.fd = -1; out: - pthread_mutex_unlock(&thread_mutex); return l; } int lxclock(struct lxc_lock *l, int timeout) { - int saved_errno = errno; - int ret = pthread_mutex_lock(&thread_mutex); - if (ret != 0) { - ERROR("pthread_mutex_lock returned:%d %s", ret, strerror(ret)); - return ret; - } + int ret = -1, saved_errno = errno; switch(l->type) { case LXC_LOCK_ANON_SEM: @@ -149,35 +142,31 @@ int lxclock(struct lxc_lock *l, int timeout) ret = -2; goto out; } + process_lock(); if (l->u.f.fd == -1) { l->u.f.fd = open(l->u.f.fname, O_RDWR|O_CREAT, S_IWUSR | S_IRUSR); if (l->u.f.fd == -1) { + process_unlock(); ERROR("Error opening %s", l->u.f.fname); goto out; } } ret = flock(l->u.f.fd, LOCK_EX); + process_unlock(); if (ret == -1) saved_errno = errno; break; } out: - pthread_mutex_unlock(&thread_mutex); errno = saved_errno; return ret; } int lxcunlock(struct lxc_lock *l) { - int saved_errno = errno; - int ret = pthread_mutex_lock(&thread_mutex); - - if (ret != 0) { - ERROR("pthread_mutex_lock returned:%d %s", ret, strerror(ret)); - return ret; - } + int ret = 0, saved_errno = errno; switch(l->type) { case LXC_LOCK_ANON_SEM: @@ -188,6 +177,7 @@ int lxcunlock(struct lxc_lock *l) saved_errno = errno; break; case LXC_LOCK_FLOCK: + process_lock(); if (l->u.f.fd != -1) { if ((ret = flock(l->u.f.fd, LOCK_UN)) < 0) saved_errno = errno; @@ -195,40 +185,84 @@ int lxcunlock(struct lxc_lock *l) l->u.f.fd = -1; } else ret = -2; + process_unlock(); break; } - pthread_mutex_unlock(&thread_mutex); errno = saved_errno; return ret; } +/* + * lxc_putlock() is only called when a container_new() fails, + * or during container_put(), which is already guaranteed to + * only be done by one task. + * So the only exclusion we need to provide here is for regular + * thread safety (i.e. file descriptor table changes). + */ void lxc_putlock(struct lxc_lock *l) { - int ret = pthread_mutex_lock(&thread_mutex); - if (ret != 0) { - ERROR("pthread_mutex_lock returned:%d %s", ret, strerror(ret)); - return; - } - if (!l) - goto out; + return; switch(l->type) { case LXC_LOCK_ANON_SEM: if (l->u.sem) sem_close(l->u.sem); break; case LXC_LOCK_FLOCK: + process_lock(); if (l->u.f.fd != -1) { close(l->u.f.fd); l->u.f.fd = -1; } + process_unlock(); if (l->u.f.fname) { free(l->u.f.fname); l->u.f.fname = NULL; } break; } -out: +} + +int process_lock(void) +{ + int ret; + ret = pthread_mutex_lock(&thread_mutex); + if (ret != 0) + ERROR("pthread_mutex_lock returned:%d %s", ret, strerror(ret)); + return ret; +} + +void process_unlock(void) +{ pthread_mutex_unlock(&thread_mutex); } + +int container_mem_lock(struct lxc_container *c) +{ + return lxclock(c->privlock, 0); +} + +void container_mem_unlock(struct lxc_container *c) +{ + lxcunlock(c->privlock); +} + +int container_disk_lock(struct lxc_container *c) +{ + int ret; + + if ((ret = lxclock(c->privlock, 0))) + return ret; + if ((ret = lxclock(c->slock, 0))) { + lxcunlock(c->privlock); + return ret; + } + return 0; +} + +void container_disk_unlock(struct lxc_container *c) +{ + lxcunlock(c->slock); + lxcunlock(c->privlock); +} diff --git a/src/lxc/lxclock.h b/src/lxc/lxclock.h index 61e7bbb9b..0438353ef 100644 --- a/src/lxc/lxclock.h +++ b/src/lxc/lxclock.h @@ -1,3 +1,5 @@ +#ifndef __LXCLOCK_H +#define __LXCLOCK_H /* liblxcapi * * Copyright © 2012 Serge Hallyn . @@ -53,7 +55,7 @@ struct lxc_lock { * We use that to protect the containers as represented on disk. * lxc_newlock() for the named lock only allocates the pathname in * memory so we can quickly open+lock it at lxclock. - * l->u.f.fname will container the malloc'ed name (which must be + * l->u.f.fname will contain the malloc'ed name (which must be * freed when the container is freed), and u.f.fd = -1. * * return lxclock on success, NULL on failure. @@ -81,3 +83,12 @@ extern int lxclock(struct lxc_lock *lock, int timeout); extern int lxcunlock(struct lxc_lock *lock); extern void lxc_putlock(struct lxc_lock *l); + +extern int process_lock(void); +extern void process_unlock(void); +struct lxc_container; +extern int container_mem_lock(struct lxc_container *c); +extern void container_mem_unlock(struct lxc_container *c); +extern int container_disk_lock(struct lxc_container *c); +extern void container_disk_unlock(struct lxc_container *c); +#endif From 3e625e2d2e12b919dd9590b97badc6108ee67b1a Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Thu, 16 May 2013 23:03:47 +0200 Subject: [PATCH 141/325] lxc-stop: use api, remove lxc_shutdown, extend lxc-stop functionality implement c->reboot(c) in the api. Also if the container is not running, return -2. Currently lxc-stop will return 0, so you cannot tell the difference between successfull stopping and noop. Per stgraber's email: - Remove lxc-shutdown - Change lxc-stop so that: * Default behaviour is to call shutdown(), wait 15s for STOPPED, if not STOPPED, print a message to the user and call stop() [ NOTE: actually 60 seconds per followup thread] * We have a -r option to reboot the container (with proper check that the container indeed rebooted within the next 15s) * We have a -s option to shutdown the container without the automatic fallback to stop() * Add a -k option allowing a user to just kill a container (equivalent to old lxc-stop, no shutdown() call and no delay). and update manpages. Signed-off-by: Serge Hallyn --- configure.ac | 2 - doc/Makefile.am | 1 - doc/lxc-shutdown.sgml.in | 98 ----------------------- doc/lxc-stop.sgml.in | 92 ++++++++++++++++++++-- src/lxc/Makefile.am | 1 - src/lxc/arguments.h | 6 +- src/lxc/lxc-shutdown.in | 165 --------------------------------------- src/lxc/lxc_stop.c | 117 ++++++++++++++++++++++++++- src/lxc/lxccontainer.c | 151 +++++++++++++++++++++++++++++++++-- src/lxc/lxccontainer.h | 2 + 10 files changed, 350 insertions(+), 285 deletions(-) delete mode 100644 doc/lxc-shutdown.sgml.in delete mode 100644 src/lxc/lxc-shutdown.in diff --git a/configure.ac b/configure.ac index 414d71b68..8979f7589 100644 --- a/configure.ac +++ b/configure.ac @@ -334,7 +334,6 @@ AC_CONFIG_FILES([ doc/lxc-netstat.sgml doc/lxc-ps.sgml doc/lxc-restart.sgml - doc/lxc-shutdown.sgml doc/lxc-start-ephemeral.sgml doc/lxc-start.sgml doc/lxc-stop.sgml @@ -383,7 +382,6 @@ AC_CONFIG_FILES([ src/lxc/lxc-checkconfig src/lxc/lxc-version src/lxc/lxc-create - src/lxc/lxc-shutdown src/lxc/lxc-start-ephemeral src/lxc/lxc-destroy src/lxc/legacy/lxc-ls diff --git a/doc/Makefile.am b/doc/Makefile.am index 1a2469548..a00036a39 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -22,7 +22,6 @@ man_MANS = \ lxc-netstat.1 \ lxc-ps.1 \ lxc-restart.1 \ - lxc-shutdown.1 \ lxc-start.1 \ lxc-stop.1 \ lxc-unfreeze.1 \ diff --git a/doc/lxc-shutdown.sgml.in b/doc/lxc-shutdown.sgml.in deleted file mode 100644 index 9262c6d79..000000000 --- a/doc/lxc-shutdown.sgml.in +++ /dev/null @@ -1,98 +0,0 @@ - - - - -]> - - - - @LXC_GENERATE_DATE@ - - - lxc-shutdown - 1 - - - - lxc-shutdown - - - externally shut down or reboot a container - - - - - - lxc-shutdown - -n name - -w - -r - - - - - Description - - - lxc-shutdown sends a SIGPWR signal to the - specified container to request it to cleanly shut down. If - -w is specified, then lxc-shutdown - will wait until the container has shut down before exiting. - If -r is specified, the container will be - asked to reboot (using a SIGINT signal), and -w - will be ignored. If the container ignore these signals, then - nothing will happen. In that case, you can use lxc-stop - to force the container to stop. - - - - - &commonoptions; - - &seealso; - - - Author - Serge Hallyn serge.hallyn@canonical.com - - - - - diff --git a/doc/lxc-stop.sgml.in b/doc/lxc-stop.sgml.in index b4dcc1bcf..2dbff8003 100644 --- a/doc/lxc-stop.sgml.in +++ b/doc/lxc-stop.sgml.in @@ -50,6 +50,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA lxc-stop -n name + -W + -r + -t timeout + -k + -s @@ -57,14 +62,90 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Description - lxc-stop kills all the processes inside the - container. This command should be used if the processes are no - longer accessible and can no be exited normally. + lxc-stop reboots, cleanly shuts down, or kills + all the processes inside the container. By default, it will + request a clean shutdown of the container (by sending SIGPWR to + the container), wait 60 seconds for the container to exit, and + returns. If the container fails to cleanly exit, then after 60 + seconds the container will be sent the + lxc.stopsignal to force it to shut down. - + + The -W, -r, -s + and -k options specify the action to perform. + -W indicates that after performing the specified + action, lxc-stop should immediately exit, while + -t TIMEOUT specifies the maximum amount of time + to wait for the container to complete the shutdown or reboot. + - &commonoptions; + + Options + + + + + + + + + Request a reboot of the container. + + + + + + + + + + + Only request a clean shutdown, do not kill the container tasks if the + clean shutdown fails. + + + + + + + + + + + Rather than requesting a clean shutdown of the container, explicitly + kill all tasks in the container. This is the legacy + lxc-stop behavior. + + + + + + + + + + + Simply perform the requestion action (reboot, shutdown, or hard + kill) and exit. + + + + + + + + + + + Wait TIMEOUT seconds before hard-stopping the container of (in + the reboot case) returning failure. + + + + + + Diagnostic @@ -92,7 +173,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index f8eb41b8d..578ee060b 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -123,7 +123,6 @@ bin_SCRIPTS = \ lxc-checkconfig \ lxc-version \ lxc-create \ - lxc-shutdown \ lxc-destroy EXTRA_DIST = \ diff --git a/src/lxc/arguments.h b/src/lxc/arguments.h index 002a91961..145474d44 100644 --- a/src/lxc/arguments.h +++ b/src/lxc/arguments.h @@ -61,9 +61,13 @@ struct lxc_arguments { int ttynum; char escape; - /* for lxc-wait */ + /* for lxc-wait and lxc-shutdown */ char *states; long timeout; + int nowait; + int reboot; + int hardstop; + int shutdown; /* close fds from parent? */ int close_all_fds; diff --git a/src/lxc/lxc-shutdown.in b/src/lxc/lxc-shutdown.in deleted file mode 100644 index c81e736b6..000000000 --- a/src/lxc/lxc-shutdown.in +++ /dev/null @@ -1,165 +0,0 @@ -#!/bin/sh - -# (C) Copyright Canonical 2011,2012 - -# 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 - -set -e - -. @DATADIR@/lxc/lxc.functions - -usage() { - echo "usage: lxc-shutdown -n name [-w] [-r] [-P lxcpath]" - echo " Cleanly shut down a container." - echo " -w: wait for shutdown to complete." - echo " -r: reboot (ignore -w)." - echo " -t timeout: wait at most timeout seconds (implies -w), then kill" - echo " the container." - echo " -P lxcpath: path to the lxc container directories." -} - -alarm() { - trap 'exit 0' TERM - pid=$1 - timeout=$2 - sleep $timeout - kill $pid -} - -dolxcstop() -{ - echo "Calling lxc-stop on $lxc_name" - lxc-stop -n $lxc_name -P "$lxc_path" - exit 0 -} - -usage_err() { - [ -n "$1" ] && echo "$1" >&2 - usage - exit 1 -} - -optarg_check() { - [ -n "$2" ] || usage_err "option '$1' requires an argument" -} - -timeout="-1" - -reboot=0 -dowait=0 - -while [ $# -gt 0 ]; do - opt="$1" - shift - case "$opt" in - -h|--help) - usage - exit 0 - ;; - -n|--name) - optarg_check $opt "$1" - lxc_name=$1 - shift - ;; - -w|--wait) - dowait=1 - ;; - -r|--reboot) - reboot=1 - ;; - -t|--timeout) - optarg_check $opt "$1" - timeout=$1 - dowait=1 - shift - ;; - -P|--lxcpath) - optarg_check $opt "$1" - lxc_path=$1 - dowait=1 - shift - ;; - --) - break;; - -?) - usage_err "unknown option '$opt'" - ;; - -*) - # split opts -abc into -a -b -c - set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@" - ;; - *) - usage_err "unknown option '$opt'" - exit 1 - ;; - esac -done - -if [ -z "$lxc_name" ]; then - echo "no container name specified" - usage - exit 1 -fi - -if [ ! -d "$lxc_path" ]; then - echo "$lxc_path: no such directory" - exit 1 -fi - -if [ "$(id -u)" != "0" ]; then - echo "This command has to be run as root" - exit 1 -fi - -which lxc-info > /dev/null 2>&1 || { echo "lxc-info not found."; exit 1; } -which lxc-wait > /dev/null 2>&1 || { echo "lxc-wait not found."; exit 1; } - -pid=`lxc-info -n $lxc_name -P "$lxc_path" -p 2>/dev/null | awk '{ print $2 }'` -if [ "$pid" = "-1" ]; then - echo "$lxc_name is not running" - exit 1 -fi - -if [ $reboot -eq 1 ]; then - kill -s INT $pid - exit 0 -else - kill -s PWR $pid -fi - -if [ $dowait -eq 0 ]; then - exit 0 -fi - -if [ $timeout != "-1" ]; then - trap dolxcstop EXIT - alarm $$ $timeout 2>/dev/null & - alarmpid=$! -fi - -while ! lxc-info -n $lxc_name -P "$lxc_path" --state-is STOPPED; do - sleep 1 -done - -if [ $timeout != "-1" ]; then - trap - EXIT - # include subprocesses; otherwise, we may have to wait until sleep completes - # if called from a non-interactive context - kill $alarmpid $(ps --no-headers --ppid $alarmpid -o pid) 2>/dev/null || : -fi - -echo "Container $lxc_name has shut down" - -exit 0 diff --git a/src/lxc/lxc_stop.c b/src/lxc/lxc_stop.c index 0ed8ca066..3d42d0e65 100644 --- a/src/lxc/lxc_stop.c +++ b/src/lxc/lxc_stop.c @@ -28,11 +28,29 @@ #include #include +#include #include "arguments.h" #include "commands.h" #include "utils.h" +static int my_parser(struct lxc_arguments* args, int c, char* arg) +{ + switch (c) { + case 'r': args->reboot = 1; break; + case 'W': args->nowait = 1; break; + case 't': args->timeout = atoi(arg); break; + case 'k': args->hardstop = 1; break; + case 's': args->shutdown = 1; break; + } + return 0; +} + static const struct option my_longopts[] = { + {"reboot", no_argument, 0, 'r'}, + {"nowait", no_argument, 0, 'W'}, + {"timeout", required_argument, 0, 't'}, + {"kill", no_argument, 0, 'k'}, + {"shutdown", no_argument, 0, 's'}, LXC_COMMON_OPTIONS }; @@ -44,14 +62,76 @@ static struct lxc_arguments my_args = { lxc-stop stops a container with the identifier NAME\n\ \n\ Options :\n\ - -n, --name=NAME NAME for name of the container\n", + -n, --name=NAME NAME for name of the container\n\ + -r, --reboot reboot the container\n\ + -W, --nowait don't wait for shutdown or reboot to complete\n\ + -t, --timeout=T wait T seconds before hard-stopping\n\ + -k, --kill kill container rather than request clean shutdown\n\ + -s, --shutdown Only request clean shutdown, don't later force kill\n", .options = my_longopts, - .parser = NULL, + .parser = my_parser, .checker = NULL, + .timeout = 60, }; +/* returns -1 on failure, 0 on success */ +int do_reboot_and_check(struct lxc_arguments *a, struct lxc_container *c) +{ + int ret; + pid_t pid; + pid_t newpid; + int timeout = a->timeout; + + pid = c->init_pid(c); + if (pid == -1) + return -1; + if (!c->reboot(c)) + return -1; + if (a->nowait) + return 0; + if (timeout <= 0) + goto out; + + for (;;) { + /* can we use c-> wait for this, assuming it will + * re-enter RUNNING? For now just sleep */ + int elapsed_time, curtime = 0; + struct timeval tv; + + newpid = c->init_pid(c); + if (newpid != -1 && newpid != pid) + return 0; + + ret = gettimeofday(&tv, NULL); + if (ret) + break; + curtime = tv.tv_sec; + + sleep(1); + ret = gettimeofday(&tv, NULL); + if (ret) + break; + elapsed_time = tv.tv_sec - curtime; + if (timeout - elapsed_time <= 0) + break; + timeout -= elapsed_time; + } + +out: + newpid = c->init_pid(c); + if (newpid == -1 || newpid == pid) { + printf("Reboot did not complete before timeout\n"); + return -1; + } + return 0; +} + int main(int argc, char *argv[]) { + struct lxc_container *c; + bool s; + int ret = -1; + if (lxc_arguments_parse(&my_args, argc, argv)) return -1; @@ -59,5 +139,36 @@ int main(int argc, char *argv[]) my_args.progname, my_args.quiet, my_args.lxcpath[0])) return -1; - return lxc_cmd_stop(my_args.name, my_args.lxcpath[0]); + c = lxc_container_new(my_args.name, my_args.lxcpath[0]); + if (!c) { + fprintf(stderr, "Error opening container\n"); + goto out; + } + + if (!c->is_running(c)) { + fprintf(stderr, "%s is not running\n", c->name); + ret = -2; + goto out; + } + + if (my_args.hardstop) { + ret = c->stop(c) ? 0 : -1; + goto out; + } + if (my_args.reboot) { + ret = do_reboot_and_check(&my_args, c); + goto out; + } + + s = c->shutdown(c, my_args.timeout); + if (!s) { + if (!my_args.shutdown) + ret = c->wait(c, "STOPPED", -1) ? 0 : -1; + else + ret = -1; // fail + } + +out: + lxc_container_put(c); + return ret; } diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 2a45c1fe0..a1c156c64 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -45,6 +45,105 @@ lxc_log_define(lxc_container, lxc); +static bool file_exists(char *f) +{ + struct stat statbuf; + + return stat(f, &statbuf) == 0; +} + +/* + * A few functions to help detect when a container creation failed. + * If a container creation was killed partway through, then trying + * to actually start that container could harm the host. We detect + * this by creating a 'partial' file under the container directory, + * and keeping an advisory lock. When container creation completes, + * we remove that file. When we load or try to start a container, if + * we find that file, without a flock, we remove the container. + */ +int ongoing_create(struct lxc_container *c) +{ + int len = strlen(c->config_path) + strlen(c->name) + 10; + char *path = alloca(len); + int fd, ret; + ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name); + if (ret < 0 || ret >= len) { + ERROR("Error writing partial pathname"); + return -1; + } + + if (!file_exists(path)) + return 0; + if (process_lock()) + return -1; + if ((fd = open(path, O_RDWR)) < 0) { + // give benefit of the doubt + SYSERROR("Error opening partial file"); + process_unlock(); + return 0; + } + if ((ret = flock(fd, LOCK_EX | LOCK_NB)) == -1 && + errno == EWOULDBLOCK) { + // create is still ongoing + close(fd); + process_unlock(); + return 1; + } + // create completed but partial is still there. + close(fd); + process_unlock(); + return 2; +} + +int create_partial(struct lxc_container *c) +{ + // $lxcpath + '/' + $name + '/partial' + \0 + int len = strlen(c->config_path) + strlen(c->name) + 10; + char *path = alloca(len); + int fd, ret; + ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name); + if (ret < 0 || ret >= len) { + ERROR("Error writing partial pathname"); + return -1; + } + if (process_lock() < 0) + return -1; + if ((fd=open(path, O_CREAT | O_EXCL, 0755)) < 0) { + SYSERROR("Erorr creating partial file"); + process_unlock(); + return -1; + } + if (flock(fd, LOCK_EX) < 0) { + SYSERROR("Error locking partial file %s", path); + close(fd); + process_unlock(); + return -1; + } + process_unlock(); + + return fd; +} + +void remove_partial(struct lxc_container *c, int fd) +{ + // $lxcpath + '/' + $name + '/partial' + \0 + int len = strlen(c->config_path) + strlen(c->name) + 10; + char *path = alloca(len); + int ret; + + close(fd); + ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name); + if (ret < 0 || ret >= len) { + ERROR("Error writing partial pathname"); + return; + } + if (process_lock()) + return; + if (unlink(path) < 0) + SYSERROR("Error unlink partial file %s", path); + process_unlock(); +} + /* LOCKING * 1. c->privlock protects the struct lxc_container from multiple threads. * 2. c->slock protects the on-disk container data @@ -163,13 +262,6 @@ int lxc_container_put(struct lxc_container *c) 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; @@ -349,6 +441,19 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv if (!c->lxc_conf) return false; + if ((ret = ongoing_create(c)) < 0) { + ERROR("Error checking for incomplete creation"); + return false; + } + if (ret == 2) { + ERROR("Error: %s creation was not completed", c->name); + c->destroy(c); + return false; + } else if (ret == 1) { + ERROR("Error: creation of %s is ongoing", c->name); + return false; + } + /* is this app meant to be run through lxcinit, as in lxc-execute? */ if (useinit && !argv) return false; @@ -550,7 +655,7 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, char *const ar bool bret = false; pid_t pid; char *tpath = NULL, **newargv; - int ret, len, nargs = 0; + int partial_fd, ret, len, nargs = 0; if (!c) return false; @@ -576,6 +681,10 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, char *const ar if (lxcapi_is_defined(c) && c->lxc_conf && c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) == 0) goto out; + /* Mark that this container is being created */ + if ((partial_fd = create_partial(c)) < 0) + goto out; + /* we're going to fork. but since we'll wait for our child, we * don't need to lxc_container_get */ @@ -659,6 +768,8 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, char *const ar bret = load_config_locked(c, c->configfile); out_unlock: + if (partial_fd >= 0) + remove_partial(c, partial_fd); container_disk_unlock(c); out: if (tpath) @@ -666,6 +777,23 @@ out: return bret; } +static bool lxcapi_reboot(struct lxc_container *c) +{ + pid_t pid; + + if (!c) + return false; + if (!c->is_running(c)) + return false; + pid = c->init_pid(c); + if (pid <= 0) + return false; + if (kill(pid, SIGINT) < 0) + return false; + return true; + +} + static bool lxcapi_shutdown(struct lxc_container *c, int timeout) { bool retv; @@ -1699,6 +1827,12 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath if (file_exists(c->configfile)) lxcapi_load_config(c, NULL); + if (ongoing_create(c) == 2) { + ERROR("Error: %s creation was not completed", c->name); + c->destroy(c); + goto err; + } + // assign the member functions c->is_defined = lxcapi_is_defined; c->state = lxcapi_state; @@ -1720,6 +1854,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath c->create = lxcapi_create; c->createl = lxcapi_createl; c->shutdown = lxcapi_shutdown; + c->reboot = lxcapi_reboot; c->clear_config_item = lxcapi_clear_config_item; c->get_config_item = lxcapi_get_config_item; c->get_cgroup_item = lxcapi_get_cgroup_item; diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index 1ca751926..3a80d0f9f 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -50,6 +50,8 @@ struct lxc_container { bool (*save_config)(struct lxc_container *c, const char *alt_file); bool (*create)(struct lxc_container *c, const char *t, char *const argv[]); bool (*createl)(struct lxc_container *c, const char *t, ...); + /* send SIGINT to ask container to reboot */ + bool (*reboot)(struct lxc_container *c); /* 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 */ From 60bf62d4ae36a48342fb8aee680fbd4b423810b1 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 17 May 2013 07:20:10 +0200 Subject: [PATCH 142/325] destroy: implement in the api This requires implementing bdev->ops->destroy() for each of the backing store types. Then implementing lxcapi_clone(), writing lxc_destroy.c using the api, and removing the lxc-destroy.in script. (this also has a few other cleanups, like marking some functions static) Changelog: fold into destroy: fix zfs destroy destroy: use correct program name in help Signed-off-by: Serge Hallyn --- configure.ac | 1 - src/lxc/Makefile.am | 7 ++- src/lxc/arguments.h | 3 + src/lxc/bdev.c | 129 +++++++++++++++++++++++++++++++++++++---- src/lxc/bdev.h | 1 + src/lxc/cgroup.c | 8 +-- src/lxc/lxc_destroy.c | 96 ++++++++++++++++++++++++++++++ src/lxc/lxccontainer.c | 57 ++++++++++++------ src/lxc/utils.c | 75 ++++++++++++++++++++++++ src/lxc/utils.h | 2 + 10 files changed, 339 insertions(+), 40 deletions(-) create mode 100644 src/lxc/lxc_destroy.c diff --git a/configure.ac b/configure.ac index 8979f7589..372fc75cb 100644 --- a/configure.ac +++ b/configure.ac @@ -383,7 +383,6 @@ AC_CONFIG_FILES([ src/lxc/lxc-version src/lxc/lxc-create src/lxc/lxc-start-ephemeral - src/lxc/lxc-destroy src/lxc/legacy/lxc-ls src/lxc/lxc.functions diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 578ee060b..6974b272e 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -122,8 +122,7 @@ bin_SCRIPTS = \ lxc-netstat \ lxc-checkconfig \ lxc-version \ - lxc-create \ - lxc-destroy + lxc-create EXTRA_DIST = \ lxc-device \ @@ -160,7 +159,8 @@ bin_PROGRAMS = \ lxc-checkpoint \ lxc-restart \ lxc-kill \ - lxc-config + lxc-config \ + lxc-destroy pkglibexec_PROGRAMS = \ lxc-init @@ -179,6 +179,7 @@ lxc_cgroup_SOURCES = lxc_cgroup.c lxc_checkpoint_SOURCES = lxc_checkpoint.c lxc_config_SOURCES = lxc_config.c lxc_console_SOURCES = lxc_console.c +lxc_destroy_SOURCES = lxc_destroy.c lxc_execute_SOURCES = lxc_execute.c lxc_freeze_SOURCES = lxc_freeze.c lxc_info_SOURCES = lxc_info.c diff --git a/src/lxc/arguments.h b/src/lxc/arguments.h index 145474d44..37de4b562 100644 --- a/src/lxc/arguments.h +++ b/src/lxc/arguments.h @@ -69,6 +69,9 @@ struct lxc_arguments { int hardstop; int shutdown; + /* for lxc-destroy */ + int force; + /* close fds from parent? */ int close_all_fds; diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index a0ce5f522..6d61d2de4 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -312,7 +312,7 @@ static int dir_detect(const char *path) // // XXXXXXX plain directory bind mount ops // -int dir_mount(struct bdev *bdev) +static int dir_mount(struct bdev *bdev) { if (strcmp(bdev->type, "dir")) return -22; @@ -321,7 +321,7 @@ int dir_mount(struct bdev *bdev) return mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC, NULL); } -int dir_umount(struct bdev *bdev) +static int dir_umount(struct bdev *bdev) { if (strcmp(bdev->type, "dir")) return -22; @@ -403,11 +403,19 @@ static int dir_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna return 0; } +static int dir_destroy(struct bdev *orig) +{ + if (!lxc_rmdir_onedev(orig->src)) + return -1; + return 0; +} + struct bdev_ops dir_ops = { .detect = &dir_detect, .mount = &dir_mount, .umount = &dir_umount, .clone_paths = &dir_clonepaths, + .destroy = &dir_destroy, }; @@ -422,7 +430,7 @@ struct bdev_ops dir_ops = { // sake of flexibility let's always bind-mount. // -static int zfs_list_entry(const char *path, char *output) +static int zfs_list_entry(const char *path, char *output, size_t inlen) { FILE *f; int found=0; @@ -431,7 +439,7 @@ static int zfs_list_entry(const char *path, char *output) SYSERROR("popen failed"); return 0; } - while (fgets(output, LXC_LOG_BUFFER_SIZE, f)) { + while (fgets(output, inlen, f)) { if (strstr(output, path)) { found = 1; break; @@ -451,12 +459,12 @@ static int zfs_detect(const char *path) ERROR("out of memory"); return 0; } - found = zfs_list_entry(path, output); + found = zfs_list_entry(path, output, LXC_LOG_BUFFER_SIZE); free(output); return found; } -int zfs_mount(struct bdev *bdev) +static int zfs_mount(struct bdev *bdev) { if (strcmp(bdev->type, "zfs")) return -22; @@ -465,7 +473,7 @@ int zfs_mount(struct bdev *bdev) return mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC, NULL); } -int zfs_umount(struct bdev *bdev) +static int zfs_umount(struct bdev *bdev) { if (strcmp(bdev->type, "zfs")) return -22; @@ -483,7 +491,7 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname, int ret; pid_t pid; - if (zfs_list_entry(opath, output)) { + if (zfs_list_entry(opath, output, MAXPATHLEN)) { // zfsroot is output up to ' ' if ((p = index(output, ' ')) == NULL) return -1; @@ -583,11 +591,41 @@ static int zfs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna return zfs_clone(orig->src, new->src, oldname, cname, lxcpath, snap); } +/* + * TODO: detect whether this was a clone, and if so then also delete the + * snapshot it was based on, so that we don't hold the original + * container busy. + */ +static int zfs_destroy(struct bdev *orig) +{ + pid_t pid; + char output[MAXPATHLEN], *p; + + if ((pid = fork()) < 0) + return -1; + if (pid) + return wait_for_pid(pid); + + if (!zfs_list_entry(orig->src, output, MAXPATHLEN)) { + ERROR("Error: zfs entry for %s not found", orig->src); + return -1; + } + + // zfs mount is output up to ' ' + if ((p = index(output, ' ')) == NULL) + return -1; + *p = '\0'; + + execlp("zfs", "zfs", "destroy", output, NULL); + exit(1); +} + struct bdev_ops zfs_ops = { .detect = &zfs_detect, .mount = &zfs_mount, .umount = &zfs_umount, .clone_paths = &zfs_clonepaths, + .destroy = &zfs_destroy, }; // @@ -815,11 +853,25 @@ static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna return 0; } +static int lvm_destroy(struct bdev *orig) +{ + pid_t pid; + + if ((pid = fork()) < 0) + return -1; + if (!pid) { + execlp("lvremove", "lvremove", "-f", orig->src, NULL); + exit(1); + } + return wait_for_pid(pid); +} + struct bdev_ops lvm_ops = { .detect = &lvm_detect, .mount = &lvm_mount, .umount = &lvm_umount, .clone_paths = &lvm_clonepaths, + .destroy = &lvm_destroy, }; // @@ -871,7 +923,7 @@ static int btrfs_detect(const char *path) return 0; } -int btrfs_mount(struct bdev *bdev) +static int btrfs_mount(struct bdev *bdev) { if (strcmp(bdev->type, "btrfs")) return -22; @@ -880,7 +932,7 @@ int btrfs_mount(struct bdev *bdev) return mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC, NULL); } -int btrfs_umount(struct bdev *bdev) +static int btrfs_umount(struct bdev *bdev) { if (strcmp(bdev->type, "btrfs")) return -22; @@ -904,6 +956,8 @@ struct btrfs_ioctl_vol_args { struct btrfs_ioctl_vol_args_v2) #define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \ struct btrfs_ioctl_vol_args) +#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \ + struct btrfs_ioctl_vol_args) #define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0) @@ -1048,11 +1102,48 @@ static int btrfs_clonepaths(struct bdev *orig, struct bdev *new, const char *old return btrfs_subvolume_create(new->dest); } +static int btrfs_destroy(struct bdev *orig) +{ + int ret, fd = -1; + struct btrfs_ioctl_vol_args args; + char *path = orig->src; + char *p, *newfull = strdup(path); + + if (!newfull) { + ERROR("Error: out of memory"); + return -1; + } + + p = rindex(newfull, '/'); + if (!p) { + ERROR("bad path: %s", path); + return -1; + } + *p = '\0'; + + if ((fd = open(newfull, O_RDONLY)) < 0) { + ERROR("Error opening %s", newfull); + free(newfull); + return -1; + } + + memset(&args, 0, sizeof(args)); + strncpy(args.name, p+1, BTRFS_SUBVOL_NAME_MAX); + args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0; + ret = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args); + INFO("btrfs: snapshot create ioctl returned %d", ret); + + free(newfull); + close(fd); + return ret; +} + struct bdev_ops btrfs_ops = { .detect = &btrfs_detect, .mount = &btrfs_mount, .umount = &btrfs_umount, .clone_paths = &btrfs_clonepaths, + .destroy = &btrfs_destroy, }; // @@ -1069,7 +1160,7 @@ static int overlayfs_detect(const char *path) // // XXXXXXX plain directory bind mount ops // -int overlayfs_mount(struct bdev *bdev) +static int overlayfs_mount(struct bdev *bdev) { char *options, *dup, *lower, *upper; int len; @@ -1108,7 +1199,7 @@ int overlayfs_mount(struct bdev *bdev) return ret; } -int overlayfs_umount(struct bdev *bdev) +static int overlayfs_umount(struct bdev *bdev) { if (strcmp(bdev->type, "overlayfs")) return -22; @@ -1217,11 +1308,25 @@ static int overlayfs_clonepaths(struct bdev *orig, struct bdev *new, const char return 0; } +int overlayfs_destroy(struct bdev *orig) +{ + char *upper; + + if (strncmp(orig->src, "overlayfs:", 10) != 0) + return -22; + upper = index(orig->src + 10, ':'); + if (!upper) + return -22; + upper++; + return lxc_rmdir_onedev(upper); +} + struct bdev_ops overlayfs_ops = { .detect = &overlayfs_detect, .mount = &overlayfs_mount, .umount = &overlayfs_umount, .clone_paths = &overlayfs_clonepaths, + .destroy = &overlayfs_destroy, }; struct bdev_type bdevs[] = { diff --git a/src/lxc/bdev.h b/src/lxc/bdev.h index d69efd8a7..e4f1e6080 100644 --- a/src/lxc/bdev.h +++ b/src/lxc/bdev.h @@ -16,6 +16,7 @@ struct bdev_ops { // mount requires src and dest to be set. int (*mount)(struct bdev *bdev); int (*umount)(struct bdev *bdev); + int (*destroy)(struct bdev *bdev); /* given original mount, rename the paths for cloned container */ int (*clone_paths)(struct bdev *orig, struct bdev *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c index f04d59a38..c28e24974 100644 --- a/src/lxc/cgroup.c +++ b/src/lxc/cgroup.c @@ -784,7 +784,7 @@ out: return retv; } -int recursive_rmdir(char *dirname) +static int cgroup_rmdir(char *dirname) { struct dirent dirent, *direntp; DIR *dir; @@ -817,7 +817,7 @@ int recursive_rmdir(char *dirname) if (ret) continue; if (S_ISDIR(mystat.st_mode)) - recursive_rmdir(pathname); + cgroup_rmdir(pathname); } ret = rmdir(dirname); @@ -825,8 +825,6 @@ int recursive_rmdir(char *dirname) if (closedir(dir)) ERROR("failed to close directory"); return ret; - - } static int lxc_one_cgroup_destroy(struct mntent *mntent, const char *cgpath) @@ -841,7 +839,7 @@ static int lxc_one_cgroup_destroy(struct mntent *mntent, const char *cgpath) return -1; } DEBUG("destroying %s\n", cgname); - if (recursive_rmdir(cgname)) { + if (cgroup_rmdir(cgname)) { SYSERROR("failed to remove cgroup '%s'", cgname); return -1; } diff --git a/src/lxc/lxc_destroy.c b/src/lxc/lxc_destroy.c new file mode 100644 index 000000000..686b3038b --- /dev/null +++ b/src/lxc/lxc_destroy.c @@ -0,0 +1,96 @@ +/* + * + * Copyright © 2013 Serge Hallyn . + * Copyright © 2013 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 "arguments.h" +#include "utils.h" + +lxc_log_define(lxc_destroy, lxc); + +static int my_parser(struct lxc_arguments* args, int c, char* arg) +{ + switch (c) { + case 'f': args->force = 1; break; + } + return 0; +} + +static const struct option my_longopts[] = { + {"force", no_argument, 0, 'f'}, + LXC_COMMON_OPTIONS +}; + +static struct lxc_arguments my_args = { + .progname = "lxc-destroy", + .help = "\ +--name=NAME [-f] [-P lxcpath]\n\ +\n\ +lxc-stop stops a container with the identifier NAME\n\ +\n\ +Options :\n\ + -n, --name=NAME NAME for name of the container\n\ + -f, --force wait for the container to shut down\n", + .options = my_longopts, + .parser = my_parser, + .checker = NULL, +}; + +int main(int argc, char *argv[]) +{ + struct lxc_container *c; + + /* this is a short term test. We'll probably want to check for + * write access to lxcpath instead */ + if (geteuid()) { + fprintf(stderr, "%s must be run as root\n", argv[0]); + exit(1); + } + + if (lxc_arguments_parse(&my_args, argc, argv)) + exit(1); + + if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, + my_args.progname, my_args.quiet, my_args.lxcpath[0])) + exit(1); + + c = lxc_container_new(my_args.name, my_args.lxcpath[0]); + if (!c) { + fprintf(stderr, "System error loading container\n"); + exit(1); + } + + if (c->is_running(c)) { + if (!my_args.force) { + fprintf(stderr, "%s is running\n", my_args.name); + exit(1); + } + c->stop(c); + } + + exit(c->destroy(c) ? 0 : 1); +} diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index a1c156c64..1d1174259 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -301,7 +301,7 @@ static const char *lxcapi_state(struct lxc_container *c) return ret; } -static bool is_stopped_nolock(struct lxc_container *c) +static bool is_stopped_locked(struct lxc_container *c) { lxc_state_t s; s = lxc_getstate(c->name, c->config_path); @@ -1048,32 +1048,51 @@ static bool lxcapi_save_config(struct lxc_container *c, const char *alt_file) return true; } +static const char *lxcapi_get_config_path(struct lxc_container *c); +// do we want the api to support --force, or leave that to the caller? static bool lxcapi_destroy(struct lxc_container *c) { - pid_t pid; - - if (!c) - return false; + struct bdev *r; + bool ret = false; /* container is already destroyed if we don't have a config and rootfs.path is not accessible */ - if (!lxcapi_is_defined(c) && (!c->lxc_conf || !c->lxc_conf->rootfs.path || access(c->lxc_conf->rootfs.path, F_OK) != 0)) + if (!c || !lxcapi_is_defined(c) || !c->lxc_conf || !c->lxc_conf->rootfs.path) return false; - pid = fork(); - if (pid < 0) + if (lxclock(c->privlock, 0)) return false; - if (pid == 0) { // child - execlp("lxc-destroy", "lxc-destroy", "-n", c->name, "-P", c->config_path, NULL); - perror("execl"); - exit(1); - } - - if (wait_for_pid(pid) < 0) { - ERROR("Error destroying container %s", c->name); + if (lxclock(c->slock, 0)) { + lxcunlock(c->privlock); return false; } - return true; + if (!is_stopped_locked(c)) { + // we should queue some sort of error - in c->error_string? + ERROR("container %s is not stopped", c->name); + goto out; + } + + r = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); + if (r) { + if (r->ops->destroy(r) < 0) { + ERROR("Error destroying rootfs for %s", c->name); + goto out; + } + } + + const char *p1 = lxcapi_get_config_path(c); + char *path = alloca(strlen(p1) + strlen(c->name) + 2); + sprintf(path, "%s/%s", p1, c->name); + if (lxc_rmdir_onedev(path) < 0) { + ERROR("Error destroying container directory for %s", c->name); + goto out; + } + ret = true; + +out: + lxcunlock(c->privlock); + lxcunlock(c->slock); + return ret; } static bool lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v) @@ -1203,7 +1222,7 @@ static bool lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys, if (container_mem_lock(c)) return false; - if (is_stopped_nolock(c)) + if (is_stopped_locked(c)) goto err; ret = lxc_cgroup_set(c->name, subsys, value, c->config_path); @@ -1224,7 +1243,7 @@ static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, c if (container_mem_lock(c)) return -1; - if (is_stopped_nolock(c)) + if (is_stopped_locked(c)) goto out; ret = lxc_cgroup_get(c->name, subsys, retv, inlen, c->config_path); diff --git a/src/lxc/utils.c b/src/lxc/utils.c index cd35e0006..6c0f9d065 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -41,6 +41,81 @@ lxc_log_define(lxc_utils, lxc); +static int _recursive_rmdir_onedev(char *dirname, dev_t pdev) +{ + struct dirent dirent, *direntp; + DIR *dir; + int ret, failed=0; + char pathname[MAXPATHLEN]; + + dir = opendir(dirname); + if (!dir) { + ERROR("%s: failed to open %s", __func__, dirname); + return 0; + } + + while (!readdir_r(dir, &dirent, &direntp)) { + struct stat mystat; + int rc; + + if (!direntp) + break; + + if (!strcmp(direntp->d_name, ".") || + !strcmp(direntp->d_name, "..")) + continue; + + rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name); + if (rc < 0 || rc >= MAXPATHLEN) { + ERROR("pathname too long"); + failed=1; + continue; + } + ret = lstat(pathname, &mystat); + if (ret) { + ERROR("%s: failed to stat %s", __func__, pathname); + failed=1; + continue; + } + if (mystat.st_dev != pdev) + continue; + if (S_ISDIR(mystat.st_mode)) { + if (!_recursive_rmdir_onedev(pathname, pdev)) + failed=1; + } else { + if (unlink(pathname) < 0) { + ERROR("%s: failed to delete %s", __func__, pathname); + failed=1; + } + } + } + + if (rmdir(dirname) < 0) { + ERROR("%s: failed to delete %s", __func__, dirname); + failed=1; + } + + if (closedir(dir)) { + ERROR("%s: failed to close directory %s", __func__, dirname); + failed=1; + } + + return !failed; +} + +/* returns 1 on success, 0 if there were any failures */ +extern int lxc_rmdir_onedev(char *path) +{ + struct stat mystat; + + if (lstat(path, &mystat) < 0) { + ERROR("%s: failed to stat %s", __func__, path); + return 0; + } + + return _recursive_rmdir_onedev(path, mystat.st_dev); +} + static int mount_fs(const char *source, const char *target, const char *type) { /* the umount may fail */ diff --git a/src/lxc/utils.h b/src/lxc/utils.h index fbfe5d3a0..7d2bfa8bb 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -27,6 +27,8 @@ #include #include "config.h" +/* returns 1 on success, 0 if there were any failures */ +extern int lxc_rmdir_onedev(char *path); extern int lxc_setup_fs(void); extern int get_u16(unsigned short *val, const char *arg, int base); extern int mkdir_p(const char *dir, mode_t mode); From 1897e3bcd36af9f3fe6d3649910a9adb93e5e988 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 17 May 2013 23:23:17 +0200 Subject: [PATCH 143/325] Move container creation fully into the api 1. implement bdev->create: python and lua: send NULL for bdevtype and bdevspecs. They'll want to be updated to pass those in in a way that makes sense, but I can't think about that right now. 2. templates: pass --rootfs If the container is backed by a device which must be mounted (i.e. lvm) then pass the actual rootfs mount destination to the templates. Note that the lxc.rootfs can be a mounted block device. The template should actually be installing the rootfs under the path where the lxc.rootfs is *mounted*. Still, some people like to run templates by hand and assume purely directory backed containers, so continue to support that use case (i.e. if no --rootfs is listed). Make sure the templates don't re-write lxc.rootfs if it is already in the config. (Most were already checking for that) 3. Replace lxc-create script with lxc_create.c program. Changelog: May 24: when creating a container, create $lxcpath/$name/partial, and flock it. When done, close that file and unlink it. In lxc_container_new() and lxcapi_start(), check for this file. If it is locked, create is ongoing. If it exists but is not locked, create() was killed - remove the container. May 24: dont disk-lock during lxcapi_create. The partial lock is sufficient. Signed-off-by: Serge Hallyn --- configure.ac | 1 - src/lua-lxc/core.c | 2 +- src/lxc/Makefile.am | 7 +- src/lxc/arguments.h | 7 + src/lxc/bdev.c | 257 +++++++++++++++++++++++++++++++++- src/lxc/bdev.h | 23 +++ src/lxc/lxc_create.c | 205 +++++++++++++++++++++++++++ src/lxc/lxccontainer.c | 171 ++++++++++++++++++---- src/lxc/lxccontainer.h | 8 +- src/python-lxc/lxc.c | 2 +- src/tests/cgpath.c | 2 +- src/tests/clonetest.c | 2 +- src/tests/createtest.c | 2 +- src/tests/get_item.c | 2 +- src/tests/shutdowntest.c | 2 +- templates/lxc-alpine.in | 14 +- templates/lxc-altlinux.in | 21 +-- templates/lxc-archlinux.in | 10 +- templates/lxc-busybox.in | 13 +- templates/lxc-debian.in | 13 +- templates/lxc-fedora.in | 16 ++- templates/lxc-opensuse.in | 13 +- templates/lxc-oracle.in | 10 +- templates/lxc-sshd.in | 15 +- templates/lxc-ubuntu-cloud.in | 13 +- templates/lxc-ubuntu.in | 15 +- 26 files changed, 749 insertions(+), 97 deletions(-) create mode 100644 src/lxc/lxc_create.c diff --git a/configure.ac b/configure.ac index 372fc75cb..d802406cf 100644 --- a/configure.ac +++ b/configure.ac @@ -381,7 +381,6 @@ AC_CONFIG_FILES([ src/lxc/lxc-netstat src/lxc/lxc-checkconfig src/lxc/lxc-version - src/lxc/lxc-create src/lxc/lxc-start-ephemeral src/lxc/legacy/lxc-ls src/lxc/lxc.functions diff --git a/src/lua-lxc/core.c b/src/lua-lxc/core.c index 364178685..778ef9926 100644 --- a/src/lua-lxc/core.c +++ b/src/lua-lxc/core.c @@ -111,7 +111,7 @@ static int container_create(lua_State *L) argv[i] = strdupa(luaL_checkstring(L, i+3)); argv[i] = NULL; - lua_pushboolean(L, !!c->create(c, template_name, argv)); + lua_pushboolean(L, !!c->create(c, template_name, NULL, NULL, argv)); return 1; } diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 6974b272e..70fcd257c 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -121,8 +121,7 @@ bin_SCRIPTS = \ lxc-ps \ lxc-netstat \ lxc-checkconfig \ - lxc-version \ - lxc-create + lxc-version EXTRA_DIST = \ lxc-device \ @@ -160,7 +159,8 @@ bin_PROGRAMS = \ lxc-restart \ lxc-kill \ lxc-config \ - lxc-destroy + lxc-destroy \ + lxc-create pkglibexec_PROGRAMS = \ lxc-init @@ -194,6 +194,7 @@ lxc_unfreeze_SOURCES = lxc_unfreeze.c lxc_unshare_SOURCES = lxc_unshare.c lxc_wait_SOURCES = lxc_wait.c lxc_kill_SOURCES = lxc_kill.c +lxc_create_SOURCES = lxc_create.c install-exec-local: install-soPROGRAMS mkdir -p $(DESTDIR)$(datadir)/lxc diff --git a/src/lxc/arguments.h b/src/lxc/arguments.h index 37de4b562..b01bd0826 100644 --- a/src/lxc/arguments.h +++ b/src/lxc/arguments.h @@ -75,6 +75,13 @@ struct lxc_arguments { /* close fds from parent? */ int close_all_fds; + /* lxc-create */ + char *bdevtype, *configfile, *template; + char *fstype; + unsigned long fssize; + char *lvname, *vgname; + char *zfsroot, *lowerdir, *dir; + /* remaining arguments */ char *const *argv; int argc; diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index 6d61d2de4..5b89ba4a6 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -410,12 +410,35 @@ static int dir_destroy(struct bdev *orig) return 0; } +static int dir_create(struct bdev *bdev, const char *dest, const char *n, + struct bdev_specs *specs) +{ + bdev->src = strdup(dest); + bdev->dest = strdup(dest); + if (!bdev->src || !bdev->dest) { + ERROR("Out of memory"); + return -1; + } + + if (mkdir_p(bdev->src, 0755) < 0) { + ERROR("Error creating %s\n", bdev->src); + return -1; + } + if (mkdir_p(bdev->dest, 0755) < 0) { + ERROR("Error creating %s\n", bdev->dest); + return -1; + } + + return 0; +} + struct bdev_ops dir_ops = { .detect = &dir_detect, .mount = &dir_mount, .umount = &dir_umount, .clone_paths = &dir_clonepaths, .destroy = &dir_destroy, + .create = &dir_create, }; @@ -620,12 +643,51 @@ static int zfs_destroy(struct bdev *orig) exit(1); } +static int zfs_create(struct bdev *bdev, const char *dest, const char *n, + struct bdev_specs *specs) +{ + const char *zfsroot; + char option[MAXPATHLEN]; + int ret; + pid_t pid; + + if (!specs || !specs->u.zfs.zfsroot) + zfsroot = default_zfs_root(); + else + zfsroot = specs->u.zfs.zfsroot; + + if (!(bdev->dest = strdup(dest))) { + ERROR("No mount target specified or out of memory"); + return -1; + } + if (!(bdev->src = strdup(bdev->dest))) { + ERROR("out of memory"); + return -1; + } + + ret = snprintf(option, MAXPATHLEN, "-omountpoint=%s", bdev->dest); + if (ret < 0 || ret >= MAXPATHLEN) + return -1; + if ((pid = fork()) < 0) + return -1; + if (pid) + return wait_for_pid(pid); + + char dev[MAXPATHLEN]; + ret = snprintf(dev, MAXPATHLEN, "%s/%s", zfsroot, n); + if (ret < 0 || ret >= MAXPATHLEN) + exit(1); + execlp("zfs", "zfs", "create", option, dev, NULL); + exit(1); +} + struct bdev_ops zfs_ops = { .detect = &zfs_detect, .mount = &zfs_mount, .umount = &zfs_umount, .clone_paths = &zfs_clonepaths, .destroy = &zfs_destroy, + .create = &zfs_create, }; // @@ -693,7 +755,7 @@ static int lvm_umount(struct bdev *bdev) * not yet exist. This function will attempt to create /dev/$vg/$lv of * size $size. */ -static int lvm_create(const char *path, unsigned long size) +static int do_lvm_create(const char *path, unsigned long size) { int ret, pid; char sz[24], *pathdup, *vg, *lv; @@ -839,7 +901,7 @@ static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna return -1; } } else { - if (lvm_create(new->src, size) < 0) { + if (do_lvm_create(new->src, size) < 0) { ERROR("Error creating new lvm blockdev"); return -1; } @@ -866,12 +928,71 @@ static int lvm_destroy(struct bdev *orig) return wait_for_pid(pid); } +#define DEFAULT_LVM_SZ 1024000000 +#define DEFAULT_LVM_FSTYPE "ext3" +static int lvm_create(struct bdev *bdev, const char *dest, const char *n, + struct bdev_specs *specs) +{ + const char *vg, *fstype, *lv = n; + unsigned long sz; + int ret, len; + + if (!specs) + return -1; + + vg = specs->u.lvm.vg; + if (!vg) + vg = default_lvm_vg(); + + /* /dev/$vg/$lv */ + if (specs->u.lvm.lv) + lv = specs->u.lvm.lv; + len = strlen(vg) + strlen(lv) + 7; + bdev->src = malloc(len); + if (!bdev->src) + return -1; + + ret = snprintf(bdev->src, len, "/dev/%s/%s", vg, lv); + if (ret < 0 || ret >= len) + return -1; + + // lvm.fssize is in bytes. + sz = specs->u.lvm.fssize; + if (!sz) + sz = DEFAULT_LVM_SZ; + + INFO("Error creating new lvm blockdev %s size %lu", bdev->src, sz); + if (do_lvm_create(bdev->src, sz) < 0) { + ERROR("Error creating new lvm blockdev %s size %lu", bdev->src, sz); + return -1; + } + + fstype = specs->u.lvm.fstype; + if (!fstype) + fstype = DEFAULT_LVM_FSTYPE; + if (do_mkfs(bdev->src, fstype) < 0) { + ERROR("Error creating filesystem type %s on %s", fstype, + bdev->src); + return -1; + } + if (!(bdev->dest = strdup(dest))) + return -1; + + if (mkdir_p(bdev->dest, 0755) < 0) { + ERROR("Error creating %s\n", bdev->dest); + return -1; + } + + return 0; +} + struct bdev_ops lvm_ops = { .detect = &lvm_detect, .mount = &lvm_mount, .umount = &lvm_umount, .clone_paths = &lvm_clonepaths, .destroy = &lvm_destroy, + .create = &lvm_create, }; // @@ -895,21 +1016,31 @@ struct btrfs_ioctl_space_args { #define BTRFS_IOC_SPACE_INFO _IOWR(BTRFS_IOCTL_MAGIC, 20, \ struct btrfs_ioctl_space_args) -static int btrfs_detect(const char *path) +static bool is_btrfs_fs(const char *path) { - struct stat st; int fd, ret; struct btrfs_ioctl_space_args sargs; // make sure this is a btrfs filesystem fd = open(path, O_RDONLY); if (fd < 0) - return 0; + return false; sargs.space_slots = 0; sargs.total_spaces = 0; ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, &sargs); close(fd); if (ret < 0) + return false; + + return true; +} + +static int btrfs_detect(const char *path) +{ + struct stat st; + int ret; + + if (!is_btrfs_fs(path)) return 0; // and make sure it's a subvolume. @@ -1138,12 +1269,23 @@ static int btrfs_destroy(struct bdev *orig) return ret; } +static int btrfs_create(struct bdev *bdev, const char *dest, const char *n, + struct bdev_specs *specs) +{ + bdev->src = strdup(dest); + bdev->dest = strdup(dest); + if (!bdev->src || !bdev->dest) + return -1; + return btrfs_subvolume_create(bdev->dest); +} + struct bdev_ops btrfs_ops = { .detect = &btrfs_detect, .mount = &btrfs_mount, .umount = &btrfs_umount, .clone_paths = &btrfs_clonepaths, .destroy = &btrfs_destroy, + .create = &btrfs_create, }; // @@ -1321,12 +1463,60 @@ int overlayfs_destroy(struct bdev *orig) return lxc_rmdir_onedev(upper); } +/* + * to say 'lxc-create -t ubuntu -n o1 -B overlayfs' means you want + * $lxcpath/$lxcname/rootfs to have the created container, while all + * changes after starting the container are written to + * $lxcpath/$lxcname/delta0 + */ +static int overlayfs_create(struct bdev *bdev, const char *dest, const char *n, + struct bdev_specs *specs) +{ + char *delta; + int ret, len = strlen(dest), newlen; + + if (len < 8 || strcmp(dest+len-7, "/rootfs") != 0) + return -1; + + if (!(bdev->dest = strdup(dest))) { + ERROR("Out of memory"); + return -1; + } + + delta = strdupa(dest); + strcpy(delta+len-6, "delta0"); + + if (mkdir_p(delta, 0755) < 0) { + ERROR("Error creating %s\n", delta); + return -1; + } + + /* overlayfs:lower:upper */ + newlen = (2 * len) + strlen("overlayfs:") + 2; + bdev->src = malloc(newlen); + if (!bdev->src) { + ERROR("Out of memory"); + return -1; + } + ret = snprintf(bdev->src, newlen, "overlayfs:%s:%s", dest, delta); + if (ret < 0 || ret >= newlen) + return -1; + + if (mkdir_p(bdev->dest, 0755) < 0) { + ERROR("Error creating %s\n", bdev->dest); + return -1; + } + + return 0; +} + struct bdev_ops overlayfs_ops = { .detect = &overlayfs_detect, .mount = &overlayfs_mount, .umount = &overlayfs_umount, .clone_paths = &overlayfs_clonepaths, .destroy = &overlayfs_destroy, + .create = &overlayfs_create, }; struct bdev_type bdevs[] = { @@ -1497,3 +1687,60 @@ struct bdev *bdev_copy(const char *src, const char *oldname, const char *cname, exit(0); } + +/* + * bdev_create: + * Create a backing store for a container. + * If successfull, return a struct bdev *, with the bdev mounted and ready + * for use. Before completing, the caller will need to call the + * umount operation and bdev_put(). + * @dest: the mountpoint (i.e. /var/lib/lxc/$name/rootfs) + * @type: the bdevtype (dir, btrfs, zfs, etc) + * @cname: the container name + * @specs: details about the backing store to create, like fstype + */ +struct bdev *bdev_create(const char *dest, const char *type, + const char *cname, struct bdev_specs *specs) +{ + struct bdev *bdev; + + if (!type) { + char *p, *p1; + + type = "dir"; + + /* + * $lxcpath/$lxcname/rootfs doesn't yet exist. Check + * whether $lxcpath/$lxcname is btrfs. If so, specify + * btrfs backing store for the container. + */ + p = strdupa(dest); + p1 = rindex(p, '/'); + if (p1) { + *p1 = '\0'; + if (is_btrfs_fs(p)) + type = "btrfs"; + } + } + + bdev = bdev_get(type); + if (!bdev) { + ERROR("Unknown fs type: %s\n", type); + return NULL; + } + + if (bdev->ops->create(bdev, dest, cname, specs) < 0) { + bdev_put(bdev); + return NULL; + } + + return bdev; +} + +char *overlayfs_getlower(char *p) +{ + char *p1 = index(p, ':'); + if (p1) + *p1 = '\0'; + return p; +} diff --git a/src/lxc/bdev.h b/src/lxc/bdev.h index e4f1e6080..7b60d953e 100644 --- a/src/lxc/bdev.h +++ b/src/lxc/bdev.h @@ -10,6 +10,23 @@ struct bdev; +/* + * specifications for how to create a new backing store + */ +struct bdev_specs { + union { + struct { + char *zfsroot; + } zfs; + struct { + char *vg; + char *lv; + char *fstype; + unsigned long fssize; // fs size in bytes + } lvm; + } u; +}; + struct bdev_ops { /* detect whether path is of this bdev type */ int (*detect)(const char *path); @@ -17,6 +34,8 @@ struct bdev_ops { int (*mount)(struct bdev *bdev); int (*umount)(struct bdev *bdev); int (*destroy)(struct bdev *bdev); + int (*create)(struct bdev *bdev, const char *dest, const char *n, + struct bdev_specs *specs); /* given original mount, rename the paths for cloned container */ int (*clone_paths)(struct bdev *orig, struct bdev *new, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, @@ -38,6 +57,8 @@ struct bdev { char *data; }; +char *overlayfs_getlower(char *p); + /* * Instantiate a bdev object. The src is used to determine which blockdev * type this should be. The dst and data are optional, and will be used @@ -54,6 +75,8 @@ struct bdev *bdev_init(const char *src, const char *dst, const char *data); struct bdev *bdev_copy(const char *src, const char *oldname, const char *cname, const char *oldpath, const char *lxcpath, const char *bdevtype, int snap, const char *bdevdata, unsigned long newsize); +struct bdev *bdev_create(const char *dest, const char *type, + const char *cname, struct bdev_specs *specs); void bdev_put(struct bdev *bdev); /* define constants if the kernel/glibc headers don't define them */ diff --git a/src/lxc/lxc_create.c b/src/lxc/lxc_create.c new file mode 100644 index 000000000..261360430 --- /dev/null +++ b/src/lxc/lxc_create.c @@ -0,0 +1,205 @@ +/* + * + * Copyright © 2013 Serge Hallyn . + * Copyright © 2013 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 + +#include "arguments.h" +#include "utils.h" + +lxc_log_define(lxc_create, lxc); + +/* we pass fssize in bytes */ +static unsigned long get_fssize(char *s) +{ + unsigned long ret; + char *end; + + ret = strtoul(s, &end, 0); + if (end == s) + return 0; + while (isblank(*end)) + end++; + if (!(*end)) + return ret; + if (*end == 'g' || *end == 'G') + ret *= 1000000000; + else if (*end == 'm' || *end == 'M') + ret *= 1000000; + else if (*end == 'k' || *end == 'K') + ret *= 1000; + return ret; +} + +static int my_parser(struct lxc_arguments* args, int c, char* arg) +{ + switch (c) { + case 'B': args->bdevtype = arg; break; + case 'f': args->configfile = arg; break; + case 't': args->template = arg; break; + case '0': args->lvname = arg; break; + case '1': args->vgname = arg; break; + case '2': args->fstype = arg; break; + case '3': args->fssize = get_fssize(arg); break; + case '4': args->zfsroot = arg; break; + case '5': args->dir = arg; break; + } + return 0; +} + +static const struct option my_longopts[] = { + {"bdev", required_argument, 0, 'B'}, + {"config", required_argument, 0, 'f'}, + {"template", required_argument, 0, 't'}, + {"lvname", required_argument, 0, '0'}, + {"vgname", required_argument, 0, '1'}, + {"fstype", required_argument, 0, '2'}, + {"fssize", required_argument, 0, '3'}, + {"zfsroot", required_argument, 0, '4'}, + {"dir", required_argument, 0, '5'}, + LXC_COMMON_OPTIONS +}; + +static struct lxc_arguments my_args = { + .progname = "lxc-create", + .help = "\ +--name=NAME [-w] [-r] [-t timeout] [-P lxcpath]\n\ +\n\ +lxc-creae creates a container\n\ +\n\ +Options :\n\ + -n, --name=NAME NAME for name of the container\n\ + -f, --config=file initial configuration file\n\ + -t, --template=t template to use to setup container\n\ + -B, --bdev=BDEV backing store type to use\n\ + --lxcpath=PATH place container under PATH\n\ + --lvname=LVNAME Use LVM lv name LVNAME\n\ + (Default: container name)\n\ + --vgname=VG Use LVM vg called VG\n\ + (Default: lxc))\n\ + --fstype=TYPE Create fstype TYPE\n\ + (Default: ext3))\n\ + --fssize=SIZE Create filesystem of size SIZE\n\ + (Default: 1G))\n\ + --dir=DIR Place rootfs directory under DIR\n\ + --zfsroot=PATH Create zfs under given zfsroot\n\ + (Default: tank/lxc))\n", + .options = my_longopts, + .parser = my_parser, + .checker = NULL, +}; + +bool validate_bdev_args(struct lxc_arguments *a) +{ + if (strcmp(a->bdevtype, "lvm") != 0) { + if (a->fstype || a->fssize) { + fprintf(stderr, "filesystem type and size are only valid with block devices\n"); + return false; + } + if (a->lvname || a->vgname) { + fprintf(stderr, "--lvname and --vgname are only valid with -B lvm\n"); + return false; + } + } + if (strcmp(a->bdevtype, "zfs") != 0) { + if (a->zfsroot) { + fprintf(stderr, "zfsroot is only valid with -B zfs\n"); + return false; + } + } + return true; +} + +/* grab this through autoconf from @config-path@ ? */ +#define DEFAULT_CONFIG "/etc/lxc/default.conf" +int main(int argc, char *argv[]) +{ + struct lxc_container *c; + struct bdev_specs spec; + + /* this is a short term test. We'll probably want to check for + * write access to lxcpath instead */ + if (geteuid()) { + fprintf(stderr, "%s must be run as root\n", argv[0]); + exit(1); + } + + if (lxc_arguments_parse(&my_args, argc, argv)) + exit(1); + + if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority, + my_args.progname, my_args.quiet, my_args.lxcpath[0])) + exit(1); + + memset(&spec, 0, sizeof(spec)); + if (!my_args.bdevtype) + my_args.bdevtype = "_unset"; + if (!validate_bdev_args(&my_args)) + exit(1); + + c = lxc_container_new(my_args.name, my_args.lxcpath[0]); + if (!c) { + fprintf(stderr, "System error loading container\n"); + exit(1); + } + if (c->is_defined(c)) { + fprintf(stderr, "Container already exists\n"); + exit(1); + } + if (my_args.configfile) + c->load_config(c, my_args.configfile); + else + c->load_config(c, DEFAULT_CONFIG); + + if (strcmp(my_args.bdevtype, "zfs") == 0) { + if (my_args.zfsroot) + spec.u.zfs.zfsroot = my_args.zfsroot; + } else if (strcmp(my_args.bdevtype, "lvm") == 0) { + if (my_args.lvname) + spec.u.lvm.lv = my_args.lvname; + if (my_args.vgname) + spec.u.lvm.vg = my_args.vgname; + if (my_args.fstype) + spec.u.lvm.fstype = my_args.fstype; + if (my_args.fssize) + spec.u.lvm.fssize = my_args.fssize; + } else if (my_args.dir) { + ERROR("--dir is not yet supported"); + exit(1); + } + + if (strcmp(my_args.bdevtype, "_unset") == 0) + my_args.bdevtype = NULL; + if (!c->create(c, my_args.template, my_args.bdevtype, &spec, &argv[optind])) { + ERROR("Error creating container %s", c->name); + lxc_container_put(c); + exit(1); + } + INFO("container %s created", c->name); + exit(0); +} diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 1d1174259..2ea9556b3 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -106,7 +106,7 @@ int create_partial(struct lxc_container *c) ERROR("Error writing partial pathname"); return -1; } - if (process_lock() < 0) + if (process_lock()) return -1; if ((fd=open(path, O_CREAT | O_EXCL, 0755)) < 0) { SYSERROR("Erorr creating partial file"); @@ -645,12 +645,54 @@ static bool create_container_dir(struct lxc_container *c) return ret == 0; } +static const char *lxcapi_get_config_path(struct lxc_container *c); +static bool lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v); + /* - * 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. + * do_bdev_create: thin wrapper around bdev_create(). Like bdev_create(), + * it returns a mounted bdev on success, NULL on error. */ -static bool lxcapi_create(struct lxc_container *c, const char *t, char *const argv[]) +static struct bdev *do_bdev_create(struct lxc_container *c, const char *type, + struct bdev_specs *specs) +{ + char *dest; + const char *lxcpath = lxcapi_get_config_path(c); + size_t len; + struct bdev *bdev; + int ret; + + /* lxcpath/lxcname/rootfs */ + len = strlen(c->name) + strlen(lxcpath) + 9; + dest = alloca(len); + ret = snprintf(dest, len, "%s/%s/rootfs", lxcpath, c->name); + if (ret < 0 || ret >= len) + return NULL; + + bdev = bdev_create(dest, type, c->name, specs); + if (!bdev) + return NULL; + lxcapi_set_config_item(c, "lxc.rootfs", bdev->src); + return bdev; +} + +static bool lxcapi_destroy(struct lxc_container *c); +/* + * lxcapi_create: + * create a container with the given parameters. + * @c: container to be created. It has the lxcpath, name, and a starting + * configuration already set + * @t: the template to execute to instantiate the root filesystem and + * adjust the configuration. + * @bdevtype: backing store type to use. If NULL, dir will be used. + * @specs: additional parameters for the backing store, i.e. LVM vg to + * use. + * + * @argv: 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, const char *t, + const char *bdevtype, struct bdev_specs *specs, + char *const argv[]) { bool bret = false; pid_t pid; @@ -685,12 +727,15 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, char *const ar if ((partial_fd = create_partial(c)) < 0) goto out; - /* we're going to fork. but since we'll wait for our child, we - * don't need to lxc_container_get */ - - if (container_disk_lock(c)) - goto out; + /* no need to get disk lock bc we have the partial locked */ + /* + * Create the backing store + * Note we can't do this in the same task as we use to execute the + * template because of the way zfs works. + * After you 'zfs create', zfs mounts the fs only in the initial + * namespace. + */ pid = fork(); if (pid < 0) { SYSERROR("failed to fork task for container creation template\n"); @@ -698,7 +743,46 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, char *const ar } if (pid == 0) { // child - char *patharg, *namearg; + struct bdev *bdev = NULL; + + if (!(bdev = do_bdev_create(c, bdevtype, specs))) { + ERROR("Error creating backing store type %s for %s", + bdevtype ? bdevtype : "(none)", c->name); + exit(1); + } + + /* save config file again to store the new rootfs location */ + if (!c->save_config(c, NULL)) { + ERROR("failed to save starting configuration for %s\n", c->name); + // parent task won't see bdev in config so we delete it + bdev->ops->umount(bdev); + bdev->ops->destroy(bdev); + exit(1); + } + exit(0); + } + if (wait_for_pid(pid) != 0) + goto out; + + /* reload config to get the rootfs */ + if (c->lxc_conf) + lxc_conf_free(c->lxc_conf); + c->lxc_conf = NULL; + if (!load_config_locked(c, c->configfile)) + goto out; + + /* + * now execute the template + */ + 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, *rootfsarg, *src; + struct bdev *bdev = NULL; int i; close(0); @@ -708,13 +792,38 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, char *const ar open("/dev/null", O_RDWR); open("/dev/null", O_RDWR); + if (unshare(CLONE_NEWNS) < 0) { + ERROR("error unsharing mounts"); + exit(1); + } + + src = c->lxc_conf->rootfs.path; + /* + * for an overlayfs create, what the user wants is the template to fill + * in what will become the readonly lower layer. So don't mount for + * the template + */ + if (strncmp(src, "overlayfs:", 10) == 0) { + src = overlayfs_getlower(src+10); + } + bdev = bdev_init(src, c->lxc_conf->rootfs.mount, NULL); + if (!bdev) { + ERROR("Error opening rootfs"); + exit(1); + } + + if (bdev->ops->mount(bdev) < 0) { + ERROR("Error mounting rootfs"); + exit(1); + } + /* * 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 + for (nargs = 0; argv[nargs]; nargs++) ; + nargs += 4; // template, path, rootfs and name args newargv = malloc(nargs * sizeof(*newargv)); if (!newargv) exit(1); @@ -737,10 +846,19 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, char *const ar exit(1); newargv[2] = namearg; + len = strlen("--rootfs=") + 1 + strlen(bdev->dest); + rootfsarg = malloc(len); + if (!rootfsarg) + exit(1); + ret = snprintf(rootfsarg, len, "--rootfs=%s", bdev->dest); + if (ret < 0 || ret >= len) + exit(1); + newargv[3] = rootfsarg; + /* add passed-in args */ if (argv) - for (i = 3; i < nargs; i++) - newargv[i] = argv[i-3]; + for (i = 4; i < nargs; i++) + newargv[i] = argv[i-4]; /* add trailing NULL */ nargs++; @@ -774,6 +892,8 @@ out_unlock: out: if (tpath) free(tpath); + if (!bret && c) + lxcapi_destroy(c); return bret; } @@ -818,7 +938,8 @@ static bool lxcapi_shutdown(struct lxc_container *c, int timeout) return retv; } -static bool lxcapi_createl(struct lxc_container *c, const char *t, ...) +static bool lxcapi_createl(struct lxc_container *c, const char *t, + const char *bdevtype, struct bdev_specs *specs, ...) { bool bret = false; char **args = NULL, **temp; @@ -832,7 +953,7 @@ static bool lxcapi_createl(struct lxc_container *c, const char *t, ...) * 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); + va_start(ap, specs); while (1) { char *arg; arg = va_arg(ap, char *); @@ -851,7 +972,7 @@ static bool lxcapi_createl(struct lxc_container *c, const char *t, ...) if (args) args[nargs] = NULL; - bret = c->create(c, t, args); + bret = c->create(c, t, bdevtype, specs, args); out: if (args) @@ -1048,15 +1169,13 @@ static bool lxcapi_save_config(struct lxc_container *c, const char *alt_file) return true; } -static const char *lxcapi_get_config_path(struct lxc_container *c); // do we want the api to support --force, or leave that to the caller? static bool lxcapi_destroy(struct lxc_container *c) { - struct bdev *r; + struct bdev *r = NULL; bool ret = false; - /* container is already destroyed if we don't have a config and rootfs.path is not accessible */ - if (!c || !lxcapi_is_defined(c) || !c->lxc_conf || !c->lxc_conf->rootfs.path) + if (!c || !lxcapi_is_defined(c)) return false; if (lxclock(c->privlock, 0)) @@ -1072,7 +1191,8 @@ static bool lxcapi_destroy(struct lxc_container *c) goto out; } - r = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); + if (c->lxc_conf->rootfs.path && c->lxc_conf->rootfs.mount) + r = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); if (r) { if (r->ops->destroy(r) < 0) { ERROR("Error destroying rootfs for %s", c->name); @@ -1848,8 +1968,9 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath if (ongoing_create(c) == 2) { ERROR("Error: %s creation was not completed", c->name); - c->destroy(c); - goto err; + lxcapi_destroy(c); + lxc_conf_free(c->lxc_conf); + c->lxc_conf = NULL; } // assign the member functions diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index 3a80d0f9f..bd3d22a54 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -12,6 +12,8 @@ #define LXC_CLONE_SNAPSHOT (1 << 3) #define LXC_CLONE_MAXFLAGS (1 << 4) +struct bdev_specs; + struct lxc_container { // private fields char *name; @@ -48,8 +50,10 @@ struct lxc_container { bool (*set_config_item)(struct lxc_container *c, const char *key, const char *value); bool (*destroy)(struct lxc_container *c); bool (*save_config)(struct lxc_container *c, const char *alt_file); - bool (*create)(struct lxc_container *c, const char *t, char *const argv[]); - bool (*createl)(struct lxc_container *c, const char *t, ...); + bool (*create)(struct lxc_container *c, const char *t, const char *bdevtype, + struct bdev_specs *specs, char *const argv[]); + bool (*createl)(struct lxc_container *c, const char *t, const char *bdevtype, + struct bdev_specs *specs, ...); /* send SIGINT to ask container to reboot */ bool (*reboot)(struct lxc_container *c); /* send SIGPWR. if timeout is not 0 or -1, do a hard stop after timeout seconds */ diff --git a/src/python-lxc/lxc.c b/src/python-lxc/lxc.c index cdc1e8b83..0b31a372e 100644 --- a/src/python-lxc/lxc.c +++ b/src/python-lxc/lxc.c @@ -249,7 +249,7 @@ Container_create(Container *self, PyObject *args, PyObject *kwds) } } - if (self->container->create(self->container, template_name, create_args)) + if (self->container->create(self->container, template_name, NULL, NULL, create_args)) retval = Py_True; else retval = Py_False; diff --git a/src/tests/cgpath.c b/src/tests/cgpath.c index 5fb1d319d..55c6664bc 100644 --- a/src/tests/cgpath.c +++ b/src/tests/cgpath.c @@ -279,7 +279,7 @@ static int test_container(const char *lxcpath, c = lxc_container_new(name, lxcpath); } c->set_config_item(c, "lxc.network.type", "empty"); - if (!c->createl(c, template, NULL)) { + if (!c->createl(c, template, NULL, NULL, NULL)) { TSTERR("creating container %s", name); goto out2; } diff --git a/src/tests/clonetest.c b/src/tests/clonetest.c index fbeacdf71..f15f400d9 100644 --- a/src/tests/clonetest.c +++ b/src/tests/clonetest.c @@ -53,7 +53,7 @@ int main(int argc, char *argv[]) goto out; } c->save_config(c, NULL); - if (!c->createl(c, "ubuntu", NULL)) { + if (!c->createl(c, "ubuntu", NULL, NULL, NULL)) { fprintf(stderr, "%d: failed to create a container\n", __LINE__); goto out; } diff --git a/src/tests/createtest.c b/src/tests/createtest.c index c2abee233..f33f59bfc 100644 --- a/src/tests/createtest.c +++ b/src/tests/createtest.c @@ -50,7 +50,7 @@ int main(int argc, char *argv[]) } 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)) { + if (!c->createl(c, "ubuntu", NULL, NULL, "-r", "lucid", NULL)) { fprintf(stderr, "%d: failed to create a lucid container\n", __LINE__); goto out; } diff --git a/src/tests/get_item.c b/src/tests/get_item.c index d3e6d29ad..95118995c 100644 --- a/src/tests/get_item.c +++ b/src/tests/get_item.c @@ -170,7 +170,7 @@ int main(int argc, char *argv[]) ret = 1; goto out; } - if (!c->createl(c, "ubuntu", "-r", "lucid", NULL)) { + if (!c->createl(c, "ubuntu", NULL, NULL, "-r", "lucid", NULL)) { fprintf(stderr, "%d: failed to create a lucid container\n", __LINE__); ret = 1; goto out; diff --git a/src/tests/shutdowntest.c b/src/tests/shutdowntest.c index a1a84e813..6e50cb097 100644 --- a/src/tests/shutdowntest.c +++ b/src/tests/shutdowntest.c @@ -51,7 +51,7 @@ int main(int argc, char *argv[]) } 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)) { + if (!c->createl(c, "ubuntu", NULL, NULL, "-r", "lucid", NULL)) { fprintf(stderr, "%d: failed to create a lucid container\n", __LINE__); goto out; } diff --git a/templates/lxc-alpine.in b/templates/lxc-alpine.in index 98347ed67..6180afd0c 100644 --- a/templates/lxc-alpine.in +++ b/templates/lxc-alpine.in @@ -150,7 +150,8 @@ die() { usage() { cat >&2 <] [-a|--arch ] - -p|--path -n|--name [PKG...] + [--rootfs ] -p|--path -n|--name + [PKG...] EOF } @@ -180,6 +181,11 @@ while [ $# -gt 0 ]; do name=$1 shift ;; + --rootfs) + optarg_check $opt "$1" + rootfs=$1 + shift + ;; -p|--path) optarg_check $opt "$1" path=$1 @@ -218,9 +224,11 @@ if [ -z "${path}" ]; then path="${default_path}/${name}" fi -rootfs=`awk -F= '$1 ~ /^lxc.rootfs/ { print $2 }' "$path/config" 2>/dev/null` if [ -z "$rootfs" ]; then - rootfs="${path}/rootfs" + rootfs=`awk -F= '$1 ~ /^lxc.rootfs/ { print $2 }' "$path/config" 2>/dev/null` + if [ -z "$rootfs" ]; then + rootfs="${path}/rootfs" + fi fi lxc_arch=$arch diff --git a/templates/lxc-altlinux.in b/templates/lxc-altlinux.in index cce214c0f..798f88249 100644 --- a/templates/lxc-altlinux.in +++ b/templates/lxc-altlinux.in @@ -337,7 +337,7 @@ usage: [-p|--path=] [-c|--clean] [-R|--release=] [-4|--ipv4=] [-6|--ipv6=] [-g|--gw=] [-d|--dns=] - [-P|--profile=] + [-P|--profile=] [--rootfs=] [-A|--arch=] [-h|--help] Mandatory args: @@ -353,12 +353,13 @@ Optional args: -d,--dns specify the DNS server, eg. 192.168.1.2 -P,--profile Profile name is the file name in /etc/lxc/profiles contained packages name for install to cache. -A,--arch NOT USED YET. Define what arch the container will be [i686,x86_64] + ---rootfs rootfs path -h,--help print this help EOF return 0 } -options=$(getopt -o hp:n:P:cR:4:6:g:d: -l help,path:,name:,profile:,clean,release:ipv4:ipv6:gw:dns: -- "$@") +options=$(getopt -o hp:n:P:cR:4:6:g:d: -l help,rootfs:,path:,name:,profile:,clean,release:ipv4:ipv6:gw:dns: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 @@ -370,6 +371,7 @@ do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; + --rootfs) rootfs_path=$2; shift 2;; -n|--name) name=$2; shift 2;; -P|--profile) profile=$2; shift 2;; -c|--clean) clean=$2; shift 2;; @@ -422,7 +424,15 @@ if [ "$(id -u)" != "0" ]; then exit 1 fi -rootfs_path=$path/$name/rootfs +# check for 'lxc.rootfs' passed in through default config by lxc-create +if [ -z "$rootfs_path" ]; then + if grep -q '^lxc.rootfs' $path/config 2>/dev/null ; then + rootfs_path=`grep 'lxc.rootfs =' $path/config | awk -F= '{ print $2 }'` + else + rootfs_path=$path/$name/rootfs + fi +fi + config_path=$default_path/$name cache=$cache_base/$release/$profile @@ -431,11 +441,6 @@ 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 98d54242a..64f16e8a8 100644 --- a/templates/lxc-archlinux.in +++ b/templates/lxc-archlinux.in @@ -125,7 +125,6 @@ lxc.utsname=${name} lxc.autodev=1 lxc.tty=1 lxc.pts=1024 -lxc.rootfs=${rootfs_path} lxc.mount=${config_path}/fstab lxc.cap.drop=mknod sys_module mac_admin mac_override sys_time lxc.kmsg=0 @@ -153,6 +152,8 @@ lxc.cgroup.devices.allow = c 5:2 rwm lxc.cgroup.devices.allow = c 136:* rwm EOF + grep -q "^lxc.rootfs" ${config_path}/config 2>/dev/null || echo "lxc.rootfs = ${rootfs_path}" >> ${config_path}/config + cat > "${config_path}/fstab" << EOF sysfs sys sysfs ro,defaults 0 0 proc proc proc nodev,noexec,nosuid 0 0 @@ -191,7 +192,7 @@ EOF return 0 } -options=$(getopt -o hp:P:n:c:l:t: -l help,path:,packages:,name:,config:,network_type:,network_link: -- "${@}") +options=$(getopt -o hp:P:n:c:l:t: -l help,rootfs:,path:,packages:,name:,config:,network_type:,network_link: -- "${@}") if [ ${?} -ne 0 ]; then usage $(basename ${0}) exit 1 @@ -204,6 +205,7 @@ do -h|--help) usage ${0} && exit 0;; -p|--path) path=${2}; shift 2;; -n|--name) name=${2}; shift 2;; + --rootfs) rootfs_path=${2}; shift 2;; -P|--packages) additional_packages=${2}; shift 2;; -c|--config) pacman_config=${2}; shift 2;; -t|--network_type) lxc_network_type=${2}; shift 2;; @@ -238,7 +240,9 @@ if [ "${EUID}" != "0" ]; then exit 1 fi -rootfs_path="${path}/rootfs" +if [ -z "$rootfs_path" ]; then + rootfs_path="${path}/rootfs" +fi config_path="${default_path}/${name}" revert() { diff --git a/templates/lxc-busybox.in b/templates/lxc-busybox.in index db39b0e62..e224aadce 100644 --- a/templates/lxc-busybox.in +++ b/templates/lxc-busybox.in @@ -288,7 +288,7 @@ EOF return 0 } -options=$(getopt -o hp:n: -l help,path:,name: -- "$@") +options=$(getopt -o hp:n: -l help,rootfs:,path:,name: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 @@ -300,6 +300,7 @@ do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; + --rootfs) rootfs=$2; shift 2;; -n|--name) name=$2; shift 2;; --) shift 1; break ;; *) break ;; @@ -318,10 +319,12 @@ fi # 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 +if [ -z "$rootfs" ]; then + if grep -q '^lxc.rootfs' $config 2>/dev/null ; then + rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'` + else + rootfs=$path/rootfs + fi fi install_busybox $rootfs $name diff --git a/templates/lxc-debian.in b/templates/lxc-debian.in index d4ea3de59..9ae1ea728 100644 --- a/templates/lxc-debian.in +++ b/templates/lxc-debian.in @@ -284,7 +284,7 @@ EOF return 0 } -options=$(getopt -o hp:n:c -l help,path:,name:,clean -- "$@") +options=$(getopt -o hp:n:c -l help,rootfs:,path:,name:,clean -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 @@ -296,6 +296,7 @@ do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; + --rootfs) rootfs=$2; shift 2;; -n|--name) name=$2; shift 2;; -c|--clean) clean=$2; shift 2;; --) shift 1; break ;; @@ -326,10 +327,12 @@ fi # 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 +if [ -z "$rootfs" ]; then + if grep -q '^lxc.rootfs' $config 2>/dev/null ; then + rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'` + else + rootfs=$path/rootfs + fi fi diff --git a/templates/lxc-fedora.in b/templates/lxc-fedora.in index 871ae7a71..f5da7b52c 100644 --- a/templates/lxc-fedora.in +++ b/templates/lxc-fedora.in @@ -360,7 +360,8 @@ 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 @LXCPATH@. The container config will go under @LXCPATH@ in that case + -p,--path path to where the container will be created, defaults to @LXCPATH@. The container config will go under @LXCPATH@ in that case + --rootfs path for actual rootfs. -c,--clean clean the cache -R,--release Fedora release for the new container. if the host is Fedora, then it will default to the host's release. -A,--arch NOT USED YET. Define what arch the container will be [i686,x86_64] @@ -369,7 +370,7 @@ EOF return 0 } -options=$(getopt -o hp:n:cR: -l help,path:,name:,clean,release: -- "$@") +options=$(getopt -o hp:n:cR: -l help,path:,rootfs:,name:,clean,release: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 @@ -381,6 +382,7 @@ do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; + --rootfs) rootfs=$2; shift 2;; -n|--name) name=$2; shift 2;; -c|--clean) clean=$2; shift 2;; -R|--release) release=$2; shift 2;; @@ -438,10 +440,12 @@ if [ "$(id -u)" != "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 }'` +if [ -z "$rootfs_path" ]; then + 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 fi config_path=$default_path/$name cache=$cache_base/$release diff --git a/templates/lxc-opensuse.in b/templates/lxc-opensuse.in index 7d3dd1cad..8d9523699 100644 --- a/templates/lxc-opensuse.in +++ b/templates/lxc-opensuse.in @@ -342,7 +342,7 @@ EOF return 0 } -options=$(getopt -o hp:n:c -l help,path:,name:,clean -- "$@") +options=$(getopt -o hp:n:c -l help,rootfs:,path:,name:,clean -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 @@ -354,6 +354,7 @@ do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; + --rootfs) rootfs=$2; shift 2;; -n|--name) name=$2; shift 2;; -c|--clean) clean=$2; shift 2;; --) shift 1; break ;; @@ -384,10 +385,12 @@ fi # 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 +if [ -z "$rootfs" ]; then + if grep -q '^lxc.rootfs' $config 2>/dev/null ; then + rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'` + else + rootfs=$path/rootfs + fi fi install_opensuse $rootfs diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in index 70ef63245..b09131e09 100644 --- a/templates/lxc-oracle.in +++ b/templates/lxc-oracle.in @@ -348,7 +348,6 @@ lxc.utsname = $name lxc.devttydir = lxc lxc.tty = 4 lxc.pts = 1024 -lxc.rootfs = $container_rootfs lxc.mount = $cfg_dir/fstab # Uncomment these if you don't run anything that needs the capability, and # would like the container to run with less privilege. @@ -370,6 +369,7 @@ lxc.cap.drop = mac_admin mac_override setfcap setpcap lxc.cap.drop = sys_module sys_nice sys_pacct lxc.cap.drop = sys_rawio sys_time EOF + grep -q "^lxc.rootfs" $cfg_dir/config 2>/dev/null || echo "lxc.rootfs = $container_rootfs" >> $cfg_dir/config if [ $container_release_major != "4" ]; then echo "lxc.cap.drop = sys_resource" >>$cfg_dir/config @@ -610,6 +610,7 @@ usage() cat < architecture (ie. i386, x86_64) -R|--release= release to download for the new container + --rootfs= rootfs path -r|--rpms= additional rpms to install into container -u|--url= replace yum repo url (ie. local yum mirror) -t|--templatefs= copy/clone rootfs at path instead of downloading @@ -620,7 +621,7 @@ EOF return 0 } -options=$(getopt -o hp:n:a:R:r:u:t: -l help,path:,name:,arch:,release:,rpms:,url:,templatefs: -- "$@") +options=$(getopt -o hp:n:a:R:r:u:t: -l help,rootfs:,path:,name:,arch:,release:,rpms:,url:,templatefs: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 @@ -633,6 +634,7 @@ do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) cfg_dir=$2; shift 2;; + --rootfs) container_rootfs=$2; shift 2;; -n|--name) name=$2; shift 2;; -a|--arch) arch=$2; shift 2;; -R|--release) container_release_version=$2; shift 2;; @@ -696,7 +698,9 @@ else fi echo "Host is $host_distribution $host_release_version" -container_rootfs="$cfg_dir/rootfs" +if [ -z "$container_rootfs" ]; then + container_rootfs="$cfg_dir/rootfs" +fi if [ -n "$template_rootfs" ]; then container_release_get $template_rootfs diff --git a/templates/lxc-sshd.in b/templates/lxc-sshd.in index 2927c9295..5400156ba 100644 --- a/templates/lxc-sshd.in +++ b/templates/lxc-sshd.in @@ -140,12 +140,12 @@ EOF usage() { cat < +$1 -h|--help -p|--path= [--rootfs=] EOF return 0 } -options=$(getopt -o hp:n:S: -l help,path:,name:,auth-key: -- "$@") +options=$(getopt -o hp:n:S: -l help,rootfs:,path:,name:,auth-key: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 @@ -157,6 +157,7 @@ do case "$1" in -h|--help) usage $0 && exit 0;; -p|--path) path=$2; shift 2;; + --rootfs) rootfs=$2; shift 2;; -n|--name) name=$2; shift 2;; -S|--auth-key) auth_key=$2; shift 2;; --) shift 1; break ;; @@ -210,10 +211,12 @@ fi # 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 +if [ -z "$rootfs" ]; then + if grep -q '^lxc.rootfs' $config 2>/dev/null ; then + rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'` + else + rootfs=$path/rootfs + fi fi install_sshd $rootfs diff --git a/templates/lxc-ubuntu-cloud.in b/templates/lxc-ubuntu-cloud.in index 7a56398d0..37a060549 100644 --- a/templates/lxc-ubuntu-cloud.in +++ b/templates/lxc-ubuntu-cloud.in @@ -116,6 +116,7 @@ LXC Container configuration for Ubuntu Cloud images. Generic Options [ -r | --release ]: Release name of container, defaults to host +[ --rootfs ]: Path in which rootfs will be placed [ -a | --arch ]: Arhcitecture of container, defaults to host arcitecture [ -C | --cloud ]: Configure container for use with meta-data service, defaults to no [ -T | --tarball ]: Location of tarball @@ -132,7 +133,7 @@ EOF return 0 } -options=$(getopt -o a:hp:r:n:Fi:CLS:T:ds:u: -l arch:,help,path:,release:,name:,flush-cache,hostid:,auth-key:,cloud,no_locales,tarball:,debug,stream:,userdata: -- "$@") +options=$(getopt -o a:hp:r:n:Fi:CLS:T:ds:u: -l arch:,help,rootfs:,path:,release:,name:,flush-cache,hostid:,auth-key:,cloud,no_locales,tarball:,debug,stream:,userdata: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 @@ -266,10 +267,12 @@ fi # 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 +if [ -z "$rootfs" ]; then + if grep -q '^lxc.rootfs' $config 2>/dev/null ; then + rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'` + else + rootfs=$path/rootfs + fi fi type ubuntu-cloudimg-query diff --git a/templates/lxc-ubuntu.in b/templates/lxc-ubuntu.in index 02ffa199d..1623b750b 100644 --- a/templates/lxc-ubuntu.in +++ b/templates/lxc-ubuntu.in @@ -622,6 +622,7 @@ usage() cat <] [--trim] [-d|--debug] [-F | --flush-cache] [-r|--release ] [ -S | --auth-key ] + [--rootfs ] release: the ubuntu release (e.g. precise): defaults to host release on ubuntu, otherwise uses latest LTS trim: make a minimal (faster, but not upgrade-safe) container bindhome: bind 's home into the container @@ -633,7 +634,7 @@ EOF return 0 } -options=$(getopt -o a:b:hp:r:xn:FS:d -l arch:,bindhome:,help,path:,release:,trim,name:,flush-cache,auth-key:,debug -- "$@") +options=$(getopt -o a:b:hp:r:xn:FS:d -l arch:,bindhome:,help,path:,release:,trim,name:,flush-cache,auth-key:,debug,rootfs: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 @@ -674,6 +675,7 @@ while true do case "$1" in -h|--help) usage $0 && exit 0;; + --rootfs) rootfs=$2; shift 2;; -p|--path) path=$2; shift 2;; -n|--name) name=$2; shift 2;; -F|--flush-cache) flushcache=1; shift 1;; @@ -735,10 +737,13 @@ fi # 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 +# if $rootfs exists here, it was passed in with --rootfs +if [ -z "$rootfs" ]; then + if grep -q '^lxc.rootfs' $config 2>/dev/null ; then + rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'` + else + rootfs=$path/rootfs + fi fi install_ubuntu $rootfs $release $flushcache From 64f782ca69c70fd155427a81d69fda593981e770 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 28 May 2013 14:07:43 -0500 Subject: [PATCH 144/325] lxc.conf.sgml.in: fill in missing configuration file statements Signed-off-by: Serge Hallyn --- doc/lxc.conf.sgml.in | 139 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in index af8663add..6500e50ba 100644 --- a/doc/lxc.conf.sgml.in +++ b/doc/lxc.conf.sgml.in @@ -75,6 +75,32 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA the line is a comment. + + Configuration + + In order to ease administration of multiple related containers, it + is possible to have a container configuration file cause another + file to be loaded. For instance, network configuration + can be defined in one common file which is included by multiple + containers. Then, if the containers are moved to another host, + only one file may need to be updated. + + + + + + + + + + Specify the file to be included. The included file must be + in the same valid lxc configuration file format. + + + + + + Architecture @@ -395,6 +421,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA type, other arguments may be passed: veth/macvlan/phys. And finally (host-sided) device name. + + Standard output from the script is logged at debug level. + Standard error is not logged, but can be captured by the + hook redirecting its standard error to standard output. + @@ -415,6 +446,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA type, other arguments may be passed: veth/macvlan/phys. And finally (host-sided) device name. + + Standard output from the script is logged at debug level. + Standard error is not logged, but can be captured by the + hook redirecting its standard error to standard output. + @@ -738,6 +774,56 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Apparmor profile + + If lxc was compiled and installed with apparmor support, and the host + system has apparmor enabled, then the apparmor profile under which the + container should be run can be specified in the container + configuration. The default is lxc-container-default. + + + + + + + + + Specify the apparmor profile under which the container should + be run. To specify that the container should be unconfined, + use + + lxc.aa_profile = unconfined + + + + + + + Seccomp configuration + + A container can be started with a reduced set of available + system calls by loading a seccomp profile at startup. The + seccomp configuration file should begin with a version number + (which currently must be 1) on the first line, a policy type + (which must be 'whitelist') on the second line, followed by a + list of allowed system call numbers, one per line. + + + + + + + + + Specify a file containing the seccomp configuration to + load before the container starts. + + + + + + UID mappings @@ -775,6 +861,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Container hooks are programs or scripts which can be executed at various times in a container's lifetime. + + Standard output from the hooks is logged at debug level. + Standard error is not logged, but can be captured by the + hook redirecting its standard error to standard output. + @@ -978,6 +1069,54 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Logging + + Logging can be configured on a per-container basis. By default, + depending upon how the lxc package was compiled, container startup + is logged only at the ERROR level, and logged to a file named after + the container (with '.log' appended) either under the container path, + or under @LOGPATH@. + + + Both the default log level and the log file can be specified in the + container configuration file, overriding the default behavior. Note + that the configuration file entries can in turn be overridden by the + command line options to lxc-start. + + + + + + + + + The level at which to log. The log level is an integer in + the range of 0..8 inclusive, where a lower number means more + verbose debugging. In particular 0 = trace, 1 = debug, 2 = + info, 3 = notice, 4 = warn, 5 = error, 6 = critical, 7 = + alert, and 8 = fatal. If unspecified, the level defaults + to 5 (error), so that only errors and above are logged. + + + Note that when a script (such as either a hook script or a + network interface up or down script) is called, the script's + standard output is logged at level 1, debug. + + + + + + + + + + The file to which logging info should be written. + + + + + From 5790f7b7a76b9ccff662fdd6ff0013b8f218d020 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 28 May 2013 15:27:42 -0500 Subject: [PATCH 145/325] api_clone: call is_stopped_locked() to avoid deadlock. Technically as Dwight has mentioned we should probably drop the locking from api_state() altogether, since those are protected through the lxc command system. 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 2ea9556b3..b34b8e85d 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -1827,7 +1827,7 @@ struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname, if (container_mem_lock(c)) return NULL; - if (c->is_running(c)) { + if (!is_stopped_locked(c)) { ERROR("error: Original container (%s) is running", c->name); goto out; } From dc5e436e702f0bf4001e3e6e9f855443b2fcf448 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 28 May 2013 15:18:22 -0400 Subject: [PATCH 146/325] lxc.spec.in: remove lxc-shutdown (for commit 3e625e2d) Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- lxc.spec.in | 1 - 1 file changed, 1 deletion(-) diff --git a/lxc.spec.in b/lxc.spec.in index 058393a10..7a772cacb 100644 --- a/lxc.spec.in +++ b/lxc.spec.in @@ -100,7 +100,6 @@ rm -rf %{buildroot} %attr(4111,root,root) %{_bindir}/lxc-attach %attr(4111,root,root) %{_bindir}/lxc-create %attr(4111,root,root) %{_bindir}/lxc-clone -%attr(4111,root,root) %{_bindir}/lxc-shutdown %attr(4111,root,root) %{_bindir}/lxc-start %attr(4111,root,root) %{_bindir}/lxc-netstat %attr(4111,root,root) %{_bindir}/lxc-unshare From 3db989bad5d58bafac80f448e1dd2d048e791478 Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Mon, 27 May 2013 19:10:38 +0800 Subject: [PATCH 147/325] lxc-console: use fd instead of 0 in setup_tios We should use the fd specified by caller. Signed-off-by: Qiang Huang Signed-off-by: Serge Hallyn --- src/lxc/lxc_console.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lxc/lxc_console.c b/src/lxc/lxc_console.c index 820794ab9..688de8070 100644 --- a/src/lxc/lxc_console.c +++ b/src/lxc/lxc_console.c @@ -109,7 +109,7 @@ static int setup_tios(int fd, struct termios *newtios, struct termios *oldtios) } /* Get current termios */ - if (tcgetattr(0, oldtios)) { + if (tcgetattr(fd, oldtios)) { SYSERROR("failed to get current terminal settings"); return -1; } @@ -125,7 +125,7 @@ static int setup_tios(int fd, struct termios *newtios, struct termios *oldtios) newtios->c_cc[VTIME] = 0; /* Set new attributes */ - if (tcsetattr(0, TCSAFLUSH, newtios)) { + if (tcsetattr(fd, TCSAFLUSH, newtios)) { ERROR("failed to set new terminal settings"); return -1; } From 0115f8fd27b1a31d367bb161a121694f92b45e62 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Tue, 28 May 2013 15:25:41 -0400 Subject: [PATCH 148/325] add console to lxc api Make lxc_cmd_console() return the fd from the socket connection to the caller. This fd keeps the tty slot allocated until the caller closes it. Returning the fd allows for a long lived process to close the fd and reuse consoles. Add API function for console allocation. Create test program for console API. Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- .gitignore | 6 +- src/lxc/commands.c | 44 +++++----- src/lxc/commands.h | 2 +- src/lxc/lxc_console.c | 9 ++- src/lxc/lxccontainer.c | 11 +++ src/lxc/lxccontainer.h | 15 ++++ src/tests/Makefile.am | 6 +- src/tests/console.c | 177 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 245 insertions(+), 25 deletions(-) create mode 100644 src/tests/console.c diff --git a/.gitignore b/.gitignore index a661bccd2..36a56876b 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ templates/lxc-alpine templates/lxc-altlinux templates/lxc-archlinux templates/lxc-busybox +templates/lxc-cirros templates/lxc-debian templates/lxc-fedora templates/lxc-opensuse @@ -70,17 +71,20 @@ src/lxc/legacy/lxc-ls src/python-lxc/build/ src/python-lxc/lxc/__pycache__/ +src/tests/lxc-test-cgpath +src/tests/lxc-test-clonetest +src/tests/lxc-test-console 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-lxcpath src/tests/lxc-test-saveconfig src/tests/lxc-test-shutdowntest src/tests/lxc-test-startone - config/compile config/config.guess config/config.sub diff --git a/src/lxc/commands.c b/src/lxc/commands.c index 169914e18..b4afc07d4 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -115,7 +115,8 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd) * stored directly in data and datalen will be 0. * * As a special case, the response for LXC_CMD_CONSOLE is created - * here as it contains an fd passed through the unix socket. + * here as it contains an fd for the master pty passed through the + * unix socket. */ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd) { @@ -132,13 +133,19 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd) if (cmd->req.cmd == LXC_CMD_CONSOLE) { struct lxc_cmd_console_rsp_data *rspdata; + /* recv() returns 0 bytes when a tty cannot be allocated, + * rsp->ret is < 0 when the peer permission check failed + */ + if (ret == 0 || rsp->ret < 0) + return 0; + rspdata = malloc(sizeof(*rspdata)); if (!rspdata) { ERROR("command %s couldn't allocate response buffer", lxc_cmd_str(cmd->req.cmd)); return -1; } - rspdata->fd = rspfd; + rspdata->masterfd = rspfd; rspdata->ttynum = PTR_TO_INT(rsp->data); rsp->data = rspdata; } @@ -214,8 +221,9 @@ static int lxc_cmd_rsp_send(int fd, struct lxc_cmd_rsp *rsp) * the fd cannot be closed because it is used as a placeholder to indicate * that a particular tty slot is in use. The fd is also used as a signal to * the container that when the caller dies or closes the fd, the container - * will notice the fd in its mainloop select and then free the slot with - * lxc_cmd_fd_cleanup(). + * will notice the fd on its side of the socket in its mainloop select and + * then free the slot with lxc_cmd_fd_cleanup(). The socket fd will be + * returned in the cmd response structure. */ static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped, const char *lxcpath) @@ -262,8 +270,10 @@ static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped, ret = lxc_cmd_rsp_recv(sock, cmd); out: - if (!stay_connected || ret < 0) + if (!stay_connected || ret <= 0) close(sock); + if (stay_connected && ret > 0) + cmd->rsp.ret = sock; return ret; } @@ -544,7 +554,7 @@ static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req, * @fd : out: file descriptor for master side of pty * @lxcpath : the lxcpath in which the container is running * - * Returns 0 on success, < 0 on failure + * Returns fd holding tty allocated on success, < 0 on failure */ int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath) { @@ -557,31 +567,29 @@ int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath) ret = lxc_cmd(name, &cmd, &stopped, lxcpath); if (ret < 0) return ret; - if (ret == 0) { - ERROR("console %d invalid or all consoles busy", *ttynum); + + if (cmd.rsp.ret < 0) { + ERROR("console access denied: %s", strerror(-cmd.rsp.ret)); ret = -1; goto out; } - ret = -1; - #if 1 /* FIXME: how can this happen? */ - if (cmd.rsp.ret) { - ERROR("console access denied: %s", - strerror(-cmd.rsp.ret)); + if (ret == 0) { + ERROR("console %d invalid,busy or all consoles busy", *ttynum); + ret = -1; goto out; } - #endif rspdata = cmd.rsp.data; - if (rspdata->fd < 0) { + if (rspdata->masterfd < 0) { ERROR("unable to allocate fd for tty %d", rspdata->ttynum); goto out; } - ret = 0; - *fd = rspdata->fd; + ret = cmd.rsp.ret; /* sock fd */ + *fd = rspdata->masterfd; *ttynum = rspdata->ttynum; - INFO("tty %d allocated", rspdata->ttynum); + INFO("tty %d allocated fd %d sock %d", rspdata->ttynum, *fd, ret); out: free(cmd.rsp.data); return ret; diff --git a/src/lxc/commands.h b/src/lxc/commands.h index 08bde9cb6..c3738cdc4 100644 --- a/src/lxc/commands.h +++ b/src/lxc/commands.h @@ -61,7 +61,7 @@ struct lxc_cmd_rr { }; struct lxc_cmd_console_rsp_data { - int fd; + int masterfd; int ttynum; }; diff --git a/src/lxc/lxc_console.c b/src/lxc/lxc_console.c index 688de8070..52e6988f9 100644 --- a/src/lxc/lxc_console.c +++ b/src/lxc/lxc_console.c @@ -184,7 +184,7 @@ static int master_handler(int fd, void *data, struct lxc_epoll_descr *descr) int main(int argc, char *argv[]) { - int err, std_in = 1; + int err, ttyfd, std_in = 1; struct lxc_epoll_descr descr; struct termios newtios, oldtios; @@ -203,9 +203,11 @@ int main(int argc, char *argv[]) return -1; } - err = lxc_cmd_console(my_args.name, &my_args.ttynum, &master, my_args.lxcpath[0]); - if (err) + ttyfd = lxc_cmd_console(my_args.name, &my_args.ttynum, &master, my_args.lxcpath[0]); + if (ttyfd < 0) { + err = ttyfd; goto out; + } fprintf(stderr, "\n\ Connected to tty %1$d\n\ @@ -249,6 +251,7 @@ Type to exit the console, \ goto out_mainloop_open; } + close(ttyfd); err = 0; out_mainloop_open: diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index b34b8e85d..5a9cdf127 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -350,6 +350,16 @@ static bool lxcapi_unfreeze(struct lxc_container *c) return true; } +static int lxcapi_console(struct lxc_container *c, int *ttynum, int *masterfd) +{ + int ttyfd; + if (!c) + return -1; + + ttyfd = lxc_cmd_console(c->name, ttynum, masterfd, c->config_path); + return ttyfd; +} + static pid_t lxcapi_init_pid(struct lxc_container *c) { if (!c) @@ -1979,6 +1989,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath c->is_running = lxcapi_is_running; c->freeze = lxcapi_freeze; c->unfreeze = lxcapi_unfreeze; + c->console = lxcapi_console; c->init_pid = lxcapi_init_pid; c->load_config = lxcapi_load_config; c->want_daemonize = lxcapi_want_daemonize; diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index bd3d22a54..5078f03cb 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -115,6 +115,21 @@ struct lxc_container { const char *lxcpath, int flags, const char *bdevtype, const char *bdevdata, unsigned long newsize, char **hookargs); + /* lxcapi_console: allocate a console tty from container @c + * + * @c : the running container + * @ttynum : in : tty number to attempt to allocate or -1 to + * allocate the first available tty + * out: the tty number that was allocated + * @masterfd : out: fd refering to the master side of pty + * + * Returns "ttyfd" on success, -1 on failure. The returned "ttyfd" is + * used to keep the tty allocated. The caller should close "ttyfd" to + * indicate that it is done with the allocated console so that it can + * be allocated by another caller. + */ + int (*console)(struct lxc_container *c, int *ttynum, int *masterfd); + #if 0 bool (*commit_cgroups)(struct lxc_container *c); bool (*reread_cgroups)(struct lxc_container *c); diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index c0ce6484a..ba24d2c7a 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -14,6 +14,7 @@ lxc_test_getkeys_SOURCES = getkeys.c lxc_test_lxcpath_SOURCES = lxcpath.c lxc_test_cgpath_SOURCES = cgpath.c lxc_test_clonetest_SOURCES = clonetest.c +lxc_test_console_SOURCES = console.c AM_CFLAGS=-I$(top_srcdir)/src \ -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ @@ -24,7 +25,7 @@ AM_CFLAGS=-I$(top_srcdir)/src \ 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 lxc-test-lxcpath \ - lxc-test-cgpath lxc-test-clonetest + lxc-test-cgpath lxc-test-clonetest lxc-test-console endif @@ -40,4 +41,5 @@ EXTRA_DIST = \ saveconfig.c \ shutdowntest.c \ clonetest.c \ - startone.c + startone.c \ + console.c diff --git a/src/tests/console.c b/src/tests/console.c new file mode 100644 index 000000000..a3e7bca14 --- /dev/null +++ b/src/tests/console.c @@ -0,0 +1,177 @@ +/* liblxcapi + * + * Copyright © 2013 Oracle. + * + * Authors: + * Dwight Engen + * + * 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 + +#define TTYCNT 4 +#define TTYCNT_STR "4" +#define TSTNAME "lxcconsoletest" +#define MAXCONSOLES 512 + +#define TSTERR(fmt, ...) do { \ + fprintf(stderr, "%s:%d " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ +} while (0) + +static void test_console_close_all(int ttyfd[MAXCONSOLES], + int masterfd[MAXCONSOLES]) +{ + int i; + + for (i = 0; i < MAXCONSOLES; i++) { + if (masterfd[i] != -1) { + close(masterfd[i]); + masterfd[i] = -1; + } + if (ttyfd[i] != -1) { + close(ttyfd[i]); + ttyfd[i] = -1; + } + } +} + +static int test_console_running_container(struct lxc_container *c) +{ + int nrconsoles, i, ret = -1; + int ttynum [MAXCONSOLES]; + int ttyfd [MAXCONSOLES]; + int masterfd[MAXCONSOLES]; + + for (i = 0; i < MAXCONSOLES; i++) + ttynum[i] = ttyfd[i] = masterfd[i] = -1; + + ttynum[0] = 1; + ret = c->console(c, &ttynum[0], &masterfd[0]); + if (ret < 0) { + TSTERR("console allocate failed"); + goto err1; + } + ttyfd[0] = ret; + if (ttynum[0] != 1) { + TSTERR("console allocate got bad ttynum %d", ttynum[0]); + goto err2; + } + + /* attempt to alloc same ttynum */ + ret = c->console(c, &ttynum[0], &masterfd[1]); + if (ret != -1) { + TSTERR("console allocate should fail for allocated ttynum %d", ttynum[0]); + goto err2; + } + close(masterfd[0]); masterfd[0] = -1; + close(ttyfd[0]); ttyfd[0] = -1; + + /* ensure we can allocate all consoles, we do this a few times to + * show that the closes are freeing up the allocated slots + */ + for (i = 0; i < 10; i++) { + for (nrconsoles = 0; nrconsoles < MAXCONSOLES; nrconsoles++) { + ret = c->console(c, &ttynum[nrconsoles], &masterfd[nrconsoles]); + if (ret < 0) + break; + ttyfd[nrconsoles] = ret; + } + if (nrconsoles != TTYCNT) { + TSTERR("didn't allocate all consoles %d != %d", nrconsoles, TTYCNT); + goto err2; + } + test_console_close_all(ttyfd, masterfd); + } + ret = 0; + +err2: + test_console_close_all(ttyfd, masterfd); +err1: + return ret; +} + +/* test_container: test console function + * + * @lxcpath : the lxcpath in which to create the container + * @group : name of the container group or NULL for default "lxc" + * @name : name of the container + * @template : template to use when creating the container + */ +static int test_console(const char *lxcpath, + const char *group, const char *name, + const char *template) +{ + int ret; + struct lxc_container *c = NULL; + + if (lxcpath) { + ret = mkdir(lxcpath, 0755); + if (ret < 0 && errno != EEXIST) { + TSTERR("failed to mkdir %s %s", lxcpath, strerror(errno)); + goto out1; + } + } + ret = -1; + + if ((c = lxc_container_new(name, lxcpath)) == NULL) { + TSTERR("instantiating container %s", name); + goto out1; + } + if (c->is_defined(c)) { + c->stop(c); + c->destroy(c); + c = lxc_container_new(name, lxcpath); + } + if (!c->createl(c, template, NULL, NULL, NULL)) { + TSTERR("creating container %s", name); + goto out2; + } + c->load_config(c, NULL); + c->set_config_item(c, "lxc.tty", TTYCNT_STR); + c->save_config(c, NULL); + c->want_daemonize(c); + if (!c->startl(c, 0, NULL)) { + TSTERR("starting container %s", name); + goto out3; + } + + ret = test_console_running_container(c); + + c->stop(c); +out3: + c->destroy(c); +out2: + lxc_container_put(c); +out1: + return ret; +} + +int main(int argc, char *argv[]) +{ + int ret; + ret = test_console(NULL, NULL, TSTNAME, "busybox"); + if (ret < 0) + goto err1; + + ret = test_console("/var/lib/lxctest2", NULL, TSTNAME, "busybox"); + if (ret < 0) + goto err1; + printf("All tests passed\n"); +err1: + return ret; +} From 39dc698cb4025516a3428a68e19da05feb6fc0e9 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Wed, 29 May 2013 12:26:25 -0500 Subject: [PATCH 149/325] lxccontainer: don't lock around getstate and freeze/unfreeze (v2) Those go through commands.c and are already mutex'ed that way. Also remove a unmatched container_disk_unlock in lxcapi_create. Since is_stopped uses getstate which is no longer locked, rename it to drop the _locked suffix. And convert save_config to taking the disk lock. This way the save_ and load_config are mutexing each other, as they should. Changelog: May 29: Per Dwight's comment, take the lock before opening the config FILE *. Only take disklock at load and save_config when we're using the container's config file, not when read/writing from/to another file. Signed-off-by: Serge Hallyn Acked-by: Dwight Engen --- src/lxc/lxccontainer.c | 92 ++++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 31 deletions(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 5a9cdf127..9bc1caf56 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -287,21 +287,15 @@ out: static const char *lxcapi_state(struct lxc_container *c) { - const char *ret; lxc_state_t s; if (!c) return NULL; - if (container_disk_lock(c)) - return NULL; s = lxc_getstate(c->name, c->config_path); - ret = lxc_state2str(s); - container_disk_unlock(c); - - return ret; + return lxc_state2str(s); } -static bool is_stopped_locked(struct lxc_container *c) +static bool is_stopped(struct lxc_container *c) { lxc_state_t s; s = lxc_getstate(c->name, c->config_path); @@ -326,10 +320,7 @@ static bool lxcapi_freeze(struct lxc_container *c) if (!c) return false; - if (container_disk_lock(c)) - return false; ret = lxc_freeze(c->name, c->config_path); - container_disk_unlock(c); if (ret) return false; return true; @@ -341,10 +332,7 @@ static bool lxcapi_unfreeze(struct lxc_container *c) if (!c) return false; - if (container_disk_lock(c)) - return false; ret = lxc_unfreeze(c->name, c->config_path); - container_disk_unlock(c); if (ret) return false; return true; @@ -379,7 +367,8 @@ static bool load_config_locked(struct lxc_container *c, const char *fname) static bool lxcapi_load_config(struct lxc_container *c, const char *alt_file) { - bool ret = false; + bool ret = false, need_disklock = false; + int lret; const char *fname; if (!c) return false; @@ -389,10 +378,27 @@ static bool lxcapi_load_config(struct lxc_container *c, const char *alt_file) fname = alt_file; if (!fname) return false; - if (container_disk_lock(c)) + /* + * If we're reading something other than the container's config, + * we only need to lock the in-memory container. If loading the + * container's config file, take the disk lock. + */ + if (strcmp(fname, c->configfile) == 0) + need_disklock = true; + + if (need_disklock) + lret = container_disk_lock(c); + else + lret = container_mem_lock(c); + if (lret) return false; + ret = load_config_locked(c, fname); - container_disk_unlock(c); + + if (need_disklock) + container_disk_unlock(c); + else + container_mem_unlock(c); return ret; } @@ -898,7 +904,6 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, out_unlock: if (partial_fd >= 0) remove_partial(c, partial_fd); - container_disk_unlock(c); out: if (tpath) free(tpath); @@ -1153,30 +1158,55 @@ static int lxcapi_get_keys(struct lxc_container *c, const char *key, char *retv, #define LXC_DEFAULT_CONFIG "/etc/lxc/default.conf" static bool lxcapi_save_config(struct lxc_container *c, const char *alt_file) { + FILE *fout; + bool ret = false, need_disklock = false; + int lret; + 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 we haven't yet loaded a config, load the stock config + 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; } + } if (!create_container_dir(c)) return false; - FILE *fout = fopen(alt_file, "w"); + /* + * If we're writing to the container's config file, take the + * disk lock. Otherwise just take the memlock to protect the + * struct lxc_container while we're traversing it. + */ + if (strcmp(c->configfile, alt_file) == 0) + need_disklock = true; + + if (need_disklock) + lret = container_disk_lock(c); + else + lret = container_mem_lock(c); + + if (lret) + return false; + + fout = fopen(alt_file, "w"); if (!fout) - return false; - if (container_mem_lock(c)) { - fclose(fout); - return false; - } + goto out; write_config(fout, c->lxc_conf); fclose(fout); - container_mem_unlock(c); - return true; + ret = true; + +out: + if (need_disklock) + container_disk_unlock(c); + else + container_mem_unlock(c); + return ret; } // do we want the api to support --force, or leave that to the caller? @@ -1195,7 +1225,7 @@ static bool lxcapi_destroy(struct lxc_container *c) return false; } - if (!is_stopped_locked(c)) { + if (!is_stopped(c)) { // we should queue some sort of error - in c->error_string? ERROR("container %s is not stopped", c->name); goto out; @@ -1352,7 +1382,7 @@ static bool lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys, if (container_mem_lock(c)) return false; - if (is_stopped_locked(c)) + if (is_stopped(c)) goto err; ret = lxc_cgroup_set(c->name, subsys, value, c->config_path); @@ -1373,7 +1403,7 @@ static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, c if (container_mem_lock(c)) return -1; - if (is_stopped_locked(c)) + if (is_stopped(c)) goto out; ret = lxc_cgroup_get(c->name, subsys, retv, inlen, c->config_path); @@ -1837,7 +1867,7 @@ struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname, if (container_mem_lock(c)) return NULL; - if (!is_stopped_locked(c)) { + if (!is_stopped(c)) { ERROR("error: Original container (%s) is running", c->name); goto out; } From 73e608b21f73509c5f8c7a948cc6d4b0898edb2c Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Thu, 30 May 2013 11:22:16 -0500 Subject: [PATCH 150/325] waitpid at abort to make sure we can rmdir cgroups If we abort the container start, and don't wait for the init task to be reaped after we kill it, then we can't remove the container cgroup because it is not empty. Signed-off-by: Serge Hallyn --- src/lxc/start.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lxc/start.c b/src/lxc/start.c index d43d58073..5092b51cf 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -451,9 +451,12 @@ static void lxc_fini(const char *name, struct lxc_handler *handler) void lxc_abort(const char *name, struct lxc_handler *handler) { + int ret, status; + lxc_set_state(name, handler, ABORTING); if (handler->pid > 0) kill(handler->pid, SIGKILL); + while ((ret = waitpid(-1, &status, 0)) > 0) ; } #include From 3bc449ed24edc4b754cbe0af19fe878d29731f59 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 31 May 2013 07:55:14 -0500 Subject: [PATCH 151/325] lxccontainer: update locking comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the LOCKING comment. Take mem_lock in want_daemonize. convert lxcapi_destroy to not use privlock/slock by hand. Fix a coverity-found potential dereference of NULL c->lxc_conf. api_cgroup_get_item() and api_cgroup_set_item(): use disklock, not memlock, since the values are set through the cgroup fs on the running container. Signed-off-by: Serge Hallyn Acked-by: Stéphane Graber --- src/lxc/lxccontainer.c | 56 ++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 9bc1caf56..24b6008c2 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -145,11 +145,11 @@ void remove_partial(struct lxc_container *c, int fd) } /* LOCKING - * 1. c->privlock protects the struct lxc_container from multiple threads. - * 2. c->slock protects the on-disk container data - * 3. thread_mutex protects process data (ex: fd table) from multiple threads - * slock is an flock, which does not exclude threads. Therefore slock should - * always be wrapped inside privlock. + * 1. container_mem_lock(c) protects the struct lxc_container from multiple threads. + * 2. container_disk_lock(c) protects the on-disk container data - in particular the + * container configuration file. + * The container_disk_lock also takes the container_mem_lock. + * 3. thread_mutex protects process data (ex: fd table) from multiple threads. * 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 @@ -160,7 +160,7 @@ void remove_partial(struct lxc_container *c, int fd) * 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 + * If you're going to clone 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. @@ -406,7 +406,12 @@ static void lxcapi_want_daemonize(struct lxc_container *c) { if (!c) return; + if (!container_mem_lock(c)) { + ERROR("Error getting mem lock"); + return; + } c->daemonize = 1; + container_mem_unlock(c); } static bool lxcapi_wait(struct lxc_container *c, const char *state, int timeout) @@ -1218,12 +1223,8 @@ static bool lxcapi_destroy(struct lxc_container *c) if (!c || !lxcapi_is_defined(c)) return false; - if (lxclock(c->privlock, 0)) + if (container_disk_lock(c)) return false; - if (lxclock(c->slock, 0)) { - lxcunlock(c->privlock); - return false; - } if (!is_stopped(c)) { // we should queue some sort of error - in c->error_string? @@ -1231,7 +1232,7 @@ static bool lxcapi_destroy(struct lxc_container *c) goto out; } - if (c->lxc_conf->rootfs.path && c->lxc_conf->rootfs.mount) + if (c->lxc_conf && c->lxc_conf->rootfs.path && c->lxc_conf->rootfs.mount) r = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); if (r) { if (r->ops->destroy(r) < 0) { @@ -1250,8 +1251,7 @@ static bool lxcapi_destroy(struct lxc_container *c) ret = true; out: - lxcunlock(c->privlock); - lxcunlock(c->slock); + container_disk_unlock(c); return ret; } @@ -1374,42 +1374,38 @@ err: 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 (container_mem_lock(c)) + if (is_stopped(c)) return false; - if (is_stopped(c)) - goto err; + if (container_disk_lock(c)) + return false; - ret = lxc_cgroup_set(c->name, subsys, value, c->config_path); - if (!ret) - b = true; -err: - container_mem_unlock(c); - return b; + ret = lxc_cgroup_set(c->name, subsys, value, c->config_path) == 0; + + container_disk_unlock(c); + return ret == 0; } static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, char *retv, int inlen) { - int ret = -1; + int ret; if (!c || !c->lxc_conf) return -1; - if (container_mem_lock(c)) + if (is_stopped(c)) return -1; - if (is_stopped(c)) - goto out; + if (container_disk_lock(c)) + return -1; ret = lxc_cgroup_get(c->name, subsys, retv, inlen, c->config_path); -out: - container_mem_unlock(c); + container_disk_unlock(c); return ret; } From 3a647d582dc759e43c2087f0d906adf77c62ab6c Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 31 May 2013 16:02:33 +0200 Subject: [PATCH 152/325] configure/makefile: rename default_conf to distro_conf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit configure/makefile: rename default_conf to distro_conf, since it is a per-distro default. Then we'll be able to use the symbol LXC_DEFAULT_CONF in the code to refer to the installed file. Signed-off-by: Serge Hallyn Acked-by: Stéphane Graber --- config/Makefile.am | 4 ++-- configure.ac | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config/Makefile.am b/config/Makefile.am index 47f27505e..81d770921 100644 --- a/config/Makefile.am +++ b/config/Makefile.am @@ -1,11 +1,11 @@ configdir = $(sysconfdir)/lxc config_DATA = default.conf -defaultconf = @LXC_DEFAULT_CONF@ +distroconf = @LXC_DISTRO_CONF@ EXTRA_DIST = default.conf.ubuntu default.conf.libvirt default.conf.unknown default.conf: - cp $(defaultconf) $@ + cp $(distroconf) $@ clean-local: @$(RM) -f default.conf diff --git a/configure.ac b/configure.ac index d802406cf..65751ce3c 100644 --- a/configure.ac +++ b/configure.ac @@ -39,14 +39,14 @@ if test "z$with_distro" = "z"; then fi case $with_distro in ubuntu) - defaultconf=default.conf.ubuntu + distroconf=default.conf.ubuntu ;; redhat|fedora|oracle|oracleserver) - defaultconf=default.conf.libvirt + distroconf=default.conf.libvirt ;; *) echo -n "Linux distribution network config unknown, defaulting to lxc.network.type = empty" - defaultconf=default.conf.unknown + distroconf=default.conf.unknown ;; esac AC_MSG_RESULT([$with_distro]) @@ -221,7 +221,7 @@ AS_AC_EXPAND(SYSCONFDIR, "$sysconfdir") AS_AC_EXPAND(DATADIR, "$datadir") AS_AC_EXPAND(LOCALSTATEDIR, "$localstatedir") AS_AC_EXPAND(DOCDIR, "$docdir") -AS_AC_EXPAND(LXC_DEFAULT_CONF, "$defaultconf") +AS_AC_EXPAND(LXC_DISTRO_CONF, "$distroconf") AS_AC_EXPAND(LXC_GENERATE_DATE, "$(date)") AS_AC_EXPAND(LXCPATH, "$with_config_path") AS_AC_EXPAND(LXC_GLOBAL_CONF, "$with_global_conf") From 0a18b5458b6d0fcad9a82b96f99035254af50c7a Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 31 May 2013 16:09:14 +0200 Subject: [PATCH 153/325] Define LXC_DEFAULT_CONFIG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And use it in place of the various ways we were deducing /etc/lxc/default.conf. Signed-off-by: Serge Hallyn Acked-by: Stéphane Graber --- configure.ac | 1 + src/lxc/Makefile.am | 3 ++- src/lxc/lxc_create.c | 4 +--- src/lxc/lxccontainer.c | 3 --- src/tests/Makefile.am | 3 ++- src/tests/containertests.c | 2 +- src/tests/destroytest.c | 2 +- src/tests/saveconfig.c | 2 +- src/tests/startone.c | 2 +- 9 files changed, 10 insertions(+), 12 deletions(-) diff --git a/configure.ac b/configure.ac index 65751ce3c..456700185 100644 --- a/configure.ac +++ b/configure.ac @@ -218,6 +218,7 @@ AS_AC_EXPAND(BINDIR, "$bindir") AS_AC_EXPAND(LIBEXECDIR, "$libexecdir") AS_AC_EXPAND(INCLUDEDIR, "$includedir") AS_AC_EXPAND(SYSCONFDIR, "$sysconfdir") +AS_AC_EXPAND(LXC_DEFAULT_CONFIG, "$sysconfdir/lxc/default.conf") AS_AC_EXPAND(DATADIR, "$datadir") AS_AC_EXPAND(LOCALSTATEDIR, "$localstatedir") AS_AC_EXPAND(DOCDIR, "$docdir") diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 70fcd257c..18469a1c4 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -94,7 +94,8 @@ AM_CFLAGS=-I$(top_srcdir)/src \ -DLXC_GLOBAL_CONF=\"$(LXC_GLOBAL_CONF)\" \ -DLXCINITDIR=\"$(LXCINITDIR)\" \ -DLXCTEMPLATEDIR=\"$(LXCTEMPLATEDIR)\" \ - -DLOGPATH=\"$(LOGPATH)\" + -DLOGPATH=\"$(LOGPATH)\" \ + -DLXC_DEFAULT_CONFIG=\"$(LXC_DEFAULT_CONFIG)\" if ENABLE_APPARMOR AM_CFLAGS += -DHAVE_APPARMOR diff --git a/src/lxc/lxc_create.c b/src/lxc/lxc_create.c index 261360430..9176d1f68 100644 --- a/src/lxc/lxc_create.c +++ b/src/lxc/lxc_create.c @@ -135,8 +135,6 @@ bool validate_bdev_args(struct lxc_arguments *a) return true; } -/* grab this through autoconf from @config-path@ ? */ -#define DEFAULT_CONFIG "/etc/lxc/default.conf" int main(int argc, char *argv[]) { struct lxc_container *c; @@ -174,7 +172,7 @@ int main(int argc, char *argv[]) if (my_args.configfile) c->load_config(c, my_args.configfile); else - c->load_config(c, DEFAULT_CONFIG); + c->load_config(c, LXC_DEFAULT_CONFIG); if (strcmp(my_args.bdevtype, "zfs") == 0) { if (my_args.zfsroot) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 24b6008c2..61e1327cd 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -1158,9 +1158,6 @@ static int lxcapi_get_keys(struct lxc_container *c, const char *key, char *retv, return ret; } - -/* default config file - should probably come through autoconf */ -#define LXC_DEFAULT_CONFIG "/etc/lxc/default.conf" static bool lxcapi_save_config(struct lxc_container *c, const char *alt_file) { FILE *fout; diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index ba24d2c7a..be036e3fa 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -20,7 +20,8 @@ AM_CFLAGS=-I$(top_srcdir)/src \ -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -DLXCPATH=\"$(LXCPATH)\" \ -DLXC_GLOBAL_CONF=\"$(LXC_GLOBAL_CONF)\" \ - -DLXCINITDIR=\"$(LXCINITDIR)\" + -DLXCINITDIR=\"$(LXCINITDIR)\" \ + -DLXC_DEFAULT_CONFIG=\"$(LXC_DEFAULT_CONFIG)\" bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \ lxc-test-destroytest lxc-test-saveconfig lxc-test-createtest \ diff --git a/src/tests/containertests.c b/src/tests/containertests.c index 8868faacb..8d1658fab 100644 --- a/src/tests/containertests.c +++ b/src/tests/containertests.c @@ -71,7 +71,7 @@ static int create_busybox(void) return -1; } if (pid == 0) { - ret = execlp("lxc-create", "lxc-create", "-t", "busybox", "-f", "/etc/lxc/default.conf", "-n", MYNAME, NULL); + ret = execlp("lxc-create", "lxc-create", "-t", "busybox", "-f", LXC_DEFAULT_CONFIG, "-n", MYNAME, NULL); // Should not return perror("execl"); exit(1); diff --git a/src/tests/destroytest.c b/src/tests/destroytest.c index 0552b4c05..428d09ef1 100644 --- a/src/tests/destroytest.c +++ b/src/tests/destroytest.c @@ -38,7 +38,7 @@ static int create_ubuntu(void) return -1; } if (pid == 0) { - ret = execlp("lxc-create", "lxc-create", "-t", "ubuntu", "-f", "/etc/lxc/default.conf", "-n", MYNAME, NULL); + ret = execlp("lxc-create", "lxc-create", "-t", "ubuntu", "-f", LXC_DEFAULT_CONFIG, "-n", MYNAME, NULL); // Should not return perror("execl"); exit(1); diff --git a/src/tests/saveconfig.c b/src/tests/saveconfig.c index fa84e3214..649573f60 100644 --- a/src/tests/saveconfig.c +++ b/src/tests/saveconfig.c @@ -38,7 +38,7 @@ static int create_ubuntu(void) return -1; } if (pid == 0) { - ret = execlp("lxc-create", "lxc-create", "-t", "ubuntu", "-f", "/etc/lxc/default.conf", "-n", MYNAME, NULL); + ret = execlp("lxc-create", "lxc-create", "-t", "ubuntu", "-f", LXC_DEFAULT_CONFIG, "-n", MYNAME, NULL); // Should not return perror("execl"); exit(1); diff --git a/src/tests/startone.c b/src/tests/startone.c index 591399d9d..d781e758b 100644 --- a/src/tests/startone.c +++ b/src/tests/startone.c @@ -70,7 +70,7 @@ static int create_ubuntu(void) return -1; } if (pid == 0) { - ret = execlp("lxc-create", "lxc-create", "-t", "ubuntu", "-f", "/etc/lxc/default.conf", "-n", MYNAME, NULL); + ret = execlp("lxc-create", "lxc-create", "-t", "ubuntu", "-f", LXC_DEFAULT_CONFIG, "-n", MYNAME, NULL); // Should not return perror("execl"); exit(1); From 569bee5cc3d647032573db8f72734faa9307d577 Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Tue, 28 May 2013 10:25:14 +0200 Subject: [PATCH 154/325] lxc-alpine: download a static package manager if its missing If the package manager, apk-tools is missing, then: - download a static binary and public keys - verify the keys against embedded checksum - verify the signature of the static binary against the downloaded keys - use the verified static binary Signed-off-by: Natanael Copa Signed-off-by: Kaarle Ritvanen Signed-off-by: Serge Hallyn --- templates/lxc-alpine.in | 56 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/templates/lxc-alpine.in b/templates/lxc-alpine.in index 6180afd0c..cdf274370 100644 --- a/templates/lxc-alpine.in +++ b/templates/lxc-alpine.in @@ -1,10 +1,56 @@ #!/bin/sh +key_sha256sums="9c102bcc376af1498d549b77bdbfa815ae86faa1d2d82f040e616b18ef2df2d4 alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub +2adcf7ce224f476330b5360ca5edb92fd0bf91c92d83292ed028d7c4e26333ab alpine-devel@lists.alpinelinux.org-4d07755e.rsa.pub" + +get_static_apk () { + rootfs="$1" + echo "Using static apk from $repository/$apk_arch" + wget="wget -q -O - $repository/$apk_arch" + + # parse APKINDEX to find the current versions + static_pkgs=$($wget/APKINDEX.tar.gz | \ + tar -Oxz APKINDEX | \ + awk -F: -v pkglist="alpine-keys:apk-tools-static" ' + BEGIN { split(pkglist,pkg) } + $0 != "" { f[$1] = $2 } + $0 == "" { for (i in pkg) + if (pkg[i] == f["P"]) + print(f["P"] "-" f["V"] ".apk") }') + [ "$static_pkgs" ] || return 1 + + mkdir -p "$rootfs" || return 1 + for pkg in $static_pkgs; do + echo "Downloading $pkg" + $wget/$pkg | tar -xz -C "$rootfs" + done + + # clean up .apk meta files + rm -f "$rootfs"/.[A-Z]* + + # verify checksum of the key + keyname=$(echo $rootfs/sbin/apk.static.*.pub | sed 's/.*\.SIGN\.RSA\.//') + checksum=$(echo "$key_sha256sums" | grep -w "$keyname") + if [ -z "$checksum" ]; then + echo "ERROR: checksum is missing for $keyname" + return 1 + fi + (cd $rootfs/etc/apk/keys && echo "$checksum" | sha256sum -c -) || return 1 + + # verify the static apk binary signature + APK=$rootfs/sbin/apk.static + openssl dgst -verify $rootfs/etc/apk/keys/$keyname \ + -signature "$APK.SIGN.RSA.$keyname" "$APK" || return 1 +} + install_alpine() { rootfs="$1" shift mkdir -p "$rootfs"/etc/apk || return 1 - cp -r ${keys_dir:-/etc/apk/keys} "$rootfs"/etc/apk/ + : ${keys_dir:=/etc/apk/keys} + if ! [ -d "$rootfs"/etc/apk/keys ] && [ -d "$keys_dir" ]; then + cp -r "$keys_dir" "$rootfs"/etc/apk/keys + fi if [ -n "$repository" ]; then echo "$repository" > "$rootfs"/etc/apk/repositories else @@ -14,7 +60,7 @@ install_alpine() { if [ -n "$apk_arch" ]; then opt_arch="--arch $apk_arch" fi - ${APK:-apk} add -U --initdb --root $rootfs $opt_arch "$@" alpine-base + $APK add -U --initdb --root $rootfs $opt_arch "$@" alpine-base } configure_alpine() { @@ -167,6 +213,7 @@ optarg_check() { } default_path=@LXCPATH@ +arch=$(uname -m) while [ $# -gt 0 ]; do opt="$1" @@ -243,6 +290,11 @@ case "$arch" in *) die "unsupported architecture: $arch";; esac +: ${APK:=apk} +if ! which $APK >/dev/null; then + get_static_apk "$rootfs" || die "Failed to download a valid static apk" +fi + install_alpine "$rootfs" "$@" || die "Failed to install rootfs for $name" configure_alpine "$rootfs" "$name" || die "Failed to configure $name" copy_configuration "$path" "$rootfs" "$name" From 44ef0c0c7200ef4e8783387d886d3748da3d50fd Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Mon, 3 Jun 2013 10:47:21 -0500 Subject: [PATCH 155/325] lxcapi_create: don't close stdin/out/err Otherwise we can't see template progress. Signed-off-by: Serge Hallyn --- src/lxc/lxccontainer.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 61e1327cd..3764923f8 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -806,13 +806,6 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, struct bdev *bdev = NULL; int i; - close(0); - close(1); - close(2); - open("/dev/null", O_RDONLY); - open("/dev/null", O_RDWR); - open("/dev/null", O_RDWR); - if (unshare(CLONE_NEWNS) < 0) { ERROR("error unsharing mounts"); exit(1); From 63c3090c913142cd19f443b040cdede2c0522ce8 Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Mon, 3 Jun 2013 09:48:12 +0800 Subject: [PATCH 156/325] arguments: should return negative number when error happens We should return -ENOMEM instead of ENOMEM when realloc fails. Signed-off-by: Qiang Huang Signed-off-by: Serge Hallyn --- src/lxc/arguments.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/arguments.c b/src/lxc/arguments.c index 5f1c1af66..8a8d3067e 100644 --- a/src/lxc/arguments.c +++ b/src/lxc/arguments.c @@ -164,7 +164,7 @@ static int lxc_arguments_lxcpath_add(struct lxc_arguments *args, sizeof(args->lxcpath[0])); if (args->lxcpath == NULL) { lxc_error(args, "no memory"); - return ENOMEM; + return -ENOMEM; } args->lxcpath[args->lxcpath_cnt++] = lxcpath; return 0; From 3155e7f954d4b5d7da528d2a3cd8be254432e3c3 Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Mon, 3 Jun 2013 09:48:13 +0800 Subject: [PATCH 157/325] lxc-create: fix the typo in help info Fix typo in help info of lxc-create, and get rid of duplicate comments in bdev.h Signed-off-by: Qiang Huang Signed-off-by: Serge Hallyn --- src/lxc/bdev.h | 2 +- src/lxc/lxc_create.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lxc/bdev.h b/src/lxc/bdev.h index 7b60d953e..4f27ea982 100644 --- a/src/lxc/bdev.h +++ b/src/lxc/bdev.h @@ -1,7 +1,7 @@ #ifndef __LXC_BDEV_H #define __LXC_BDEV_H /* blockdev operations for: - * dir, raw, btrfs, overlayfs, aufs, lvm, loop, zfs, btrfs + * dir, raw, btrfs, overlayfs, aufs, lvm, loop, zfs * someday: qemu-nbd, qcow2, qed */ diff --git a/src/lxc/lxc_create.c b/src/lxc/lxc_create.c index 9176d1f68..f35a664b5 100644 --- a/src/lxc/lxc_create.c +++ b/src/lxc/lxc_create.c @@ -90,7 +90,7 @@ static struct lxc_arguments my_args = { .help = "\ --name=NAME [-w] [-r] [-t timeout] [-P lxcpath]\n\ \n\ -lxc-creae creates a container\n\ +lxc-create creates a container\n\ \n\ Options :\n\ -n, --name=NAME NAME for name of the container\n\ From 4c1f6b67d9b842d9e5c293eea2ff19301ecc5596 Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Mon, 3 Jun 2013 09:48:14 +0800 Subject: [PATCH 158/325] lxc-destroy: fix the wrong help info of lxc-destroy Changelog: jun 3: (Serge) trivial typo fix inline. Signed-off-by: Qiang Huang Signed-off-by: Serge Hallyn --- src/lxc/lxc_destroy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lxc/lxc_destroy.c b/src/lxc/lxc_destroy.c index 686b3038b..9a1b11fba 100644 --- a/src/lxc/lxc_destroy.c +++ b/src/lxc/lxc_destroy.c @@ -50,7 +50,7 @@ static struct lxc_arguments my_args = { .help = "\ --name=NAME [-f] [-P lxcpath]\n\ \n\ -lxc-stop stops a container with the identifier NAME\n\ +lxc-destroy destroys a container with the identifier NAME\n\ \n\ Options :\n\ -n, --name=NAME NAME for name of the container\n\ From f002c8a7655e42a325ef6bad9fb0844fad4e410b Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Mon, 3 Jun 2013 18:19:01 +0200 Subject: [PATCH 159/325] lxc_create: support 'lxc-create -t